VirtualBox

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

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

Main/src-server: comment.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 503.0 KB
Line 
1/* $Id: MachineImpl.cpp 54927 2015-03-24 15:53:55Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#ifdef VBOX_WITH_DTRACE_R3_MAIN
90# include "dtrace/VBoxAPI.h"
91#endif
92
93#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
94# define HOSTSUFF_EXE ".exe"
95#else /* !RT_OS_WINDOWS */
96# define HOSTSUFF_EXE ""
97#endif /* !RT_OS_WINDOWS */
98
99// defines / prototypes
100/////////////////////////////////////////////////////////////////////////////
101
102/////////////////////////////////////////////////////////////////////////////
103// Machine::Data structure
104/////////////////////////////////////////////////////////////////////////////
105
106Machine::Data::Data()
107{
108 mRegistered = FALSE;
109 pMachineConfigFile = NULL;
110 /* Contains hints on what has changed when the user is using the VM (config
111 * changes, running the VM, ...). This is used to decide if a config needs
112 * to be written to disk. */
113 flModifications = 0;
114 /* VM modification usually also trigger setting the current state to
115 * "Modified". Although this is not always the case. An e.g. is the VM
116 * initialization phase or when snapshot related data is changed. The
117 * actually behavior is controlled by the following flag. */
118 m_fAllowStateModification = false;
119 mAccessible = FALSE;
120 /* mUuid is initialized in Machine::init() */
121
122 mMachineState = MachineState_PoweredOff;
123 RTTimeNow(&mLastStateChange);
124
125 mMachineStateDeps = 0;
126 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
127 mMachineStateChangePending = 0;
128
129 mCurrentStateModified = TRUE;
130 mGuestPropertiesModified = FALSE;
131
132 mSession.mPID = NIL_RTPROCESS;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mSyntheticCpu = false;
196 mTripleFaultReset = false;
197 mHPETEnabled = false;
198
199 /* default boot order: floppy - DVD - HDD */
200 mBootOrder[0] = DeviceType_Floppy;
201 mBootOrder[1] = DeviceType_DVD;
202 mBootOrder[2] = DeviceType_HardDisk;
203 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
204 mBootOrder[i] = DeviceType_Null;
205
206 mClipboardMode = ClipboardMode_Disabled;
207 mDnDMode = DnDMode_Disabled;
208 mGuestPropertyNotificationPatterns = "";
209
210 mFirmwareType = FirmwareType_BIOS;
211 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
212 mPointingHIDType = PointingHIDType_PS2Mouse;
213 mChipsetType = ChipsetType_PIIX3;
214 mParavirtProvider = ParavirtProvider_Default;
215 mEmulatedUSBCardReaderEnabled = FALSE;
216
217 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
218 mCPUAttached[i] = false;
219
220 mIOCacheEnabled = true;
221 mIOCacheSize = 5; /* 5MB */
222
223 /* Maximum CPU execution cap by default. */
224 mCpuExecutionCap = 100;
225}
226
227Machine::HWData::~HWData()
228{
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HDData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::MediaData::MediaData()
236{
237}
238
239Machine::MediaData::~MediaData()
240{
241}
242
243/////////////////////////////////////////////////////////////////////////////
244// Machine class
245/////////////////////////////////////////////////////////////////////////////
246
247// constructor / destructor
248/////////////////////////////////////////////////////////////////////////////
249
250Machine::Machine() :
251#ifdef VBOX_WITH_RESOURCE_USAGE_API
252 mCollectorGuest(NULL),
253#endif
254 mPeer(NULL),
255 mParent(NULL),
256 mSerialPorts(),
257 mParallelPorts(),
258 uRegistryNeedsSaving(0)
259{}
260
261Machine::~Machine()
262{}
263
264HRESULT Machine::FinalConstruct()
265{
266 LogFlowThisFunc(("\n"));
267 return BaseFinalConstruct();
268}
269
270void Machine::FinalRelease()
271{
272 LogFlowThisFunc(("\n"));
273 uninit();
274 BaseFinalRelease();
275}
276
277/**
278 * Initializes a new machine instance; this init() variant creates a new, empty machine.
279 * This gets called from VirtualBox::CreateMachine().
280 *
281 * @param aParent Associated parent object
282 * @param strConfigFile Local file system path to the VM settings file (can
283 * be relative to the VirtualBox config directory).
284 * @param strName name for the machine
285 * @param llGroups list of groups for the machine
286 * @param aOsType OS Type of this machine or NULL.
287 * @param aId UUID for the new machine.
288 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 GuestOSType *aOsType,
297 const Guid &aId,
298 bool fForceOverwrite,
299 bool fDirectoryIncludesUUID)
300{
301 LogFlowThisFuncEnter();
302 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
303
304 /* Enclose the state transition NotReady->InInit->Ready */
305 AutoInitSpan autoInitSpan(this);
306 AssertReturn(autoInitSpan.isOk(), E_FAIL);
307
308 HRESULT rc = initImpl(aParent, strConfigFile);
309 if (FAILED(rc)) return rc;
310
311 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
312 if (FAILED(rc)) return rc;
313
314 if (SUCCEEDED(rc))
315 {
316 // create an empty machine config
317 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
318
319 rc = initDataAndChildObjects();
320 }
321
322 if (SUCCEEDED(rc))
323 {
324 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
325 mData->mAccessible = TRUE;
326
327 unconst(mData->mUuid) = aId;
328
329 mUserData->s.strName = strName;
330
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Apply BIOS defaults */
349 mBIOSSettings->i_applyDefaults(aOsType);
350
351 /* Apply network adapters defaults */
352 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
353 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
354
355 /* Apply serial port defaults */
356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
357 mSerialPorts[slot]->i_applyDefaults(aOsType);
358
359 /* Let the OS type select 64-bit ness. */
360 mHWData->mLongMode = aOsType->i_is64Bit()
361 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 }
363
364 /* At this point the changing of the current state modification
365 * flag is allowed. */
366 i_allowStateModification();
367
368 /* commit all changes made during the initialization */
369 i_commit();
370 }
371
372 /* Confirm a successful initialization when it's the case */
373 if (SUCCEEDED(rc))
374 {
375 if (mData->mAccessible)
376 autoInitSpan.setSucceeded();
377 else
378 autoInitSpan.setLimited();
379 }
380
381 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
382 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
383 mData->mRegistered,
384 mData->mAccessible,
385 rc));
386
387 LogFlowThisFuncLeave();
388
389 return rc;
390}
391
392/**
393 * Initializes a new instance with data from machine XML (formerly Init_Registered).
394 * Gets called in two modes:
395 *
396 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
397 * UUID is specified and we mark the machine as "registered";
398 *
399 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
400 * and the machine remains unregistered until RegisterMachine() is called.
401 *
402 * @param aParent Associated parent object
403 * @param aConfigFile Local file system path to the VM settings file (can
404 * be relative to the VirtualBox config directory).
405 * @param aId UUID of the machine or NULL (see above).
406 *
407 * @return Success indicator. if not S_OK, the machine object is invalid
408 */
409HRESULT Machine::initFromSettings(VirtualBox *aParent,
410 const Utf8Str &strConfigFile,
411 const Guid *aId)
412{
413 LogFlowThisFuncEnter();
414 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
415
416 /* Enclose the state transition NotReady->InInit->Ready */
417 AutoInitSpan autoInitSpan(this);
418 AssertReturn(autoInitSpan.isOk(), E_FAIL);
419
420 HRESULT rc = initImpl(aParent, strConfigFile);
421 if (FAILED(rc)) return rc;
422
423 if (aId)
424 {
425 // loading a registered VM:
426 unconst(mData->mUuid) = *aId;
427 mData->mRegistered = TRUE;
428 // now load the settings from XML:
429 rc = i_registeredInit();
430 // this calls initDataAndChildObjects() and loadSettings()
431 }
432 else
433 {
434 // opening an unregistered VM (VirtualBox::OpenMachine()):
435 rc = initDataAndChildObjects();
436
437 if (SUCCEEDED(rc))
438 {
439 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
440 mData->mAccessible = TRUE;
441
442 try
443 {
444 // load and parse machine XML; this will throw on XML or logic errors
445 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
446
447 // reject VM UUID duplicates, they can happen if someone
448 // tries to register an already known VM config again
449 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
450 true /* fPermitInaccessible */,
451 false /* aDoSetError */,
452 NULL) != VBOX_E_OBJECT_NOT_FOUND)
453 {
454 throw setError(E_FAIL,
455 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
456 mData->m_strConfigFile.c_str());
457 }
458
459 // use UUID from machine config
460 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
461
462 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
463 NULL /* puuidRegistry */);
464 if (FAILED(rc)) throw rc;
465
466 /* At this point the changing of the current state modification
467 * flag is allowed. */
468 i_allowStateModification();
469
470 i_commit();
471 }
472 catch (HRESULT err)
473 {
474 /* we assume that error info is set by the thrower */
475 rc = err;
476 }
477 catch (...)
478 {
479 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
480 }
481 }
482 }
483
484 /* Confirm a successful initialization when it's the case */
485 if (SUCCEEDED(rc))
486 {
487 if (mData->mAccessible)
488 autoInitSpan.setSucceeded();
489 else
490 {
491 autoInitSpan.setLimited();
492
493 // uninit media from this machine's media registry, or else
494 // reloading the settings will fail
495 mParent->i_unregisterMachineMedia(i_getId());
496 }
497 }
498
499 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
500 "rc=%08X\n",
501 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
502 mData->mRegistered, mData->mAccessible, rc));
503
504 LogFlowThisFuncLeave();
505
506 return rc;
507}
508
509/**
510 * Initializes a new instance from a machine config that is already in memory
511 * (import OVF case). Since we are importing, the UUID in the machine
512 * config is ignored and we always generate a fresh one.
513 *
514 * @param strName Name for the new machine; this overrides what is specified in config and is used
515 * for the settings file as well.
516 * @param config Machine configuration loaded and parsed from XML.
517 *
518 * @return Success indicator. if not S_OK, the machine object is invalid
519 */
520HRESULT Machine::init(VirtualBox *aParent,
521 const Utf8Str &strName,
522 const settings::MachineConfigFile &config)
523{
524 LogFlowThisFuncEnter();
525
526 /* Enclose the state transition NotReady->InInit->Ready */
527 AutoInitSpan autoInitSpan(this);
528 AssertReturn(autoInitSpan.isOk(), E_FAIL);
529
530 Utf8Str strConfigFile;
531 aParent->i_getDefaultMachineFolder(strConfigFile);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(".vbox");
537
538 HRESULT rc = initImpl(aParent, strConfigFile);
539 if (FAILED(rc)) return rc;
540
541 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
542 if (FAILED(rc)) return rc;
543
544 rc = initDataAndChildObjects();
545
546 if (SUCCEEDED(rc))
547 {
548 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
549 mData->mAccessible = TRUE;
550
551 // create empty machine config for instance data
552 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
553
554 // generate fresh UUID, ignore machine config
555 unconst(mData->mUuid).create();
556
557 rc = i_loadMachineDataFromSettings(config,
558 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
559
560 // override VM name as well, it may be different
561 mUserData->s.strName = strName;
562
563 if (SUCCEEDED(rc))
564 {
565 /* At this point the changing of the current state modification
566 * flag is allowed. */
567 i_allowStateModification();
568
569 /* commit all changes made during the initialization */
570 i_commit();
571 }
572 }
573
574 /* Confirm a successful initialization when it's the case */
575 if (SUCCEEDED(rc))
576 {
577 if (mData->mAccessible)
578 autoInitSpan.setSucceeded();
579 else
580 {
581 /* Ignore all errors from unregistering, they would destroy
582- * the more interesting error information we already have,
583- * pinpointing the issue with the VM config. */
584 ErrorInfoKeeper eik;
585
586 autoInitSpan.setLimited();
587
588 // uninit media from this machine's media registry, or else
589 // reloading the settings will fail
590 mParent->i_unregisterMachineMedia(i_getId());
591 }
592 }
593
594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
595 "rc=%08X\n",
596 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
597 mData->mRegistered, mData->mAccessible, rc));
598
599 LogFlowThisFuncLeave();
600
601 return rc;
602}
603
604/**
605 * Shared code between the various init() implementations.
606 * @param aParent
607 * @return
608 */
609HRESULT Machine::initImpl(VirtualBox *aParent,
610 const Utf8Str &strConfigFile)
611{
612 LogFlowThisFuncEnter();
613
614 AssertReturn(aParent, E_INVALIDARG);
615 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
616
617 HRESULT rc = S_OK;
618
619 /* share the parent weakly */
620 unconst(mParent) = aParent;
621
622 /* allocate the essential machine data structure (the rest will be
623 * allocated later by initDataAndChildObjects() */
624 mData.allocate();
625
626 /* memorize the config file name (as provided) */
627 mData->m_strConfigFile = strConfigFile;
628
629 /* get the full file name */
630 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
631 if (RT_FAILURE(vrc1))
632 return setError(VBOX_E_FILE_ERROR,
633 tr("Invalid machine settings file name '%s' (%Rrc)"),
634 strConfigFile.c_str(),
635 vrc1);
636
637 LogFlowThisFuncLeave();
638
639 return rc;
640}
641
642/**
643 * Tries to create a machine settings file in the path stored in the machine
644 * instance data. Used when a new machine is created to fail gracefully if
645 * the settings file could not be written (e.g. because machine dir is read-only).
646 * @return
647 */
648HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
649{
650 HRESULT rc = S_OK;
651
652 // when we create a new machine, we must be able to create the settings file
653 RTFILE f = NIL_RTFILE;
654 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
655 if ( RT_SUCCESS(vrc)
656 || vrc == VERR_SHARING_VIOLATION
657 )
658 {
659 if (RT_SUCCESS(vrc))
660 RTFileClose(f);
661 if (!fForceOverwrite)
662 rc = setError(VBOX_E_FILE_ERROR,
663 tr("Machine settings file '%s' already exists"),
664 mData->m_strConfigFileFull.c_str());
665 else
666 {
667 /* try to delete the config file, as otherwise the creation
668 * of a new settings file will fail. */
669 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
670 if (RT_FAILURE(vrc2))
671 rc = setError(VBOX_E_FILE_ERROR,
672 tr("Could not delete the existing settings file '%s' (%Rrc)"),
673 mData->m_strConfigFileFull.c_str(), vrc2);
674 }
675 }
676 else if ( vrc != VERR_FILE_NOT_FOUND
677 && vrc != VERR_PATH_NOT_FOUND
678 )
679 rc = setError(VBOX_E_FILE_ERROR,
680 tr("Invalid machine settings file name '%s' (%Rrc)"),
681 mData->m_strConfigFileFull.c_str(),
682 vrc);
683 return rc;
684}
685
686/**
687 * Initializes the registered machine by loading the settings file.
688 * This method is separated from #init() in order to make it possible to
689 * retry the operation after VirtualBox startup instead of refusing to
690 * startup the whole VirtualBox server in case if the settings file of some
691 * registered VM is invalid or inaccessible.
692 *
693 * @note Must be always called from this object's write lock
694 * (unless called from #init() that doesn't need any locking).
695 * @note Locks the mUSBController method for writing.
696 * @note Subclasses must not call this method.
697 */
698HRESULT Machine::i_registeredInit()
699{
700 AssertReturn(!i_isSessionMachine(), E_FAIL);
701 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
702 AssertReturn(mData->mUuid.isValid(), E_FAIL);
703 AssertReturn(!mData->mAccessible, E_FAIL);
704
705 HRESULT rc = initDataAndChildObjects();
706
707 if (SUCCEEDED(rc))
708 {
709 /* Temporarily reset the registered flag in order to let setters
710 * potentially called from loadSettings() succeed (isMutable() used in
711 * all setters will return FALSE for a Machine instance if mRegistered
712 * is TRUE). */
713 mData->mRegistered = FALSE;
714
715 try
716 {
717 // load and parse machine XML; this will throw on XML or logic errors
718 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
719
720 if (mData->mUuid != mData->pMachineConfigFile->uuid)
721 throw setError(E_FAIL,
722 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
723 mData->pMachineConfigFile->uuid.raw(),
724 mData->m_strConfigFileFull.c_str(),
725 mData->mUuid.toString().c_str(),
726 mParent->i_settingsFilePath().c_str());
727
728 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
729 NULL /* const Guid *puuidRegistry */);
730 if (FAILED(rc)) throw rc;
731 }
732 catch (HRESULT err)
733 {
734 /* we assume that error info is set by the thrower */
735 rc = err;
736 }
737 catch (...)
738 {
739 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
740 }
741
742 /* Restore the registered flag (even on failure) */
743 mData->mRegistered = TRUE;
744 }
745
746 if (SUCCEEDED(rc))
747 {
748 /* Set mAccessible to TRUE only if we successfully locked and loaded
749 * the settings file */
750 mData->mAccessible = TRUE;
751
752 /* commit all changes made during loading the settings file */
753 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
754 /// @todo r=klaus for some reason the settings loading logic backs up
755 // the settings, and therefore a commit is needed. Should probably be changed.
756 }
757 else
758 {
759 /* If the machine is registered, then, instead of returning a
760 * failure, we mark it as inaccessible and set the result to
761 * success to give it a try later */
762
763 /* fetch the current error info */
764 mData->mAccessError = com::ErrorInfo();
765 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
766 mData->mUuid.raw(),
767 mData->mAccessError.getText().raw()));
768
769 /* rollback all changes */
770 i_rollback(false /* aNotify */);
771
772 // uninit media from this machine's media registry, or else
773 // reloading the settings will fail
774 mParent->i_unregisterMachineMedia(i_getId());
775
776 /* uninitialize the common part to make sure all data is reset to
777 * default (null) values */
778 uninitDataAndChildObjects();
779
780 rc = S_OK;
781 }
782
783 return rc;
784}
785
786/**
787 * Uninitializes the instance.
788 * Called either from FinalRelease() or by the parent when it gets destroyed.
789 *
790 * @note The caller of this method must make sure that this object
791 * a) doesn't have active callers on the current thread and b) is not locked
792 * by the current thread; otherwise uninit() will hang either a) due to
793 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
794 * a dead-lock caused by this thread waiting for all callers on the other
795 * threads are done but preventing them from doing so by holding a lock.
796 */
797void Machine::uninit()
798{
799 LogFlowThisFuncEnter();
800
801 Assert(!isWriteLockOnCurrentThread());
802
803 Assert(!uRegistryNeedsSaving);
804 if (uRegistryNeedsSaving)
805 {
806 AutoCaller autoCaller(this);
807 if (SUCCEEDED(autoCaller.rc()))
808 {
809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
810 i_saveSettings(NULL, Machine::SaveS_Force);
811 }
812 }
813
814 /* Enclose the state transition Ready->InUninit->NotReady */
815 AutoUninitSpan autoUninitSpan(this);
816 if (autoUninitSpan.uninitDone())
817 return;
818
819 Assert(!i_isSnapshotMachine());
820 Assert(!i_isSessionMachine());
821 Assert(!!mData);
822
823 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
824 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
825
826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
827
828 if (!mData->mSession.mMachine.isNull())
829 {
830 /* Theoretically, this can only happen if the VirtualBox server has been
831 * terminated while there were clients running that owned open direct
832 * sessions. Since in this case we are definitely called by
833 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
834 * won't happen on the client watcher thread (because it does
835 * VirtualBox::addCaller() for the duration of the
836 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
837 * cannot happen until the VirtualBox caller is released). This is
838 * important, because SessionMachine::uninit() cannot correctly operate
839 * after we return from this method (it expects the Machine instance is
840 * still valid). We'll call it ourselves below.
841 */
842 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
843 (SessionMachine*)mData->mSession.mMachine));
844
845 if (Global::IsOnlineOrTransient(mData->mMachineState))
846 {
847 LogWarningThisFunc(("Setting state to Aborted!\n"));
848 /* set machine state using SessionMachine reimplementation */
849 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
850 }
851
852 /*
853 * Uninitialize SessionMachine using public uninit() to indicate
854 * an unexpected uninitialization.
855 */
856 mData->mSession.mMachine->uninit();
857 /* SessionMachine::uninit() must set mSession.mMachine to null */
858 Assert(mData->mSession.mMachine.isNull());
859 }
860
861 // uninit media from this machine's media registry, if they're still there
862 Guid uuidMachine(i_getId());
863
864 /* the lock is no more necessary (SessionMachine is uninitialized) */
865 alock.release();
866
867 /* XXX This will fail with
868 * "cannot be closed because it is still attached to 1 virtual machines"
869 * because at this point we did not call uninitDataAndChildObjects() yet
870 * and therefore also removeBackReference() for all these mediums was not called! */
871
872 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
873 mParent->i_unregisterMachineMedia(uuidMachine);
874
875 // has machine been modified?
876 if (mData->flModifications)
877 {
878 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
879 i_rollback(false /* aNotify */);
880 }
881
882 if (mData->mAccessible)
883 uninitDataAndChildObjects();
884
885 /* free the essential data structure last */
886 mData.free();
887
888 LogFlowThisFuncLeave();
889}
890
891// Wrapped IMachine properties
892/////////////////////////////////////////////////////////////////////////////
893HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
894{
895 /* mParent is constant during life time, no need to lock */
896 ComObjPtr<VirtualBox> pVirtualBox(mParent);
897 aParent = pVirtualBox;
898
899 return S_OK;
900}
901
902
903HRESULT Machine::getAccessible(BOOL *aAccessible)
904{
905 /* In some cases (medium registry related), it is necessary to be able to
906 * go through the list of all machines. Happens when an inaccessible VM
907 * has a sensible medium registry. */
908 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->i_dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = i_registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->i_onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
955{
956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
957
958 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
959 {
960 /* return shortly */
961 aAccessError = NULL;
962 return S_OK;
963 }
964
965 HRESULT rc = S_OK;
966
967 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
968 rc = errorInfo.createObject();
969 if (SUCCEEDED(rc))
970 {
971 errorInfo->init(mData->mAccessError.getResultCode(),
972 mData->mAccessError.getInterfaceID().ref(),
973 Utf8Str(mData->mAccessError.getComponent()).c_str(),
974 Utf8Str(mData->mAccessError.getText()));
975 aAccessError = errorInfo;
976 }
977
978 return rc;
979}
980
981HRESULT Machine::getName(com::Utf8Str &aName)
982{
983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
984
985 aName = mUserData->s.strName;
986
987 return S_OK;
988}
989
990HRESULT Machine::setName(const com::Utf8Str &aName)
991{
992 // prohibit setting a UUID only as the machine name, or else it can
993 // never be found by findMachine()
994 Guid test(aName);
995
996 if (test.isValid())
997 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
998
999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 HRESULT rc = i_checkStateDependency(MutableStateDep);
1002 if (FAILED(rc)) return rc;
1003
1004 i_setModified(IsModified_MachineData);
1005 mUserData.backup();
1006 mUserData->s.strName = aName;
1007
1008 return S_OK;
1009}
1010
1011HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1012{
1013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 aDescription = mUserData->s.strDescription;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1021{
1022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 // this can be done in principle in any state as it doesn't affect the VM
1025 // significantly, but play safe by not messing around while complex
1026 // activities are going on
1027 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1028 if (FAILED(rc)) return rc;
1029
1030 i_setModified(IsModified_MachineData);
1031 mUserData.backup();
1032 mUserData->s.strDescription = aDescription;
1033
1034 return S_OK;
1035}
1036
1037HRESULT Machine::getId(com::Guid &aId)
1038{
1039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 aId = mData->mUuid;
1042
1043 return S_OK;
1044}
1045
1046HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1047{
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049 aGroups.resize(mUserData->s.llGroups.size());
1050 size_t i = 0;
1051 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1052 it != mUserData->s.llGroups.end(); ++it, ++i)
1053 aGroups[i] = (*it);
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1059{
1060 StringsList llGroups;
1061 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1062 if (FAILED(rc))
1063 return rc;
1064
1065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1066
1067 rc = i_checkStateDependency(MutableStateDep);
1068 if (FAILED(rc)) return rc;
1069
1070 i_setModified(IsModified_MachineData);
1071 mUserData.backup();
1072 mUserData->s.llGroups = llGroups;
1073
1074 return S_OK;
1075}
1076
1077HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1078{
1079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1080
1081 aOSTypeId = mUserData->s.strOsType;
1082
1083 return S_OK;
1084}
1085
1086HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1087{
1088 /* look up the object by Id to check it is valid */
1089 ComPtr<IGuestOSType> guestOSType;
1090 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1091 if (FAILED(rc)) return rc;
1092
1093 /* when setting, always use the "etalon" value for consistency -- lookup
1094 * by ID is case-insensitive and the input value may have different case */
1095 Bstr osTypeId;
1096 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1097 if (FAILED(rc)) return rc;
1098
1099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1100
1101 rc = i_checkStateDependency(MutableStateDep);
1102 if (FAILED(rc)) return rc;
1103
1104 i_setModified(IsModified_MachineData);
1105 mUserData.backup();
1106 mUserData->s.strOsType = osTypeId;
1107
1108 return S_OK;
1109}
1110
1111HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1112{
1113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 *aFirmwareType = mHWData->mFirmwareType;
1116
1117 return S_OK;
1118}
1119
1120HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1121{
1122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1123
1124 HRESULT rc = i_checkStateDependency(MutableStateDep);
1125 if (FAILED(rc)) return rc;
1126
1127 i_setModified(IsModified_MachineData);
1128 mHWData.backup();
1129 mHWData->mFirmwareType = aFirmwareType;
1130
1131 return S_OK;
1132}
1133
1134HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1135{
1136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
1138 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1139
1140 return S_OK;
1141}
1142
1143HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1144{
1145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 HRESULT rc = i_checkStateDependency(MutableStateDep);
1148 if (FAILED(rc)) return rc;
1149
1150 i_setModified(IsModified_MachineData);
1151 mHWData.backup();
1152 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1153
1154 return S_OK;
1155}
1156
1157HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1158{
1159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1160
1161 *aPointingHIDType = mHWData->mPointingHIDType;
1162
1163 return S_OK;
1164}
1165
1166HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1167{
1168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 HRESULT rc = i_checkStateDependency(MutableStateDep);
1171 if (FAILED(rc)) return rc;
1172
1173 i_setModified(IsModified_MachineData);
1174 mHWData.backup();
1175 mHWData->mPointingHIDType = aPointingHIDType;
1176
1177 return S_OK;
1178}
1179
1180HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1181{
1182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 *aChipsetType = mHWData->mChipsetType;
1185
1186 return S_OK;
1187}
1188
1189HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1190{
1191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT rc = i_checkStateDependency(MutableStateDep);
1194 if (FAILED(rc)) return rc;
1195
1196 if (aChipsetType != mHWData->mChipsetType)
1197 {
1198 i_setModified(IsModified_MachineData);
1199 mHWData.backup();
1200 mHWData->mChipsetType = aChipsetType;
1201
1202 // Resize network adapter array, to be finalized on commit/rollback.
1203 // We must not throw away entries yet, otherwise settings are lost
1204 // without a way to roll back.
1205 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1206 size_t oldCount = mNetworkAdapters.size();
1207 if (newCount > oldCount)
1208 {
1209 mNetworkAdapters.resize(newCount);
1210 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1211 {
1212 unconst(mNetworkAdapters[slot]).createObject();
1213 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1214 }
1215 }
1216 }
1217
1218 return S_OK;
1219}
1220
1221HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1222{
1223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1224
1225 *aParavirtProvider = mHWData->mParavirtProvider;
1226
1227 return S_OK;
1228}
1229
1230HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1231{
1232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 HRESULT rc = i_checkStateDependency(MutableStateDep);
1235 if (FAILED(rc)) return rc;
1236
1237 if (aParavirtProvider != mHWData->mParavirtProvider)
1238 {
1239 i_setModified(IsModified_MachineData);
1240 mHWData.backup();
1241 mHWData->mParavirtProvider = aParavirtProvider;
1242 }
1243
1244 return S_OK;
1245}
1246
1247HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1248{
1249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1250
1251 *aParavirtProvider = mHWData->mParavirtProvider;
1252 switch (mHWData->mParavirtProvider)
1253 {
1254 case ParavirtProvider_None:
1255 case ParavirtProvider_HyperV:
1256 case ParavirtProvider_KVM:
1257 case ParavirtProvider_Minimal:
1258 break;
1259
1260 /* Resolve dynamic provider types to the effective types. */
1261 default:
1262 {
1263 ComPtr<IGuestOSType> ptrGuestOSType;
1264 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1265 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1266
1267 Bstr guestTypeFamilyId;
1268 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1269 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1270 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1271
1272 switch (mHWData->mParavirtProvider)
1273 {
1274 case ParavirtProvider_Legacy:
1275 {
1276 if (fOsXGuest)
1277 *aParavirtProvider = ParavirtProvider_Minimal;
1278 else
1279 *aParavirtProvider = ParavirtProvider_None;
1280 break;
1281 }
1282
1283 case ParavirtProvider_Default:
1284 {
1285 if (fOsXGuest)
1286 *aParavirtProvider = ParavirtProvider_Minimal;
1287 else if ( mUserData->s.strOsType == "Windows10"
1288 || mUserData->s.strOsType == "Windows10_64"
1289 || mUserData->s.strOsType == "Windows81"
1290 || mUserData->s.strOsType == "Windows81_64"
1291 || mUserData->s.strOsType == "Windows8"
1292 || mUserData->s.strOsType == "Windows8_64"
1293 || mUserData->s.strOsType == "Windows7"
1294 || mUserData->s.strOsType == "Windows7_64"
1295 || mUserData->s.strOsType == "WindowsVista"
1296 || mUserData->s.strOsType == "WindowsVista_64"
1297 || mUserData->s.strOsType == "Windows2012"
1298 || mUserData->s.strOsType == "Windows2012_64"
1299 || mUserData->s.strOsType == "Windows2008"
1300 || mUserData->s.strOsType == "Windows2008_64")
1301 {
1302 *aParavirtProvider = ParavirtProvider_HyperV;
1303 }
1304 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1305 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1306 || mUserData->s.strOsType == "Linux"
1307 || mUserData->s.strOsType == "Linux_64"
1308 || mUserData->s.strOsType == "ArchLinux"
1309 || mUserData->s.strOsType == "ArchLinux_64"
1310 || mUserData->s.strOsType == "Debian"
1311 || mUserData->s.strOsType == "Debian_64"
1312 || mUserData->s.strOsType == "Fedora"
1313 || mUserData->s.strOsType == "Fedora_64"
1314 || mUserData->s.strOsType == "Gentoo"
1315 || mUserData->s.strOsType == "Gentoo_64"
1316 || mUserData->s.strOsType == "Mandriva"
1317 || mUserData->s.strOsType == "Mandriva_64"
1318 || mUserData->s.strOsType == "OpenSUSE"
1319 || mUserData->s.strOsType == "OpenSUSE_64"
1320 || mUserData->s.strOsType == "Oracle"
1321 || mUserData->s.strOsType == "Oracle_64"
1322 || mUserData->s.strOsType == "RedHat"
1323 || mUserData->s.strOsType == "RedHat_64"
1324 || mUserData->s.strOsType == "Turbolinux"
1325 || mUserData->s.strOsType == "Turbolinux_64"
1326 || mUserData->s.strOsType == "Ubuntu"
1327 || mUserData->s.strOsType == "Ubuntu_64"
1328 || mUserData->s.strOsType == "Xandros"
1329 || mUserData->s.strOsType == "Xandros_64")
1330 {
1331 *aParavirtProvider = ParavirtProvider_KVM;
1332 }
1333 else
1334 *aParavirtProvider = ParavirtProvider_None;
1335 break;
1336 }
1337 }
1338 break;
1339 }
1340 }
1341
1342 Assert( *aParavirtProvider == ParavirtProvider_None
1343 || *aParavirtProvider == ParavirtProvider_Minimal
1344 || *aParavirtProvider == ParavirtProvider_HyperV
1345 || *aParavirtProvider == ParavirtProvider_KVM);
1346 return S_OK;
1347}
1348
1349HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1350{
1351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1352
1353 aHardwareVersion = mHWData->mHWVersion;
1354
1355 return S_OK;
1356}
1357
1358HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1359{
1360 /* check known version */
1361 Utf8Str hwVersion = aHardwareVersion;
1362 if ( hwVersion.compare("1") != 0
1363 && hwVersion.compare("2") != 0)
1364 return setError(E_INVALIDARG,
1365 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1366
1367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1368
1369 HRESULT rc = i_checkStateDependency(MutableStateDep);
1370 if (FAILED(rc)) return rc;
1371
1372 i_setModified(IsModified_MachineData);
1373 mHWData.backup();
1374 mHWData->mHWVersion = aHardwareVersion;
1375
1376 return S_OK;
1377}
1378
1379HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1380{
1381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 if (!mHWData->mHardwareUUID.isZero())
1384 aHardwareUUID = mHWData->mHardwareUUID;
1385 else
1386 aHardwareUUID = mData->mUuid;
1387
1388 return S_OK;
1389}
1390
1391HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1392{
1393 if (!aHardwareUUID.isValid())
1394 return E_INVALIDARG;
1395
1396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 HRESULT rc = i_checkStateDependency(MutableStateDep);
1399 if (FAILED(rc)) return rc;
1400
1401 i_setModified(IsModified_MachineData);
1402 mHWData.backup();
1403 if (aHardwareUUID == mData->mUuid)
1404 mHWData->mHardwareUUID.clear();
1405 else
1406 mHWData->mHardwareUUID = aHardwareUUID;
1407
1408 return S_OK;
1409}
1410
1411HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1412{
1413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1414
1415 *aMemorySize = mHWData->mMemorySize;
1416
1417 return S_OK;
1418}
1419
1420HRESULT Machine::setMemorySize(ULONG aMemorySize)
1421{
1422 /* check RAM limits */
1423 if ( aMemorySize < MM_RAM_MIN_IN_MB
1424 || aMemorySize > MM_RAM_MAX_IN_MB
1425 )
1426 return setError(E_INVALIDARG,
1427 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1428 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1429
1430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1431
1432 HRESULT rc = i_checkStateDependency(MutableStateDep);
1433 if (FAILED(rc)) return rc;
1434
1435 i_setModified(IsModified_MachineData);
1436 mHWData.backup();
1437 mHWData->mMemorySize = aMemorySize;
1438
1439 return S_OK;
1440}
1441
1442HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1443{
1444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1445
1446 *aCPUCount = mHWData->mCPUCount;
1447
1448 return S_OK;
1449}
1450
1451HRESULT Machine::setCPUCount(ULONG aCPUCount)
1452{
1453 /* check CPU limits */
1454 if ( aCPUCount < SchemaDefs::MinCPUCount
1455 || aCPUCount > SchemaDefs::MaxCPUCount
1456 )
1457 return setError(E_INVALIDARG,
1458 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1459 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1460
1461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1462
1463 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1464 if (mHWData->mCPUHotPlugEnabled)
1465 {
1466 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1467 {
1468 if (mHWData->mCPUAttached[idx])
1469 return setError(E_INVALIDARG,
1470 tr("There is still a CPU attached to socket %lu."
1471 "Detach the CPU before removing the socket"),
1472 aCPUCount, idx+1);
1473 }
1474 }
1475
1476 HRESULT rc = i_checkStateDependency(MutableStateDep);
1477 if (FAILED(rc)) return rc;
1478
1479 i_setModified(IsModified_MachineData);
1480 mHWData.backup();
1481 mHWData->mCPUCount = aCPUCount;
1482
1483 return S_OK;
1484}
1485
1486HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1487{
1488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1489
1490 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1491
1492 return S_OK;
1493}
1494
1495HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1496{
1497 HRESULT rc = S_OK;
1498
1499 /* check throttle limits */
1500 if ( aCPUExecutionCap < 1
1501 || aCPUExecutionCap > 100
1502 )
1503 return setError(E_INVALIDARG,
1504 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1505 aCPUExecutionCap, 1, 100);
1506
1507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1508
1509 alock.release();
1510 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1511 alock.acquire();
1512 if (FAILED(rc)) return rc;
1513
1514 i_setModified(IsModified_MachineData);
1515 mHWData.backup();
1516 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1517
1518 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1519 if (Global::IsOnline(mData->mMachineState))
1520 i_saveSettings(NULL);
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1526{
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1535{
1536 HRESULT rc = S_OK;
1537
1538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1539
1540 rc = i_checkStateDependency(MutableStateDep);
1541 if (FAILED(rc)) return rc;
1542
1543 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1544 {
1545 if (aCPUHotPlugEnabled)
1546 {
1547 i_setModified(IsModified_MachineData);
1548 mHWData.backup();
1549
1550 /* Add the amount of CPUs currently attached */
1551 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1552 mHWData->mCPUAttached[i] = true;
1553 }
1554 else
1555 {
1556 /*
1557 * We can disable hotplug only if the amount of maximum CPUs is equal
1558 * to the amount of attached CPUs
1559 */
1560 unsigned cCpusAttached = 0;
1561 unsigned iHighestId = 0;
1562
1563 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1564 {
1565 if (mHWData->mCPUAttached[i])
1566 {
1567 cCpusAttached++;
1568 iHighestId = i;
1569 }
1570 }
1571
1572 if ( (cCpusAttached != mHWData->mCPUCount)
1573 || (iHighestId >= mHWData->mCPUCount))
1574 return setError(E_INVALIDARG,
1575 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1576
1577 i_setModified(IsModified_MachineData);
1578 mHWData.backup();
1579 }
1580 }
1581
1582 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1583
1584 return rc;
1585}
1586
1587HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1588{
1589#ifdef VBOX_WITH_USB_CARDREADER
1590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1591
1592 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1593
1594 return S_OK;
1595#else
1596 NOREF(aEmulatedUSBCardReaderEnabled);
1597 return E_NOTIMPL;
1598#endif
1599}
1600
1601HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1602{
1603#ifdef VBOX_WITH_USB_CARDREADER
1604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1605
1606 HRESULT rc = i_checkStateDependency(MutableStateDep);
1607 if (FAILED(rc)) return rc;
1608
1609 i_setModified(IsModified_MachineData);
1610 mHWData.backup();
1611 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1612
1613 return S_OK;
1614#else
1615 NOREF(aEmulatedUSBCardReaderEnabled);
1616 return E_NOTIMPL;
1617#endif
1618}
1619
1620HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1621{
1622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 *aHPETEnabled = mHWData->mHPETEnabled;
1625
1626 return S_OK;
1627}
1628
1629HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1630{
1631 HRESULT rc = S_OK;
1632
1633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1634
1635 rc = i_checkStateDependency(MutableStateDep);
1636 if (FAILED(rc)) return rc;
1637
1638 i_setModified(IsModified_MachineData);
1639 mHWData.backup();
1640
1641 mHWData->mHPETEnabled = aHPETEnabled;
1642
1643 return rc;
1644}
1645
1646HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1647{
1648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1649
1650 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1651 return S_OK;
1652}
1653
1654HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1655{
1656 HRESULT rc = S_OK;
1657
1658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1659
1660 i_setModified(IsModified_MachineData);
1661 mHWData.backup();
1662 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1663
1664 alock.release();
1665 rc = i_onVideoCaptureChange();
1666 alock.acquire();
1667 if (FAILED(rc))
1668 {
1669 /*
1670 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1671 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1672 * determine if it should start or stop capturing. Therefore we need to manually
1673 * undo change.
1674 */
1675 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1676 return rc;
1677 }
1678
1679 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1680 if (Global::IsOnline(mData->mMachineState))
1681 i_saveSettings(NULL);
1682
1683 return rc;
1684}
1685
1686HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1687{
1688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1689 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1690 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1691 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1692 return S_OK;
1693}
1694
1695HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1696{
1697 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1698 bool fChanged = false;
1699
1700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1701
1702 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1703 {
1704 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1705 {
1706 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1707 fChanged = true;
1708 }
1709 }
1710 if (fChanged)
1711 {
1712 alock.release();
1713 HRESULT rc = i_onVideoCaptureChange();
1714 alock.acquire();
1715 if (FAILED(rc)) return rc;
1716 i_setModified(IsModified_MachineData);
1717
1718 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1719 if (Global::IsOnline(mData->mMachineState))
1720 i_saveSettings(NULL);
1721 }
1722
1723 return S_OK;
1724}
1725
1726HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1727{
1728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1729 if (mHWData->mVideoCaptureFile.isEmpty())
1730 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1731 else
1732 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1733 return S_OK;
1734}
1735
1736HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1737{
1738 Utf8Str strFile(aVideoCaptureFile);
1739 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1740
1741 if ( Global::IsOnline(mData->mMachineState)
1742 && mHWData->mVideoCaptureEnabled)
1743 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1744
1745 if (!RTPathStartsWithRoot(strFile.c_str()))
1746 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1747
1748 if (!strFile.isEmpty())
1749 {
1750 Utf8Str defaultFile;
1751 i_getDefaultVideoCaptureFile(defaultFile);
1752 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1753 strFile.setNull();
1754 }
1755
1756 i_setModified(IsModified_MachineData);
1757 mHWData.backup();
1758 mHWData->mVideoCaptureFile = strFile;
1759
1760 return S_OK;
1761}
1762
1763HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1764{
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1767 return S_OK;
1768}
1769
1770HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1771{
1772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1773
1774 if ( Global::IsOnline(mData->mMachineState)
1775 && mHWData->mVideoCaptureEnabled)
1776 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1777
1778 i_setModified(IsModified_MachineData);
1779 mHWData.backup();
1780 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1781
1782 return S_OK;
1783}
1784
1785HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1786{
1787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1788 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1789 return S_OK;
1790}
1791
1792HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1793{
1794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 if ( Global::IsOnline(mData->mMachineState)
1797 && mHWData->mVideoCaptureEnabled)
1798 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1799
1800 i_setModified(IsModified_MachineData);
1801 mHWData.backup();
1802 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1803
1804 return S_OK;
1805}
1806
1807HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1808{
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1811 return S_OK;
1812}
1813
1814HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1815{
1816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1817
1818 if ( Global::IsOnline(mData->mMachineState)
1819 && mHWData->mVideoCaptureEnabled)
1820 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1821
1822 i_setModified(IsModified_MachineData);
1823 mHWData.backup();
1824 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1830{
1831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1832 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1833 return S_OK;
1834}
1835
1836HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1837{
1838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 if ( Global::IsOnline(mData->mMachineState)
1841 && mHWData->mVideoCaptureEnabled)
1842 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1843
1844 i_setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1847
1848 return S_OK;
1849}
1850
1851HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1852{
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1855 return S_OK;
1856}
1857
1858HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1859{
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 if ( Global::IsOnline(mData->mMachineState)
1863 && mHWData->mVideoCaptureEnabled)
1864 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1865
1866 i_setModified(IsModified_MachineData);
1867 mHWData.backup();
1868 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1869
1870 return S_OK;
1871}
1872
1873HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1877 return S_OK;
1878}
1879
1880HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1881{
1882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1883
1884 if ( Global::IsOnline(mData->mMachineState)
1885 && mHWData->mVideoCaptureEnabled)
1886 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1887
1888 i_setModified(IsModified_MachineData);
1889 mHWData.backup();
1890 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1891
1892 return S_OK;
1893}
1894
1895HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1896{
1897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1898
1899 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1900 return S_OK;
1901}
1902
1903HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1904{
1905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 if ( Global::IsOnline(mData->mMachineState)
1908 && mHWData->mVideoCaptureEnabled)
1909 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1910
1911 i_setModified(IsModified_MachineData);
1912 mHWData.backup();
1913 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1914
1915 return S_OK;
1916}
1917
1918HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1919{
1920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1921
1922 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1923
1924 return S_OK;
1925}
1926
1927HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1928{
1929 switch (aGraphicsControllerType)
1930 {
1931 case GraphicsControllerType_Null:
1932 case GraphicsControllerType_VBoxVGA:
1933#ifdef VBOX_WITH_VMSVGA
1934 case GraphicsControllerType_VMSVGA:
1935#endif
1936 break;
1937 default:
1938 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1939 }
1940
1941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1942
1943 HRESULT rc = i_checkStateDependency(MutableStateDep);
1944 if (FAILED(rc)) return rc;
1945
1946 i_setModified(IsModified_MachineData);
1947 mHWData.backup();
1948 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1949
1950 return S_OK;
1951}
1952
1953HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1954{
1955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1956
1957 *aVRAMSize = mHWData->mVRAMSize;
1958
1959 return S_OK;
1960}
1961
1962HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1963{
1964 /* check VRAM limits */
1965 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1966 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1967 return setError(E_INVALIDARG,
1968 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1969 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1970
1971 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 HRESULT rc = i_checkStateDependency(MutableStateDep);
1974 if (FAILED(rc)) return rc;
1975
1976 i_setModified(IsModified_MachineData);
1977 mHWData.backup();
1978 mHWData->mVRAMSize = aVRAMSize;
1979
1980 return S_OK;
1981}
1982
1983/** @todo this method should not be public */
1984HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1985{
1986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1987
1988 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1989
1990 return S_OK;
1991}
1992
1993/**
1994 * Set the memory balloon size.
1995 *
1996 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1997 * we have to make sure that we never call IGuest from here.
1998 */
1999HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2000{
2001 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2002#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2003 /* check limits */
2004 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2005 return setError(E_INVALIDARG,
2006 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2007 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2008
2009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2010
2011 i_setModified(IsModified_MachineData);
2012 mHWData.backup();
2013 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2014
2015 return S_OK;
2016#else
2017 NOREF(aMemoryBalloonSize);
2018 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2019#endif
2020}
2021
2022HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2023{
2024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2025
2026 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2027 return S_OK;
2028}
2029
2030HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2031{
2032#ifdef VBOX_WITH_PAGE_SHARING
2033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2034
2035 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2036 i_setModified(IsModified_MachineData);
2037 mHWData.backup();
2038 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2039 return S_OK;
2040#else
2041 NOREF(aPageFusionEnabled);
2042 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2043#endif
2044}
2045
2046HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2047{
2048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2051
2052 return S_OK;
2053}
2054
2055HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2056{
2057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 HRESULT rc = i_checkStateDependency(MutableStateDep);
2060 if (FAILED(rc)) return rc;
2061
2062 /** @todo check validity! */
2063
2064 i_setModified(IsModified_MachineData);
2065 mHWData.backup();
2066 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2067
2068 return S_OK;
2069}
2070
2071
2072HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2073{
2074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2075
2076 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2077
2078 return S_OK;
2079}
2080
2081HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2082{
2083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2084
2085 HRESULT rc = i_checkStateDependency(MutableStateDep);
2086 if (FAILED(rc)) return rc;
2087
2088 /** @todo check validity! */
2089 i_setModified(IsModified_MachineData);
2090 mHWData.backup();
2091 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2092
2093 return S_OK;
2094}
2095
2096HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2097{
2098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2099
2100 *aMonitorCount = mHWData->mMonitorCount;
2101
2102 return S_OK;
2103}
2104
2105HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2106{
2107 /* make sure monitor count is a sensible number */
2108 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2109 return setError(E_INVALIDARG,
2110 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2111 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2112
2113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2114
2115 HRESULT rc = i_checkStateDependency(MutableStateDep);
2116 if (FAILED(rc)) return rc;
2117
2118 i_setModified(IsModified_MachineData);
2119 mHWData.backup();
2120 mHWData->mMonitorCount = aMonitorCount;
2121
2122 return S_OK;
2123}
2124
2125HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2126{
2127 /* mBIOSSettings is constant during life time, no need to lock */
2128 aBIOSSettings = mBIOSSettings;
2129
2130 return S_OK;
2131}
2132
2133HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2134{
2135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2136
2137 switch (aProperty)
2138 {
2139 case CPUPropertyType_PAE:
2140 *aValue = mHWData->mPAEEnabled;
2141 break;
2142
2143 case CPUPropertyType_Synthetic:
2144 *aValue = mHWData->mSyntheticCpu;
2145 break;
2146
2147 case CPUPropertyType_LongMode:
2148 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2149 *aValue = TRUE;
2150 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2151 *aValue = FALSE;
2152#if HC_ARCH_BITS == 64
2153 else
2154 *aValue = TRUE;
2155#else
2156 else
2157 {
2158 *aValue = FALSE;
2159
2160 ComPtr<IGuestOSType> ptrGuestOSType;
2161 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2162 if (SUCCEEDED(hrc2))
2163 {
2164 BOOL fIs64Bit = FALSE;
2165 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2166 if (SUCCEEDED(hrc2) && fIs64Bit)
2167 {
2168 ComObjPtr<Host> ptrHost = mParent->i_host();
2169 alock.release();
2170
2171 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2172 if (FAILED(hrc2))
2173 *aValue = FALSE;
2174 }
2175 }
2176 }
2177#endif
2178 break;
2179
2180 case CPUPropertyType_TripleFaultReset:
2181 *aValue = mHWData->mTripleFaultReset;
2182 break;
2183
2184 default:
2185 return E_INVALIDARG;
2186 }
2187 return S_OK;
2188}
2189
2190HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2191{
2192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2193
2194 HRESULT rc = i_checkStateDependency(MutableStateDep);
2195 if (FAILED(rc)) return rc;
2196
2197 switch (aProperty)
2198 {
2199 case CPUPropertyType_PAE:
2200 i_setModified(IsModified_MachineData);
2201 mHWData.backup();
2202 mHWData->mPAEEnabled = !!aValue;
2203 break;
2204
2205 case CPUPropertyType_Synthetic:
2206 i_setModified(IsModified_MachineData);
2207 mHWData.backup();
2208 mHWData->mSyntheticCpu = !!aValue;
2209 break;
2210
2211 case CPUPropertyType_LongMode:
2212 i_setModified(IsModified_MachineData);
2213 mHWData.backup();
2214 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2215 break;
2216
2217 case CPUPropertyType_TripleFaultReset:
2218 i_setModified(IsModified_MachineData);
2219 mHWData.backup();
2220 mHWData->mTripleFaultReset = !!aValue;
2221 break;
2222
2223 default:
2224 return E_INVALIDARG;
2225 }
2226 return S_OK;
2227}
2228
2229HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2230{
2231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2232
2233 switch(aId)
2234 {
2235 case 0x0:
2236 case 0x1:
2237 case 0x2:
2238 case 0x3:
2239 case 0x4:
2240 case 0x5:
2241 case 0x6:
2242 case 0x7:
2243 case 0x8:
2244 case 0x9:
2245 case 0xA:
2246 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2247 return E_INVALIDARG;
2248
2249 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2250 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2251 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2252 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2253 break;
2254
2255 case 0x80000000:
2256 case 0x80000001:
2257 case 0x80000002:
2258 case 0x80000003:
2259 case 0x80000004:
2260 case 0x80000005:
2261 case 0x80000006:
2262 case 0x80000007:
2263 case 0x80000008:
2264 case 0x80000009:
2265 case 0x8000000A:
2266 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2267 return E_INVALIDARG;
2268
2269 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2270 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2271 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2272 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2273 break;
2274
2275 default:
2276 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2277 }
2278 return S_OK;
2279}
2280
2281
2282HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2283{
2284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2285
2286 HRESULT rc = i_checkStateDependency(MutableStateDep);
2287 if (FAILED(rc)) return rc;
2288
2289 switch(aId)
2290 {
2291 case 0x0:
2292 case 0x1:
2293 case 0x2:
2294 case 0x3:
2295 case 0x4:
2296 case 0x5:
2297 case 0x6:
2298 case 0x7:
2299 case 0x8:
2300 case 0x9:
2301 case 0xA:
2302 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2303 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2304 i_setModified(IsModified_MachineData);
2305 mHWData.backup();
2306 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2307 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2308 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2309 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2310 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2311 break;
2312
2313 case 0x80000000:
2314 case 0x80000001:
2315 case 0x80000002:
2316 case 0x80000003:
2317 case 0x80000004:
2318 case 0x80000005:
2319 case 0x80000006:
2320 case 0x80000007:
2321 case 0x80000008:
2322 case 0x80000009:
2323 case 0x8000000A:
2324 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2325 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2326 i_setModified(IsModified_MachineData);
2327 mHWData.backup();
2328 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2329 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2330 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2331 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2332 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2333 break;
2334
2335 default:
2336 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2337 }
2338 return S_OK;
2339}
2340
2341HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2342{
2343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2344
2345 HRESULT rc = i_checkStateDependency(MutableStateDep);
2346 if (FAILED(rc)) return rc;
2347
2348 switch(aId)
2349 {
2350 case 0x0:
2351 case 0x1:
2352 case 0x2:
2353 case 0x3:
2354 case 0x4:
2355 case 0x5:
2356 case 0x6:
2357 case 0x7:
2358 case 0x8:
2359 case 0x9:
2360 case 0xA:
2361 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2362 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2363 i_setModified(IsModified_MachineData);
2364 mHWData.backup();
2365 /* Invalidate leaf. */
2366 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2367 break;
2368
2369 case 0x80000000:
2370 case 0x80000001:
2371 case 0x80000002:
2372 case 0x80000003:
2373 case 0x80000004:
2374 case 0x80000005:
2375 case 0x80000006:
2376 case 0x80000007:
2377 case 0x80000008:
2378 case 0x80000009:
2379 case 0x8000000A:
2380 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2381 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2382 i_setModified(IsModified_MachineData);
2383 mHWData.backup();
2384 /* Invalidate leaf. */
2385 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2386 break;
2387
2388 default:
2389 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2390 }
2391 return S_OK;
2392}
2393
2394HRESULT Machine::removeAllCPUIDLeaves()
2395{
2396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2397
2398 HRESULT rc = i_checkStateDependency(MutableStateDep);
2399 if (FAILED(rc)) return rc;
2400
2401 i_setModified(IsModified_MachineData);
2402 mHWData.backup();
2403
2404 /* Invalidate all standard leafs. */
2405 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2406 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2407
2408 /* Invalidate all extended leafs. */
2409 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2410 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2411
2412 return S_OK;
2413}
2414HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2415{
2416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2417
2418 switch(aProperty)
2419 {
2420 case HWVirtExPropertyType_Enabled:
2421 *aValue = mHWData->mHWVirtExEnabled;
2422 break;
2423
2424 case HWVirtExPropertyType_VPID:
2425 *aValue = mHWData->mHWVirtExVPIDEnabled;
2426 break;
2427
2428 case HWVirtExPropertyType_NestedPaging:
2429 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2430 break;
2431
2432 case HWVirtExPropertyType_UnrestrictedExecution:
2433 *aValue = mHWData->mHWVirtExUXEnabled;
2434 break;
2435
2436 case HWVirtExPropertyType_LargePages:
2437 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2438#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2439 *aValue = FALSE;
2440#endif
2441 break;
2442
2443 case HWVirtExPropertyType_Force:
2444 *aValue = mHWData->mHWVirtExForceEnabled;
2445 break;
2446
2447 default:
2448 return E_INVALIDARG;
2449 }
2450 return S_OK;
2451}
2452
2453HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2454{
2455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2456
2457 HRESULT rc = i_checkStateDependency(MutableStateDep);
2458 if (FAILED(rc)) return rc;
2459
2460 switch(aProperty)
2461 {
2462 case HWVirtExPropertyType_Enabled:
2463 i_setModified(IsModified_MachineData);
2464 mHWData.backup();
2465 mHWData->mHWVirtExEnabled = !!aValue;
2466 break;
2467
2468 case HWVirtExPropertyType_VPID:
2469 i_setModified(IsModified_MachineData);
2470 mHWData.backup();
2471 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2472 break;
2473
2474 case HWVirtExPropertyType_NestedPaging:
2475 i_setModified(IsModified_MachineData);
2476 mHWData.backup();
2477 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2478 break;
2479
2480 case HWVirtExPropertyType_UnrestrictedExecution:
2481 i_setModified(IsModified_MachineData);
2482 mHWData.backup();
2483 mHWData->mHWVirtExUXEnabled = !!aValue;
2484 break;
2485
2486 case HWVirtExPropertyType_LargePages:
2487 i_setModified(IsModified_MachineData);
2488 mHWData.backup();
2489 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2490 break;
2491
2492 case HWVirtExPropertyType_Force:
2493 i_setModified(IsModified_MachineData);
2494 mHWData.backup();
2495 mHWData->mHWVirtExForceEnabled = !!aValue;
2496 break;
2497
2498 default:
2499 return E_INVALIDARG;
2500 }
2501
2502 return S_OK;
2503}
2504
2505HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2506{
2507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2510
2511 return S_OK;
2512}
2513
2514HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2515{
2516 /* @todo (r=dmik):
2517 * 1. Allow to change the name of the snapshot folder containing snapshots
2518 * 2. Rename the folder on disk instead of just changing the property
2519 * value (to be smart and not to leave garbage). Note that it cannot be
2520 * done here because the change may be rolled back. Thus, the right
2521 * place is #saveSettings().
2522 */
2523
2524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2525
2526 HRESULT rc = i_checkStateDependency(MutableStateDep);
2527 if (FAILED(rc)) return rc;
2528
2529 if (!mData->mCurrentSnapshot.isNull())
2530 return setError(E_FAIL,
2531 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2532
2533 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2534
2535 if (strSnapshotFolder.isEmpty())
2536 strSnapshotFolder = "Snapshots";
2537 int vrc = i_calculateFullPath(strSnapshotFolder,
2538 strSnapshotFolder);
2539 if (RT_FAILURE(vrc))
2540 return setError(E_FAIL,
2541 tr("Invalid snapshot folder '%s' (%Rrc)"),
2542 strSnapshotFolder.c_str(), vrc);
2543
2544 i_setModified(IsModified_MachineData);
2545 mUserData.backup();
2546
2547 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2548
2549 return S_OK;
2550}
2551
2552HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2553{
2554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2555
2556 aMediumAttachments.resize(mMediaData->mAttachments.size());
2557 size_t i = 0;
2558 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2559 it != mMediaData->mAttachments.end(); ++it, ++i)
2560 aMediumAttachments[i] = *it;
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2566{
2567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569 Assert(!!mVRDEServer);
2570
2571 aVRDEServer = mVRDEServer;
2572
2573 return S_OK;
2574}
2575
2576HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2577{
2578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2579
2580 aAudioAdapter = mAudioAdapter;
2581
2582 return S_OK;
2583}
2584
2585HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2586{
2587#ifdef VBOX_WITH_VUSB
2588 clearError();
2589 MultiResult rc(S_OK);
2590
2591# ifdef VBOX_WITH_USB
2592 rc = mParent->i_host()->i_checkUSBProxyService();
2593 if (FAILED(rc)) return rc;
2594# endif
2595
2596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2597
2598 USBControllerList data = *mUSBControllers.data();
2599 aUSBControllers.resize(data.size());
2600 size_t i = 0;
2601 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2602 aUSBControllers[i] = *it;
2603
2604 return S_OK;
2605#else
2606 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2607 * extended error info to indicate that USB is simply not available
2608 * (w/o treating it as a failure), for example, as in OSE */
2609 NOREF(aUSBControllers);
2610 ReturnComNotImplemented();
2611#endif /* VBOX_WITH_VUSB */
2612}
2613
2614HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2615{
2616#ifdef VBOX_WITH_VUSB
2617 clearError();
2618 MultiResult rc(S_OK);
2619
2620# ifdef VBOX_WITH_USB
2621 rc = mParent->i_host()->i_checkUSBProxyService();
2622 if (FAILED(rc)) return rc;
2623# endif
2624
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 aUSBDeviceFilters = mUSBDeviceFilters;
2628 return rc;
2629#else
2630 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2631 * extended error info to indicate that USB is simply not available
2632 * (w/o treating it as a failure), for example, as in OSE */
2633 NOREF(aUSBDeviceFilters);
2634 ReturnComNotImplemented();
2635#endif /* VBOX_WITH_VUSB */
2636}
2637
2638HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2639{
2640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 aSettingsFilePath = mData->m_strConfigFileFull;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2648{
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2652 if (FAILED(rc)) return rc;
2653
2654 if (!mData->pMachineConfigFile->fileExists())
2655 // this is a new machine, and no config file exists yet:
2656 *aSettingsModified = TRUE;
2657 else
2658 *aSettingsModified = (mData->flModifications != 0);
2659
2660 return S_OK;
2661}
2662
2663HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2664{
2665
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 *aSessionState = mData->mSession.mState;
2669
2670 return S_OK;
2671}
2672
2673HRESULT Machine::getSessionType(com::Utf8Str &aSessionType)
2674{
2675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
2677 aSessionType = mData->mSession.mType;
2678
2679 return S_OK;
2680}
2681
2682HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2683{
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 *aSessionPID = mData->mSession.mPID;
2687
2688 return S_OK;
2689}
2690
2691HRESULT Machine::getState(MachineState_T *aState)
2692{
2693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 *aState = mData->mMachineState;
2696
2697 return S_OK;
2698}
2699
2700HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2701{
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2705
2706 return S_OK;
2707}
2708
2709HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2710{
2711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2712
2713 aStateFilePath = mSSData->strStateFilePath;
2714
2715 return S_OK;
2716}
2717
2718HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2719{
2720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2721
2722 i_getLogFolder(aLogFolder);
2723
2724 return S_OK;
2725}
2726
2727HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2728{
2729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2730
2731 aCurrentSnapshot = mData->mCurrentSnapshot;
2732
2733 return S_OK;
2734}
2735
2736HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2737{
2738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2741 ? 0
2742 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2743
2744 return S_OK;
2745}
2746
2747HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 /* Note: for machines with no snapshots, we always return FALSE
2752 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2753 * reasons :) */
2754
2755 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2756 ? FALSE
2757 : mData->mCurrentStateModified;
2758
2759 return S_OK;
2760}
2761
2762HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2763{
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 aSharedFolders.resize(mHWData->mSharedFolders.size());
2767 size_t i = 0;
2768 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2769 it != mHWData->mSharedFolders.end(); ++i, ++it)
2770 aSharedFolders[i] = *it;
2771
2772 return S_OK;
2773}
2774
2775HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2776{
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 *aClipboardMode = mHWData->mClipboardMode;
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2785{
2786 HRESULT rc = S_OK;
2787
2788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 alock.release();
2791 rc = i_onClipboardModeChange(aClipboardMode);
2792 alock.acquire();
2793 if (FAILED(rc)) return rc;
2794
2795 i_setModified(IsModified_MachineData);
2796 mHWData.backup();
2797 mHWData->mClipboardMode = aClipboardMode;
2798
2799 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2800 if (Global::IsOnline(mData->mMachineState))
2801 i_saveSettings(NULL);
2802
2803 return S_OK;
2804}
2805
2806HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2807{
2808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2809
2810 *aDnDMode = mHWData->mDnDMode;
2811
2812 return S_OK;
2813}
2814
2815HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2816{
2817 HRESULT rc = S_OK;
2818
2819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 alock.release();
2822 rc = i_onDnDModeChange(aDnDMode);
2823
2824 alock.acquire();
2825 if (FAILED(rc)) return rc;
2826
2827 i_setModified(IsModified_MachineData);
2828 mHWData.backup();
2829 mHWData->mDnDMode = aDnDMode;
2830
2831 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2832 if (Global::IsOnline(mData->mMachineState))
2833 i_saveSettings(NULL);
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getGuestPropertyNotificationPatterns(com::Utf8Str &aGuestPropertyNotificationPatterns)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 try
2843 {
2844 aGuestPropertyNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
2845 }
2846 catch (...)
2847 {
2848 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2849 }
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::setGuestPropertyNotificationPatterns(const com::Utf8Str &aGuestPropertyNotificationPatterns)
2855{
2856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2859 if (FAILED(rc)) return rc;
2860
2861 i_setModified(IsModified_MachineData);
2862 mHWData.backup();
2863 mHWData->mGuestPropertyNotificationPatterns = aGuestPropertyNotificationPatterns;
2864 return rc;
2865}
2866
2867HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2868{
2869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2870 StorageControllerList data = *mStorageControllers.data();
2871 size_t i = 0;
2872 aStorageControllers.resize(data.size());
2873 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2874 aStorageControllers[i] = *it;
2875 return S_OK;
2876}
2877
2878HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2879{
2880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2881
2882 *aEnabled = mUserData->s.fTeleporterEnabled;
2883
2884 return S_OK;
2885}
2886
2887HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2888{
2889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2890
2891 /* Only allow it to be set to true when PoweredOff or Aborted.
2892 (Clearing it is always permitted.) */
2893 if ( aTeleporterEnabled
2894 && mData->mRegistered
2895 && ( !i_isSessionMachine()
2896 || ( mData->mMachineState != MachineState_PoweredOff
2897 && mData->mMachineState != MachineState_Teleported
2898 && mData->mMachineState != MachineState_Aborted
2899 )
2900 )
2901 )
2902 return setError(VBOX_E_INVALID_VM_STATE,
2903 tr("The machine is not powered off (state is %s)"),
2904 Global::stringifyMachineState(mData->mMachineState));
2905
2906 i_setModified(IsModified_MachineData);
2907 mUserData.backup();
2908 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2909
2910 return S_OK;
2911}
2912
2913HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2914{
2915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2923{
2924 if (aTeleporterPort >= _64K)
2925 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2926
2927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 HRESULT rc = i_checkStateDependency(MutableStateDep);
2930 if (FAILED(rc)) return rc;
2931
2932 i_setModified(IsModified_MachineData);
2933 mUserData.backup();
2934 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2935
2936 return S_OK;
2937}
2938
2939HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2940{
2941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2942
2943 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2949{
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 HRESULT rc = i_checkStateDependency(MutableStateDep);
2953 if (FAILED(rc)) return rc;
2954
2955 i_setModified(IsModified_MachineData);
2956 mUserData.backup();
2957 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2958
2959 return S_OK;
2960}
2961
2962HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2963{
2964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2965 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2966
2967 return S_OK;
2968}
2969
2970HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2971{
2972 /*
2973 * Hash the password first.
2974 */
2975 com::Utf8Str aT = aTeleporterPassword;
2976
2977 if (!aT.isEmpty())
2978 {
2979 if (VBoxIsPasswordHashed(&aT))
2980 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2981 VBoxHashPassword(&aT);
2982 }
2983
2984 /*
2985 * Do the update.
2986 */
2987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2988 HRESULT hrc = i_checkStateDependency(MutableStateDep);
2989 if (SUCCEEDED(hrc))
2990 {
2991 i_setModified(IsModified_MachineData);
2992 mUserData.backup();
2993 mUserData->s.strTeleporterPassword = aT;
2994 }
2995
2996 return hrc;
2997}
2998
2999HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3000{
3001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3002
3003 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3004 return S_OK;
3005}
3006
3007HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3008{
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 /* @todo deal with running state change. */
3012 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3013 if (FAILED(rc)) return rc;
3014
3015 i_setModified(IsModified_MachineData);
3016 mUserData.backup();
3017 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3018 return S_OK;
3019}
3020
3021HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3022{
3023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3024
3025 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3026 return S_OK;
3027}
3028
3029HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3030{
3031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3032
3033 /* @todo deal with running state change. */
3034 HRESULT rc = i_checkStateDependency(MutableStateDep);
3035 if (FAILED(rc)) return rc;
3036
3037 i_setModified(IsModified_MachineData);
3038 mUserData.backup();
3039 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3040 return S_OK;
3041}
3042
3043HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3044{
3045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3046
3047 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3048 return S_OK;
3049}
3050
3051HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3052{
3053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3054
3055 /* @todo deal with running state change. */
3056 HRESULT rc = i_checkStateDependency(MutableStateDep);
3057 if (FAILED(rc)) return rc;
3058
3059 i_setModified(IsModified_MachineData);
3060 mUserData.backup();
3061 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3062 return S_OK;
3063}
3064
3065HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3066{
3067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3068
3069 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3070
3071 return S_OK;
3072}
3073
3074HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3075{
3076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3077
3078 /* @todo deal with running state change. */
3079 HRESULT rc = i_checkStateDependency(MutableStateDep);
3080 if (FAILED(rc)) return rc;
3081
3082 i_setModified(IsModified_MachineData);
3083 mUserData.backup();
3084 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3085
3086 return S_OK;
3087}
3088
3089HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3090{
3091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3092
3093 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3094 return S_OK;
3095}
3096
3097HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3098{
3099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3100
3101 /* @todo deal with running state change. */
3102 HRESULT rc = i_checkStateDependency(MutableStateDep);
3103 if (FAILED(rc)) return rc;
3104
3105 i_setModified(IsModified_MachineData);
3106 mUserData.backup();
3107 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3108 return S_OK;
3109}
3110
3111HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3112{
3113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3114
3115 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3116
3117 return S_OK;
3118}
3119
3120HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3121{
3122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3123
3124 /* Only allow it to be set to true when PoweredOff or Aborted.
3125 (Clearing it is always permitted.) */
3126 if ( aRTCUseUTC
3127 && mData->mRegistered
3128 && ( !i_isSessionMachine()
3129 || ( mData->mMachineState != MachineState_PoweredOff
3130 && mData->mMachineState != MachineState_Teleported
3131 && mData->mMachineState != MachineState_Aborted
3132 )
3133 )
3134 )
3135 return setError(VBOX_E_INVALID_VM_STATE,
3136 tr("The machine is not powered off (state is %s)"),
3137 Global::stringifyMachineState(mData->mMachineState));
3138
3139 i_setModified(IsModified_MachineData);
3140 mUserData.backup();
3141 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3142
3143 return S_OK;
3144}
3145
3146HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3147{
3148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3149
3150 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3151
3152 return S_OK;
3153}
3154
3155HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3156{
3157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3158
3159 HRESULT rc = i_checkStateDependency(MutableStateDep);
3160 if (FAILED(rc)) return rc;
3161
3162 i_setModified(IsModified_MachineData);
3163 mHWData.backup();
3164 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3165
3166 return S_OK;
3167}
3168
3169HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3170{
3171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3172
3173 *aIOCacheSize = mHWData->mIOCacheSize;
3174
3175 return S_OK;
3176}
3177
3178HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3179{
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 HRESULT rc = i_checkStateDependency(MutableStateDep);
3183 if (FAILED(rc)) return rc;
3184
3185 i_setModified(IsModified_MachineData);
3186 mHWData.backup();
3187 mHWData->mIOCacheSize = aIOCacheSize;
3188
3189 return S_OK;
3190}
3191
3192
3193/**
3194 * @note Locks objects!
3195 */
3196HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3197 LockType_T aLockType)
3198
3199{
3200 /* check the session state */
3201 SessionState_T state;
3202 HRESULT rc = aSession->COMGETTER(State)(&state);
3203 if (FAILED(rc)) return rc;
3204
3205 if (state != SessionState_Unlocked)
3206 return setError(VBOX_E_INVALID_OBJECT_STATE,
3207 tr("The given session is busy"));
3208
3209 // get the client's IInternalSessionControl interface
3210 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3211 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3212 E_INVALIDARG);
3213
3214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3215
3216 if (!mData->mRegistered)
3217 return setError(E_UNEXPECTED,
3218 tr("The machine '%s' is not registered"),
3219 mUserData->s.strName.c_str());
3220
3221 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3222
3223 SessionState_T oldState = mData->mSession.mState;
3224 /* Hack: in case the session is closing and there is a progress object
3225 * which allows waiting for the session to be closed, take the opportunity
3226 * and do a limited wait (max. 1 second). This helps a lot when the system
3227 * is busy and thus session closing can take a little while. */
3228 if ( mData->mSession.mState == SessionState_Unlocking
3229 && mData->mSession.mProgress)
3230 {
3231 alock.release();
3232 mData->mSession.mProgress->WaitForCompletion(1000);
3233 alock.acquire();
3234 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3235 }
3236
3237 // try again now
3238 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3239 // (i.e. session machine exists)
3240 && (aLockType == LockType_Shared) // caller wants a shared link to the
3241 // existing session that holds the write lock:
3242 )
3243 {
3244 // OK, share the session... we are now dealing with three processes:
3245 // 1) VBoxSVC (where this code runs);
3246 // 2) process C: the caller's client process (who wants a shared session);
3247 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3248
3249 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3250 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3251 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3252 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3253 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3254
3255 /*
3256 * Release the lock before calling the client process. It's safe here
3257 * since the only thing to do after we get the lock again is to add
3258 * the remote control to the list (which doesn't directly influence
3259 * anything).
3260 */
3261 alock.release();
3262
3263 // get the console of the session holding the write lock (this is a remote call)
3264 ComPtr<IConsole> pConsoleW;
3265 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3266 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3267 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3268 if (FAILED(rc))
3269 // the failure may occur w/o any error info (from RPC), so provide one
3270 return setError(VBOX_E_VM_ERROR,
3271 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3272
3273 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3274
3275 // share the session machine and W's console with the caller's session
3276 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3277 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3278 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3279
3280 if (FAILED(rc))
3281 // the failure may occur w/o any error info (from RPC), so provide one
3282 return setError(VBOX_E_VM_ERROR,
3283 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3284 alock.acquire();
3285
3286 // need to revalidate the state after acquiring the lock again
3287 if (mData->mSession.mState != SessionState_Locked)
3288 {
3289 pSessionControl->Uninitialize();
3290 return setError(VBOX_E_INVALID_SESSION_STATE,
3291 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3292 mUserData->s.strName.c_str());
3293 }
3294
3295 // add the caller's session to the list
3296 mData->mSession.mRemoteControls.push_back(pSessionControl);
3297 }
3298 else if ( mData->mSession.mState == SessionState_Locked
3299 || mData->mSession.mState == SessionState_Unlocking
3300 )
3301 {
3302 // sharing not permitted, or machine still unlocking:
3303 return setError(VBOX_E_INVALID_OBJECT_STATE,
3304 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3305 mUserData->s.strName.c_str());
3306 }
3307 else
3308 {
3309 // machine is not locked: then write-lock the machine (create the session machine)
3310
3311 // must not be busy
3312 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3313
3314 // get the caller's session PID
3315 RTPROCESS pid = NIL_RTPROCESS;
3316 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3317 pSessionControl->GetPID((ULONG*)&pid);
3318 Assert(pid != NIL_RTPROCESS);
3319
3320 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3321
3322 if (fLaunchingVMProcess)
3323 {
3324 if (mData->mSession.mPID == NIL_RTPROCESS)
3325 {
3326 // two or more clients racing for a lock, the one which set the
3327 // session state to Spawning will win, the others will get an
3328 // error as we can't decide here if waiting a little would help
3329 // (only for shared locks this would avoid an error)
3330 return setError(VBOX_E_INVALID_OBJECT_STATE,
3331 tr("The machine '%s' already has a lock request pending"),
3332 mUserData->s.strName.c_str());
3333 }
3334
3335 // this machine is awaiting for a spawning session to be opened:
3336 // then the calling process must be the one that got started by
3337 // LaunchVMProcess()
3338
3339 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3340 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3341
3342#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3343 /* Hardened windows builds spawns three processes when a VM is
3344 launched, the 3rd one is the one that will end up here. */
3345 RTPROCESS ppid;
3346 int rc = RTProcQueryParent(pid, &ppid);
3347 if (RT_SUCCESS(rc))
3348 rc = RTProcQueryParent(ppid, &ppid);
3349 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3350 || rc == VERR_ACCESS_DENIED)
3351 {
3352 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3353 mData->mSession.mPID = pid;
3354 }
3355#endif
3356
3357 if (mData->mSession.mPID != pid)
3358 return setError(E_ACCESSDENIED,
3359 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3360 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3361 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3362 }
3363
3364 // create the mutable SessionMachine from the current machine
3365 ComObjPtr<SessionMachine> sessionMachine;
3366 sessionMachine.createObject();
3367 rc = sessionMachine->init(this);
3368 AssertComRC(rc);
3369
3370 /* NOTE: doing return from this function after this point but
3371 * before the end is forbidden since it may call SessionMachine::uninit()
3372 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3373 * lock while still holding the Machine lock in alock so that a deadlock
3374 * is possible due to the wrong lock order. */
3375
3376 if (SUCCEEDED(rc))
3377 {
3378 /*
3379 * Set the session state to Spawning to protect against subsequent
3380 * attempts to open a session and to unregister the machine after
3381 * we release the lock.
3382 */
3383 SessionState_T origState = mData->mSession.mState;
3384 mData->mSession.mState = SessionState_Spawning;
3385
3386#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3387 /* Get the client token ID to be passed to the client process */
3388 Utf8Str strTokenId;
3389 sessionMachine->i_getTokenId(strTokenId);
3390 Assert(!strTokenId.isEmpty());
3391#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3392 /* Get the client token to be passed to the client process */
3393 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3394 /* The token is now "owned" by pToken, fix refcount */
3395 if (!pToken.isNull())
3396 pToken->Release();
3397#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3398
3399 /*
3400 * Release the lock before calling the client process -- it will call
3401 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3402 * because the state is Spawning, so that LaunchVMProcess() and
3403 * LockMachine() calls will fail. This method, called before we
3404 * acquire the lock again, will fail because of the wrong PID.
3405 *
3406 * Note that mData->mSession.mRemoteControls accessed outside
3407 * the lock may not be modified when state is Spawning, so it's safe.
3408 */
3409 alock.release();
3410
3411 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3412#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3413 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3414#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3415 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3416 /* Now the token is owned by the client process. */
3417 pToken.setNull();
3418#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3419 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3420
3421 /* The failure may occur w/o any error info (from RPC), so provide one */
3422 if (FAILED(rc))
3423 setError(VBOX_E_VM_ERROR,
3424 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3425
3426 if ( SUCCEEDED(rc)
3427 && fLaunchingVMProcess
3428 )
3429 {
3430 /* complete the remote session initialization */
3431
3432 /* get the console from the direct session */
3433 ComPtr<IConsole> console;
3434 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3435 ComAssertComRC(rc);
3436
3437 if (SUCCEEDED(rc) && !console)
3438 {
3439 ComAssert(!!console);
3440 rc = E_FAIL;
3441 }
3442
3443 /* assign machine & console to the remote session */
3444 if (SUCCEEDED(rc))
3445 {
3446 /*
3447 * after LaunchVMProcess(), the first and the only
3448 * entry in remoteControls is that remote session
3449 */
3450 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3451 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3452 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3453
3454 /* The failure may occur w/o any error info (from RPC), so provide one */
3455 if (FAILED(rc))
3456 setError(VBOX_E_VM_ERROR,
3457 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3458 }
3459
3460 if (FAILED(rc))
3461 pSessionControl->Uninitialize();
3462 }
3463
3464 /* acquire the lock again */
3465 alock.acquire();
3466
3467 /* Restore the session state */
3468 mData->mSession.mState = origState;
3469 }
3470
3471 // finalize spawning anyway (this is why we don't return on errors above)
3472 if (fLaunchingVMProcess)
3473 {
3474 /* Note that the progress object is finalized later */
3475 /** @todo Consider checking mData->mSession.mProgress for cancellation
3476 * around here. */
3477
3478 /* We don't reset mSession.mPID here because it is necessary for
3479 * SessionMachine::uninit() to reap the child process later. */
3480
3481 if (FAILED(rc))
3482 {
3483 /* Close the remote session, remove the remote control from the list
3484 * and reset session state to Closed (@note keep the code in sync
3485 * with the relevant part in checkForSpawnFailure()). */
3486
3487 Assert(mData->mSession.mRemoteControls.size() == 1);
3488 if (mData->mSession.mRemoteControls.size() == 1)
3489 {
3490 ErrorInfoKeeper eik;
3491 mData->mSession.mRemoteControls.front()->Uninitialize();
3492 }
3493
3494 mData->mSession.mRemoteControls.clear();
3495 mData->mSession.mState = SessionState_Unlocked;
3496 }
3497 }
3498 else
3499 {
3500 /* memorize PID of the directly opened session */
3501 if (SUCCEEDED(rc))
3502 mData->mSession.mPID = pid;
3503 }
3504
3505 if (SUCCEEDED(rc))
3506 {
3507 /* memorize the direct session control and cache IUnknown for it */
3508 mData->mSession.mDirectControl = pSessionControl;
3509 mData->mSession.mState = SessionState_Locked;
3510 /* associate the SessionMachine with this Machine */
3511 mData->mSession.mMachine = sessionMachine;
3512
3513 /* request an IUnknown pointer early from the remote party for later
3514 * identity checks (it will be internally cached within mDirectControl
3515 * at least on XPCOM) */
3516 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3517 NOREF(unk);
3518 }
3519
3520 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3521 * would break the lock order */
3522 alock.release();
3523
3524 /* uninitialize the created session machine on failure */
3525 if (FAILED(rc))
3526 sessionMachine->uninit();
3527
3528 }
3529
3530 if (SUCCEEDED(rc))
3531 {
3532 /*
3533 * tell the client watcher thread to update the set of
3534 * machines that have open sessions
3535 */
3536 mParent->i_updateClientWatcher();
3537
3538 if (oldState != SessionState_Locked)
3539 /* fire an event */
3540 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3541 }
3542
3543 return rc;
3544}
3545
3546/**
3547 * @note Locks objects!
3548 */
3549HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3550 const com::Utf8Str &aType,
3551 const com::Utf8Str &aEnvironment,
3552 ComPtr<IProgress> &aProgress)
3553{
3554 Utf8Str strFrontend(aType);
3555 /* "emergencystop" doesn't need the session, so skip the checks/interface
3556 * retrieval. This code doesn't quite fit in here, but introducing a
3557 * special API method would be even more effort, and would require explicit
3558 * support by every API client. It's better to hide the feature a bit. */
3559 if (strFrontend != "emergencystop")
3560 CheckComArgNotNull(aSession);
3561
3562 HRESULT rc = S_OK;
3563 if (strFrontend.isEmpty())
3564 {
3565 Bstr bstrFrontend;
3566 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3567 if (FAILED(rc))
3568 return rc;
3569 strFrontend = bstrFrontend;
3570 if (strFrontend.isEmpty())
3571 {
3572 ComPtr<ISystemProperties> systemProperties;
3573 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3574 if (FAILED(rc))
3575 return rc;
3576 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3577 if (FAILED(rc))
3578 return rc;
3579 strFrontend = bstrFrontend;
3580 }
3581 /* paranoia - emergencystop is not a valid default */
3582 if (strFrontend == "emergencystop")
3583 strFrontend = Utf8Str::Empty;
3584 }
3585 /* default frontend: Qt GUI */
3586 if (strFrontend.isEmpty())
3587 strFrontend = "GUI/Qt";
3588
3589 if (strFrontend != "emergencystop")
3590 {
3591 /* check the session state */
3592 SessionState_T state;
3593 rc = aSession->COMGETTER(State)(&state);
3594 if (FAILED(rc))
3595 return rc;
3596
3597 if (state != SessionState_Unlocked)
3598 return setError(VBOX_E_INVALID_OBJECT_STATE,
3599 tr("The given session is busy"));
3600
3601 /* get the IInternalSessionControl interface */
3602 ComPtr<IInternalSessionControl> control(aSession);
3603 ComAssertMsgRet(!control.isNull(),
3604 ("No IInternalSessionControl interface"),
3605 E_INVALIDARG);
3606
3607 /* get the teleporter enable state for the progress object init. */
3608 BOOL fTeleporterEnabled;
3609 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3610 if (FAILED(rc))
3611 return rc;
3612
3613 /* create a progress object */
3614 ComObjPtr<ProgressProxy> progress;
3615 progress.createObject();
3616 rc = progress->init(mParent,
3617 static_cast<IMachine*>(this),
3618 Bstr(tr("Starting VM")).raw(),
3619 TRUE /* aCancelable */,
3620 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3621 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3622 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3623 2 /* uFirstOperationWeight */,
3624 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3625
3626 if (SUCCEEDED(rc))
3627 {
3628 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3629 if (SUCCEEDED(rc))
3630 {
3631 aProgress = progress;
3632
3633 /* signal the client watcher thread */
3634 mParent->i_updateClientWatcher();
3635
3636 /* fire an event */
3637 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3638 }
3639 }
3640 }
3641 else
3642 {
3643 /* no progress object - either instant success or failure */
3644 aProgress = NULL;
3645
3646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3647
3648 if (mData->mSession.mState != SessionState_Locked)
3649 return setError(VBOX_E_INVALID_OBJECT_STATE,
3650 tr("The machine '%s' is not locked by a session"),
3651 mUserData->s.strName.c_str());
3652
3653 /* must have a VM process associated - do not kill normal API clients
3654 * with an open session */
3655 if (!Global::IsOnline(mData->mMachineState))
3656 return setError(VBOX_E_INVALID_OBJECT_STATE,
3657 tr("The machine '%s' does not have a VM process"),
3658 mUserData->s.strName.c_str());
3659
3660 /* forcibly terminate the VM process */
3661 if (mData->mSession.mPID != NIL_RTPROCESS)
3662 RTProcTerminate(mData->mSession.mPID);
3663
3664 /* signal the client watcher thread, as most likely the client has
3665 * been terminated */
3666 mParent->i_updateClientWatcher();
3667 }
3668
3669 return rc;
3670}
3671
3672HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3673{
3674 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3675 return setError(E_INVALIDARG,
3676 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3677 aPosition, SchemaDefs::MaxBootPosition);
3678
3679 if (aDevice == DeviceType_USB)
3680 return setError(E_NOTIMPL,
3681 tr("Booting from USB device is currently not supported"));
3682
3683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3684
3685 HRESULT rc = i_checkStateDependency(MutableStateDep);
3686 if (FAILED(rc)) return rc;
3687
3688 i_setModified(IsModified_MachineData);
3689 mHWData.backup();
3690 mHWData->mBootOrder[aPosition - 1] = aDevice;
3691
3692 return S_OK;
3693}
3694
3695HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3696{
3697 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3698 return setError(E_INVALIDARG,
3699 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3700 aPosition, SchemaDefs::MaxBootPosition);
3701
3702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3703
3704 *aDevice = mHWData->mBootOrder[aPosition - 1];
3705
3706 return S_OK;
3707}
3708
3709HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3710 LONG aControllerPort,
3711 LONG aDevice,
3712 DeviceType_T aType,
3713 const ComPtr<IMedium> &aMedium)
3714{
3715 IMedium *aM = aMedium;
3716 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3717 aName.c_str(), aControllerPort, aDevice, aType, aM));
3718
3719 // request the host lock first, since might be calling Host methods for getting host drives;
3720 // next, protect the media tree all the while we're in here, as well as our member variables
3721 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3722 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3723
3724 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3725 if (FAILED(rc)) return rc;
3726
3727 /// @todo NEWMEDIA implicit machine registration
3728 if (!mData->mRegistered)
3729 return setError(VBOX_E_INVALID_OBJECT_STATE,
3730 tr("Cannot attach storage devices to an unregistered machine"));
3731
3732 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3733
3734 /* Check for an existing controller. */
3735 ComObjPtr<StorageController> ctl;
3736 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3737 if (FAILED(rc)) return rc;
3738
3739 StorageControllerType_T ctrlType;
3740 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3741 if (FAILED(rc))
3742 return setError(E_FAIL,
3743 tr("Could not get type of controller '%s'"),
3744 aName.c_str());
3745
3746 bool fSilent = false;
3747 Utf8Str strReconfig;
3748
3749 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3750 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3751 if ( mData->mMachineState == MachineState_Paused
3752 && strReconfig == "1")
3753 fSilent = true;
3754
3755 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3756 bool fHotplug = false;
3757 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3758 fHotplug = true;
3759
3760 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3761 return setError(VBOX_E_INVALID_VM_STATE,
3762 tr("Controller '%s' does not support hotplugging"),
3763 aName.c_str());
3764
3765 // check that the port and device are not out of range
3766 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3767 if (FAILED(rc)) return rc;
3768
3769 /* check if the device slot is already busy */
3770 MediumAttachment *pAttachTemp;
3771 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3772 Bstr(aName).raw(),
3773 aControllerPort,
3774 aDevice)))
3775 {
3776 Medium *pMedium = pAttachTemp->i_getMedium();
3777 if (pMedium)
3778 {
3779 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3780 return setError(VBOX_E_OBJECT_IN_USE,
3781 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3782 pMedium->i_getLocationFull().c_str(),
3783 aControllerPort,
3784 aDevice,
3785 aName.c_str());
3786 }
3787 else
3788 return setError(VBOX_E_OBJECT_IN_USE,
3789 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3790 aControllerPort, aDevice, aName.c_str());
3791 }
3792
3793 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3794 if (aMedium && medium.isNull())
3795 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3796
3797 AutoCaller mediumCaller(medium);
3798 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3799
3800 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3801
3802 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3803 && !medium.isNull()
3804 )
3805 return setError(VBOX_E_OBJECT_IN_USE,
3806 tr("Medium '%s' is already attached to this virtual machine"),
3807 medium->i_getLocationFull().c_str());
3808
3809 if (!medium.isNull())
3810 {
3811 MediumType_T mtype = medium->i_getType();
3812 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3813 // For DVDs it's not written to the config file, so needs no global config
3814 // version bump. For floppies it's a new attribute "type", which is ignored
3815 // by older VirtualBox version, so needs no global config version bump either.
3816 // For hard disks this type is not accepted.
3817 if (mtype == MediumType_MultiAttach)
3818 {
3819 // This type is new with VirtualBox 4.0 and therefore requires settings
3820 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3821 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3822 // two reasons: The medium type is a property of the media registry tree, which
3823 // can reside in the global config file (for pre-4.0 media); we would therefore
3824 // possibly need to bump the global config version. We don't want to do that though
3825 // because that might make downgrading to pre-4.0 impossible.
3826 // As a result, we can only use these two new types if the medium is NOT in the
3827 // global registry:
3828 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3829 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3830 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3831 )
3832 return setError(VBOX_E_INVALID_OBJECT_STATE,
3833 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3834 "to machines that were created with VirtualBox 4.0 or later"),
3835 medium->i_getLocationFull().c_str());
3836 }
3837 }
3838
3839 bool fIndirect = false;
3840 if (!medium.isNull())
3841 fIndirect = medium->i_isReadOnly();
3842 bool associate = true;
3843
3844 do
3845 {
3846 if ( aType == DeviceType_HardDisk
3847 && mMediaData.isBackedUp())
3848 {
3849 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3850
3851 /* check if the medium was attached to the VM before we started
3852 * changing attachments in which case the attachment just needs to
3853 * be restored */
3854 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3855 {
3856 AssertReturn(!fIndirect, E_FAIL);
3857
3858 /* see if it's the same bus/channel/device */
3859 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3860 {
3861 /* the simplest case: restore the whole attachment
3862 * and return, nothing else to do */
3863 mMediaData->mAttachments.push_back(pAttachTemp);
3864
3865 /* Reattach the medium to the VM. */
3866 if (fHotplug || fSilent)
3867 {
3868 mediumLock.release();
3869 treeLock.release();
3870 alock.release();
3871
3872 MediumLockList *pMediumLockList(new MediumLockList());
3873
3874 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3875 true /* fMediumLockWrite */,
3876 false /* fMediumLockWriteAll */,
3877 NULL,
3878 *pMediumLockList);
3879 alock.acquire();
3880 if (FAILED(rc))
3881 delete pMediumLockList;
3882 else
3883 {
3884 mData->mSession.mLockedMedia.Unlock();
3885 alock.release();
3886 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3887 mData->mSession.mLockedMedia.Lock();
3888 alock.acquire();
3889 }
3890 alock.release();
3891
3892 if (SUCCEEDED(rc))
3893 {
3894 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3895 /* Remove lock list in case of error. */
3896 if (FAILED(rc))
3897 {
3898 mData->mSession.mLockedMedia.Unlock();
3899 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3900 mData->mSession.mLockedMedia.Lock();
3901 }
3902 }
3903 }
3904
3905 return S_OK;
3906 }
3907
3908 /* bus/channel/device differ; we need a new attachment object,
3909 * but don't try to associate it again */
3910 associate = false;
3911 break;
3912 }
3913 }
3914
3915 /* go further only if the attachment is to be indirect */
3916 if (!fIndirect)
3917 break;
3918
3919 /* perform the so called smart attachment logic for indirect
3920 * attachments. Note that smart attachment is only applicable to base
3921 * hard disks. */
3922
3923 if (medium->i_getParent().isNull())
3924 {
3925 /* first, investigate the backup copy of the current hard disk
3926 * attachments to make it possible to re-attach existing diffs to
3927 * another device slot w/o losing their contents */
3928 if (mMediaData.isBackedUp())
3929 {
3930 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3931
3932 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3933 uint32_t foundLevel = 0;
3934
3935 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3936 {
3937 uint32_t level = 0;
3938 MediumAttachment *pAttach = *it;
3939 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3940 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3941 if (pMedium.isNull())
3942 continue;
3943
3944 if (pMedium->i_getBase(&level) == medium)
3945 {
3946 /* skip the hard disk if its currently attached (we
3947 * cannot attach the same hard disk twice) */
3948 if (i_findAttachment(mMediaData->mAttachments,
3949 pMedium))
3950 continue;
3951
3952 /* matched device, channel and bus (i.e. attached to the
3953 * same place) will win and immediately stop the search;
3954 * otherwise the attachment that has the youngest
3955 * descendant of medium will be used
3956 */
3957 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3958 {
3959 /* the simplest case: restore the whole attachment
3960 * and return, nothing else to do */
3961 mMediaData->mAttachments.push_back(*it);
3962
3963 /* Reattach the medium to the VM. */
3964 if (fHotplug || fSilent)
3965 {
3966 mediumLock.release();
3967 treeLock.release();
3968 alock.release();
3969
3970 MediumLockList *pMediumLockList(new MediumLockList());
3971
3972 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3973 true /* fMediumLockWrite */,
3974 false /* fMediumLockWriteAll */,
3975 NULL,
3976 *pMediumLockList);
3977 alock.acquire();
3978 if (FAILED(rc))
3979 delete pMediumLockList;
3980 else
3981 {
3982 mData->mSession.mLockedMedia.Unlock();
3983 alock.release();
3984 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3985 mData->mSession.mLockedMedia.Lock();
3986 alock.acquire();
3987 }
3988 alock.release();
3989
3990 if (SUCCEEDED(rc))
3991 {
3992 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3993 /* Remove lock list in case of error. */
3994 if (FAILED(rc))
3995 {
3996 mData->mSession.mLockedMedia.Unlock();
3997 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3998 mData->mSession.mLockedMedia.Lock();
3999 }
4000 }
4001 }
4002
4003 return S_OK;
4004 }
4005 else if ( foundIt == oldAtts.end()
4006 || level > foundLevel /* prefer younger */
4007 )
4008 {
4009 foundIt = it;
4010 foundLevel = level;
4011 }
4012 }
4013 }
4014
4015 if (foundIt != oldAtts.end())
4016 {
4017 /* use the previously attached hard disk */
4018 medium = (*foundIt)->i_getMedium();
4019 mediumCaller.attach(medium);
4020 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4021 mediumLock.attach(medium);
4022 /* not implicit, doesn't require association with this VM */
4023 fIndirect = false;
4024 associate = false;
4025 /* go right to the MediumAttachment creation */
4026 break;
4027 }
4028 }
4029
4030 /* must give up the medium lock and medium tree lock as below we
4031 * go over snapshots, which needs a lock with higher lock order. */
4032 mediumLock.release();
4033 treeLock.release();
4034
4035 /* then, search through snapshots for the best diff in the given
4036 * hard disk's chain to base the new diff on */
4037
4038 ComObjPtr<Medium> base;
4039 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4040 while (snap)
4041 {
4042 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4043
4044 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4045
4046 MediumAttachment *pAttachFound = NULL;
4047 uint32_t foundLevel = 0;
4048
4049 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4050 {
4051 MediumAttachment *pAttach = *it;
4052 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4053 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4054 if (pMedium.isNull())
4055 continue;
4056
4057 uint32_t level = 0;
4058 if (pMedium->i_getBase(&level) == medium)
4059 {
4060 /* matched device, channel and bus (i.e. attached to the
4061 * same place) will win and immediately stop the search;
4062 * otherwise the attachment that has the youngest
4063 * descendant of medium will be used
4064 */
4065 if ( pAttach->i_getDevice() == aDevice
4066 && pAttach->i_getPort() == aControllerPort
4067 && pAttach->i_getControllerName() == aName
4068 )
4069 {
4070 pAttachFound = pAttach;
4071 break;
4072 }
4073 else if ( !pAttachFound
4074 || level > foundLevel /* prefer younger */
4075 )
4076 {
4077 pAttachFound = pAttach;
4078 foundLevel = level;
4079 }
4080 }
4081 }
4082
4083 if (pAttachFound)
4084 {
4085 base = pAttachFound->i_getMedium();
4086 break;
4087 }
4088
4089 snap = snap->i_getParent();
4090 }
4091
4092 /* re-lock medium tree and the medium, as we need it below */
4093 treeLock.acquire();
4094 mediumLock.acquire();
4095
4096 /* found a suitable diff, use it as a base */
4097 if (!base.isNull())
4098 {
4099 medium = base;
4100 mediumCaller.attach(medium);
4101 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4102 mediumLock.attach(medium);
4103 }
4104 }
4105
4106 Utf8Str strFullSnapshotFolder;
4107 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4108
4109 ComObjPtr<Medium> diff;
4110 diff.createObject();
4111 // store this diff in the same registry as the parent
4112 Guid uuidRegistryParent;
4113 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4114 {
4115 // parent image has no registry: this can happen if we're attaching a new immutable
4116 // image that has not yet been attached (medium then points to the base and we're
4117 // creating the diff image for the immutable, and the parent is not yet registered);
4118 // put the parent in the machine registry then
4119 mediumLock.release();
4120 treeLock.release();
4121 alock.release();
4122 i_addMediumToRegistry(medium);
4123 alock.acquire();
4124 treeLock.acquire();
4125 mediumLock.acquire();
4126 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4127 }
4128 rc = diff->init(mParent,
4129 medium->i_getPreferredDiffFormat(),
4130 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4131 uuidRegistryParent,
4132 DeviceType_HardDisk);
4133 if (FAILED(rc)) return rc;
4134
4135 /* Apply the normal locking logic to the entire chain. */
4136 MediumLockList *pMediumLockList(new MediumLockList());
4137 mediumLock.release();
4138 treeLock.release();
4139 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4140 true /* fMediumLockWrite */,
4141 false /* fMediumLockWriteAll */,
4142 medium,
4143 *pMediumLockList);
4144 treeLock.acquire();
4145 mediumLock.acquire();
4146 if (SUCCEEDED(rc))
4147 {
4148 mediumLock.release();
4149 treeLock.release();
4150 rc = pMediumLockList->Lock();
4151 treeLock.acquire();
4152 mediumLock.acquire();
4153 if (FAILED(rc))
4154 setError(rc,
4155 tr("Could not lock medium when creating diff '%s'"),
4156 diff->i_getLocationFull().c_str());
4157 else
4158 {
4159 /* will release the lock before the potentially lengthy
4160 * operation, so protect with the special state */
4161 MachineState_T oldState = mData->mMachineState;
4162 i_setMachineState(MachineState_SettingUp);
4163
4164 mediumLock.release();
4165 treeLock.release();
4166 alock.release();
4167
4168 rc = medium->i_createDiffStorage(diff,
4169 MediumVariant_Standard,
4170 pMediumLockList,
4171 NULL /* aProgress */,
4172 true /* aWait */);
4173
4174 alock.acquire();
4175 treeLock.acquire();
4176 mediumLock.acquire();
4177
4178 i_setMachineState(oldState);
4179 }
4180 }
4181
4182 /* Unlock the media and free the associated memory. */
4183 delete pMediumLockList;
4184
4185 if (FAILED(rc)) return rc;
4186
4187 /* use the created diff for the actual attachment */
4188 medium = diff;
4189 mediumCaller.attach(medium);
4190 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4191 mediumLock.attach(medium);
4192 }
4193 while (0);
4194
4195 ComObjPtr<MediumAttachment> attachment;
4196 attachment.createObject();
4197 rc = attachment->init(this,
4198 medium,
4199 aName,
4200 aControllerPort,
4201 aDevice,
4202 aType,
4203 fIndirect,
4204 false /* fPassthrough */,
4205 false /* fTempEject */,
4206 false /* fNonRotational */,
4207 false /* fDiscard */,
4208 fHotplug /* fHotPluggable */,
4209 Utf8Str::Empty);
4210 if (FAILED(rc)) return rc;
4211
4212 if (associate && !medium.isNull())
4213 {
4214 // as the last step, associate the medium to the VM
4215 rc = medium->i_addBackReference(mData->mUuid);
4216 // here we can fail because of Deleting, or being in process of creating a Diff
4217 if (FAILED(rc)) return rc;
4218
4219 mediumLock.release();
4220 treeLock.release();
4221 alock.release();
4222 i_addMediumToRegistry(medium);
4223 alock.acquire();
4224 treeLock.acquire();
4225 mediumLock.acquire();
4226 }
4227
4228 /* success: finally remember the attachment */
4229 i_setModified(IsModified_Storage);
4230 mMediaData.backup();
4231 mMediaData->mAttachments.push_back(attachment);
4232
4233 mediumLock.release();
4234 treeLock.release();
4235 alock.release();
4236
4237 if (fHotplug || fSilent)
4238 {
4239 if (!medium.isNull())
4240 {
4241 MediumLockList *pMediumLockList(new MediumLockList());
4242
4243 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4244 true /* fMediumLockWrite */,
4245 false /* fMediumLockWriteAll */,
4246 NULL,
4247 *pMediumLockList);
4248 alock.acquire();
4249 if (FAILED(rc))
4250 delete pMediumLockList;
4251 else
4252 {
4253 mData->mSession.mLockedMedia.Unlock();
4254 alock.release();
4255 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4256 mData->mSession.mLockedMedia.Lock();
4257 alock.acquire();
4258 }
4259 alock.release();
4260 }
4261
4262 if (SUCCEEDED(rc))
4263 {
4264 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4265 /* Remove lock list in case of error. */
4266 if (FAILED(rc))
4267 {
4268 mData->mSession.mLockedMedia.Unlock();
4269 mData->mSession.mLockedMedia.Remove(attachment);
4270 mData->mSession.mLockedMedia.Lock();
4271 }
4272 }
4273 }
4274
4275 mParent->i_saveModifiedRegistries();
4276
4277 return rc;
4278}
4279
4280HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4281 LONG aDevice)
4282{
4283 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4284 aName.c_str(), aControllerPort, aDevice));
4285
4286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4287
4288 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4289 if (FAILED(rc)) return rc;
4290
4291 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4292
4293 /* Check for an existing controller. */
4294 ComObjPtr<StorageController> ctl;
4295 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4296 if (FAILED(rc)) return rc;
4297
4298 StorageControllerType_T ctrlType;
4299 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4300 if (FAILED(rc))
4301 return setError(E_FAIL,
4302 tr("Could not get type of controller '%s'"),
4303 aName.c_str());
4304
4305 bool fSilent = false;
4306 Utf8Str strReconfig;
4307
4308 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4309 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4310 if ( mData->mMachineState == MachineState_Paused
4311 && strReconfig == "1")
4312 fSilent = true;
4313
4314 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4315 bool fHotplug = false;
4316 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4317 fHotplug = true;
4318
4319 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4320 return setError(VBOX_E_INVALID_VM_STATE,
4321 tr("Controller '%s' does not support hotplugging"),
4322 aName.c_str());
4323
4324 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4325 Bstr(aName).raw(),
4326 aControllerPort,
4327 aDevice);
4328 if (!pAttach)
4329 return setError(VBOX_E_OBJECT_NOT_FOUND,
4330 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4331 aDevice, aControllerPort, aName.c_str());
4332
4333 if (fHotplug && !pAttach->i_getHotPluggable())
4334 return setError(VBOX_E_NOT_SUPPORTED,
4335 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4336 aDevice, aControllerPort, aName.c_str());
4337
4338 /*
4339 * The VM has to detach the device before we delete any implicit diffs.
4340 * If this fails we can roll back without loosing data.
4341 */
4342 if (fHotplug || fSilent)
4343 {
4344 alock.release();
4345 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4346 alock.acquire();
4347 }
4348 if (FAILED(rc)) return rc;
4349
4350 /* If we are here everything went well and we can delete the implicit now. */
4351 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4352
4353 alock.release();
4354
4355 mParent->i_saveModifiedRegistries();
4356
4357 return rc;
4358}
4359
4360HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4361 LONG aDevice, BOOL aPassthrough)
4362{
4363 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4364 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4365
4366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4367
4368 HRESULT rc = i_checkStateDependency(MutableStateDep);
4369 if (FAILED(rc)) return rc;
4370
4371 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4372
4373 if (Global::IsOnlineOrTransient(mData->mMachineState))
4374 return setError(VBOX_E_INVALID_VM_STATE,
4375 tr("Invalid machine state: %s"),
4376 Global::stringifyMachineState(mData->mMachineState));
4377
4378 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4379 Bstr(aName).raw(),
4380 aControllerPort,
4381 aDevice);
4382 if (!pAttach)
4383 return setError(VBOX_E_OBJECT_NOT_FOUND,
4384 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4385 aDevice, aControllerPort, aName.c_str());
4386
4387
4388 i_setModified(IsModified_Storage);
4389 mMediaData.backup();
4390
4391 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4392
4393 if (pAttach->i_getType() != DeviceType_DVD)
4394 return setError(E_INVALIDARG,
4395 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4396 aDevice, aControllerPort, aName.c_str());
4397 pAttach->i_updatePassthrough(!!aPassthrough);
4398
4399 return S_OK;
4400}
4401
4402HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4403 LONG aDevice, BOOL aTemporaryEject)
4404{
4405
4406 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4407 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4408
4409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4410
4411 HRESULT rc = i_checkStateDependency(MutableStateDep);
4412 if (FAILED(rc)) return rc;
4413
4414 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4415 Bstr(aName).raw(),
4416 aControllerPort,
4417 aDevice);
4418 if (!pAttach)
4419 return setError(VBOX_E_OBJECT_NOT_FOUND,
4420 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4421 aDevice, aControllerPort, aName.c_str());
4422
4423
4424 i_setModified(IsModified_Storage);
4425 mMediaData.backup();
4426
4427 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4428
4429 if (pAttach->i_getType() != DeviceType_DVD)
4430 return setError(E_INVALIDARG,
4431 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4432 aDevice, aControllerPort, aName.c_str());
4433 pAttach->i_updateTempEject(!!aTemporaryEject);
4434
4435 return S_OK;
4436}
4437
4438HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4439 LONG aDevice, BOOL aNonRotational)
4440{
4441
4442 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4443 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4444
4445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4446
4447 HRESULT rc = i_checkStateDependency(MutableStateDep);
4448 if (FAILED(rc)) return rc;
4449
4450 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4451
4452 if (Global::IsOnlineOrTransient(mData->mMachineState))
4453 return setError(VBOX_E_INVALID_VM_STATE,
4454 tr("Invalid machine state: %s"),
4455 Global::stringifyMachineState(mData->mMachineState));
4456
4457 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4458 Bstr(aName).raw(),
4459 aControllerPort,
4460 aDevice);
4461 if (!pAttach)
4462 return setError(VBOX_E_OBJECT_NOT_FOUND,
4463 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4464 aDevice, aControllerPort, aName.c_str());
4465
4466
4467 i_setModified(IsModified_Storage);
4468 mMediaData.backup();
4469
4470 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4471
4472 if (pAttach->i_getType() != DeviceType_HardDisk)
4473 return setError(E_INVALIDARG,
4474 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"),
4475 aDevice, aControllerPort, aName.c_str());
4476 pAttach->i_updateNonRotational(!!aNonRotational);
4477
4478 return S_OK;
4479}
4480
4481HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4482 LONG aDevice, BOOL aDiscard)
4483{
4484
4485 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4486 aName.c_str(), aControllerPort, aDevice, aDiscard));
4487
4488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4489
4490 HRESULT rc = i_checkStateDependency(MutableStateDep);
4491 if (FAILED(rc)) return rc;
4492
4493 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4494
4495 if (Global::IsOnlineOrTransient(mData->mMachineState))
4496 return setError(VBOX_E_INVALID_VM_STATE,
4497 tr("Invalid machine state: %s"),
4498 Global::stringifyMachineState(mData->mMachineState));
4499
4500 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4501 Bstr(aName).raw(),
4502 aControllerPort,
4503 aDevice);
4504 if (!pAttach)
4505 return setError(VBOX_E_OBJECT_NOT_FOUND,
4506 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4507 aDevice, aControllerPort, aName.c_str());
4508
4509
4510 i_setModified(IsModified_Storage);
4511 mMediaData.backup();
4512
4513 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4514
4515 if (pAttach->i_getType() != DeviceType_HardDisk)
4516 return setError(E_INVALIDARG,
4517 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"),
4518 aDevice, aControllerPort, aName.c_str());
4519 pAttach->i_updateDiscard(!!aDiscard);
4520
4521 return S_OK;
4522}
4523
4524HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4525 LONG aDevice, BOOL aHotPluggable)
4526{
4527 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4528 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4529
4530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4531
4532 HRESULT rc = i_checkStateDependency(MutableStateDep);
4533 if (FAILED(rc)) return rc;
4534
4535 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4536
4537 if (Global::IsOnlineOrTransient(mData->mMachineState))
4538 return setError(VBOX_E_INVALID_VM_STATE,
4539 tr("Invalid machine state: %s"),
4540 Global::stringifyMachineState(mData->mMachineState));
4541
4542 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4543 Bstr(aName).raw(),
4544 aControllerPort,
4545 aDevice);
4546 if (!pAttach)
4547 return setError(VBOX_E_OBJECT_NOT_FOUND,
4548 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4549 aDevice, aControllerPort, aName.c_str());
4550
4551 /* Check for an existing controller. */
4552 ComObjPtr<StorageController> ctl;
4553 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4554 if (FAILED(rc)) return rc;
4555
4556 StorageControllerType_T ctrlType;
4557 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4558 if (FAILED(rc))
4559 return setError(E_FAIL,
4560 tr("Could not get type of controller '%s'"),
4561 aName.c_str());
4562
4563 if (!i_isControllerHotplugCapable(ctrlType))
4564 return setError(VBOX_E_NOT_SUPPORTED,
4565 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4566 aName.c_str());
4567
4568 i_setModified(IsModified_Storage);
4569 mMediaData.backup();
4570
4571 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4572
4573 if (pAttach->i_getType() == DeviceType_Floppy)
4574 return setError(E_INVALIDARG,
4575 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"),
4576 aDevice, aControllerPort, aName.c_str());
4577 pAttach->i_updateHotPluggable(!!aHotPluggable);
4578
4579 return S_OK;
4580}
4581
4582HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4583 LONG aDevice)
4584{
4585 int rc = S_OK;
4586 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4587 aName.c_str(), aControllerPort, aDevice));
4588
4589 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4590
4591 return rc;
4592}
4593
4594HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4595 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4596{
4597 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4598 aName.c_str(), aControllerPort, aDevice));
4599
4600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4601
4602 HRESULT rc = i_checkStateDependency(MutableStateDep);
4603 if (FAILED(rc)) return rc;
4604
4605 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4606
4607 if (Global::IsOnlineOrTransient(mData->mMachineState))
4608 return setError(VBOX_E_INVALID_VM_STATE,
4609 tr("Invalid machine state: %s"),
4610 Global::stringifyMachineState(mData->mMachineState));
4611
4612 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4613 Bstr(aName).raw(),
4614 aControllerPort,
4615 aDevice);
4616 if (!pAttach)
4617 return setError(VBOX_E_OBJECT_NOT_FOUND,
4618 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4619 aDevice, aControllerPort, aName.c_str());
4620
4621
4622 i_setModified(IsModified_Storage);
4623 mMediaData.backup();
4624
4625 IBandwidthGroup *iB = aBandwidthGroup;
4626 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4627 if (aBandwidthGroup && group.isNull())
4628 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4629
4630 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4631
4632 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4633 if (strBandwidthGroupOld.isNotEmpty())
4634 {
4635 /* Get the bandwidth group object and release it - this must not fail. */
4636 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4637 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4638 Assert(SUCCEEDED(rc));
4639
4640 pBandwidthGroupOld->i_release();
4641 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4642 }
4643
4644 if (!group.isNull())
4645 {
4646 group->i_reference();
4647 pAttach->i_updateBandwidthGroup(group->i_getName());
4648 }
4649
4650 return S_OK;
4651}
4652
4653HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4654 LONG aControllerPort,
4655 LONG aDevice,
4656 DeviceType_T aType)
4657{
4658 HRESULT rc = S_OK;
4659
4660 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4661 aName.c_str(), aControllerPort, aDevice, aType));
4662
4663 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4664
4665 return rc;
4666}
4667
4668
4669HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4670 LONG aControllerPort,
4671 LONG aDevice,
4672 BOOL aForce)
4673{
4674 int rc = S_OK;
4675 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4676 aName.c_str(), aControllerPort, aForce));
4677
4678 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4679
4680 return rc;
4681}
4682
4683HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4684 LONG aControllerPort,
4685 LONG aDevice,
4686 const ComPtr<IMedium> &aMedium,
4687 BOOL aForce)
4688{
4689 int rc = S_OK;
4690 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4691 aName.c_str(), aControllerPort, aDevice, aForce));
4692
4693 // request the host lock first, since might be calling Host methods for getting host drives;
4694 // next, protect the media tree all the while we're in here, as well as our member variables
4695 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4696 this->lockHandle(),
4697 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4698
4699 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4700 Bstr(aName).raw(),
4701 aControllerPort,
4702 aDevice);
4703 if (pAttach.isNull())
4704 return setError(VBOX_E_OBJECT_NOT_FOUND,
4705 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4706 aDevice, aControllerPort, aName.c_str());
4707
4708 /* Remember previously mounted medium. The medium before taking the
4709 * backup is not necessarily the same thing. */
4710 ComObjPtr<Medium> oldmedium;
4711 oldmedium = pAttach->i_getMedium();
4712
4713 IMedium *iM = aMedium;
4714 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4715 if (aMedium && pMedium.isNull())
4716 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4717
4718 AutoCaller mediumCaller(pMedium);
4719 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4720
4721 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4722 if (pMedium)
4723 {
4724 DeviceType_T mediumType = pAttach->i_getType();
4725 switch (mediumType)
4726 {
4727 case DeviceType_DVD:
4728 case DeviceType_Floppy:
4729 break;
4730
4731 default:
4732 return setError(VBOX_E_INVALID_OBJECT_STATE,
4733 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4734 aControllerPort,
4735 aDevice,
4736 aName.c_str());
4737 }
4738 }
4739
4740 i_setModified(IsModified_Storage);
4741 mMediaData.backup();
4742
4743 {
4744 // The backup operation makes the pAttach reference point to the
4745 // old settings. Re-get the correct reference.
4746 pAttach = i_findAttachment(mMediaData->mAttachments,
4747 Bstr(aName).raw(),
4748 aControllerPort,
4749 aDevice);
4750 if (!oldmedium.isNull())
4751 oldmedium->i_removeBackReference(mData->mUuid);
4752 if (!pMedium.isNull())
4753 {
4754 pMedium->i_addBackReference(mData->mUuid);
4755
4756 mediumLock.release();
4757 multiLock.release();
4758 i_addMediumToRegistry(pMedium);
4759 multiLock.acquire();
4760 mediumLock.acquire();
4761 }
4762
4763 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4764 pAttach->i_updateMedium(pMedium);
4765 }
4766
4767 i_setModified(IsModified_Storage);
4768
4769 mediumLock.release();
4770 multiLock.release();
4771 rc = i_onMediumChange(pAttach, aForce);
4772 multiLock.acquire();
4773 mediumLock.acquire();
4774
4775 /* On error roll back this change only. */
4776 if (FAILED(rc))
4777 {
4778 if (!pMedium.isNull())
4779 pMedium->i_removeBackReference(mData->mUuid);
4780 pAttach = i_findAttachment(mMediaData->mAttachments,
4781 Bstr(aName).raw(),
4782 aControllerPort,
4783 aDevice);
4784 /* If the attachment is gone in the meantime, bail out. */
4785 if (pAttach.isNull())
4786 return rc;
4787 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4788 if (!oldmedium.isNull())
4789 oldmedium->i_addBackReference(mData->mUuid);
4790 pAttach->i_updateMedium(oldmedium);
4791 }
4792
4793 mediumLock.release();
4794 multiLock.release();
4795
4796 mParent->i_saveModifiedRegistries();
4797
4798 return rc;
4799}
4800HRESULT Machine::getMedium(const com::Utf8Str &aName,
4801 LONG aControllerPort,
4802 LONG aDevice,
4803 ComPtr<IMedium> &aMedium)
4804{
4805 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4806 aName.c_str(), aControllerPort, aDevice));
4807
4808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4809
4810 aMedium = NULL;
4811
4812 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4813 Bstr(aName).raw(),
4814 aControllerPort,
4815 aDevice);
4816 if (pAttach.isNull())
4817 return setError(VBOX_E_OBJECT_NOT_FOUND,
4818 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4819 aDevice, aControllerPort, aName.c_str());
4820
4821 aMedium = pAttach->i_getMedium();
4822
4823 return S_OK;
4824}
4825
4826HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4827{
4828
4829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4830
4831 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4832
4833 return S_OK;
4834}
4835
4836HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4837{
4838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4839
4840 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4841
4842 return S_OK;
4843}
4844
4845HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4846{
4847 /* Do not assert if slot is out of range, just return the advertised
4848 status. testdriver/vbox.py triggers this in logVmInfo. */
4849 if (aSlot >= mNetworkAdapters.size())
4850 return setError(E_INVALIDARG,
4851 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4852 aSlot, mNetworkAdapters.size());
4853
4854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4855
4856 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4857
4858 return S_OK;
4859}
4860
4861HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4862{
4863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4864
4865 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4866 size_t i = 0;
4867 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4868 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4869 ++it, ++i)
4870 aKeys[i] = it->first;
4871
4872 return S_OK;
4873}
4874
4875 /**
4876 * @note Locks this object for reading.
4877 */
4878HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4879 com::Utf8Str &aValue)
4880{
4881 /* start with nothing found */
4882 aValue = "";
4883
4884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4885
4886 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4887 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4888 // found:
4889 aValue = it->second; // source is a Utf8Str
4890
4891 /* return the result to caller (may be empty) */
4892 return S_OK;
4893}
4894
4895 /**
4896 * @note Locks mParent for writing + this object for writing.
4897 */
4898HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4899{
4900 Utf8Str strOldValue; // empty
4901
4902 // locking note: we only hold the read lock briefly to look up the old value,
4903 // then release it and call the onExtraCanChange callbacks. There is a small
4904 // chance of a race insofar as the callback might be called twice if two callers
4905 // change the same key at the same time, but that's a much better solution
4906 // than the deadlock we had here before. The actual changing of the extradata
4907 // is then performed under the write lock and race-free.
4908
4909 // look up the old value first; if nothing has changed then we need not do anything
4910 {
4911 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4912
4913 // For snapshots don't even think about allowing changes, extradata
4914 // is global for a machine, so there is nothing snapshot specific.
4915 if (i_isSnapshotMachine())
4916 return setError(VBOX_E_INVALID_VM_STATE,
4917 tr("Cannot set extradata for a snapshot"));
4918
4919 // check if the right IMachine instance is used
4920 if (!i_isSessionMachine())
4921 return setError(VBOX_E_INVALID_VM_STATE,
4922 tr("Cannot set extradata for an immutable machine"));
4923
4924 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4925 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4926 strOldValue = it->second;
4927 }
4928
4929 bool fChanged;
4930 if ((fChanged = (strOldValue != aValue)))
4931 {
4932 // ask for permission from all listeners outside the locks;
4933 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4934 // lock to copy the list of callbacks to invoke
4935 Bstr error;
4936 Bstr bstrValue(aValue);
4937
4938 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4939 {
4940 const char *sep = error.isEmpty() ? "" : ": ";
4941 CBSTR err = error.raw();
4942 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4943 sep, err));
4944 return setError(E_ACCESSDENIED,
4945 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4946 aKey.c_str(),
4947 aValue.c_str(),
4948 sep,
4949 err);
4950 }
4951
4952 // data is changing and change not vetoed: then write it out under the lock
4953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4954
4955 if (aValue.isEmpty())
4956 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4957 else
4958 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4959 // creates a new key if needed
4960
4961 bool fNeedsGlobalSaveSettings = false;
4962 // This saving of settings is tricky: there is no "old state" for the
4963 // extradata items at all (unlike all other settings), so the old/new
4964 // settings comparison would give a wrong result!
4965 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4966
4967 if (fNeedsGlobalSaveSettings)
4968 {
4969 // save the global settings; for that we should hold only the VirtualBox lock
4970 alock.release();
4971 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4972 mParent->i_saveSettings();
4973 }
4974 }
4975
4976 // fire notification outside the lock
4977 if (fChanged)
4978 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4979
4980 return S_OK;
4981}
4982
4983HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4984{
4985 aProgress = NULL;
4986 NOREF(aSettingsFilePath);
4987 ReturnComNotImplemented();
4988}
4989
4990HRESULT Machine::saveSettings()
4991{
4992 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4993
4994 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4995 if (FAILED(rc)) return rc;
4996
4997 /* the settings file path may never be null */
4998 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4999
5000 /* save all VM data excluding snapshots */
5001 bool fNeedsGlobalSaveSettings = false;
5002 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5003 mlock.release();
5004
5005 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5006 {
5007 // save the global settings; for that we should hold only the VirtualBox lock
5008 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5009 rc = mParent->i_saveSettings();
5010 }
5011
5012 return rc;
5013}
5014
5015
5016HRESULT Machine::discardSettings()
5017{
5018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5019
5020 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5021 if (FAILED(rc)) return rc;
5022
5023 /*
5024 * during this rollback, the session will be notified if data has
5025 * been actually changed
5026 */
5027 i_rollback(true /* aNotify */);
5028
5029 return S_OK;
5030}
5031
5032/** @note Locks objects! */
5033HRESULT Machine::unregister(CleanupMode_T aCleanupMode,
5034 std::vector<ComPtr<IMedium> > &aMedia)
5035{
5036 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5037 AutoLimitedCaller autoCaller(this);
5038 AssertComRCReturnRC(autoCaller.rc());
5039
5040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5041
5042 Guid id(i_getId());
5043
5044 if (mData->mSession.mState != SessionState_Unlocked)
5045 return setError(VBOX_E_INVALID_OBJECT_STATE,
5046 tr("Cannot unregister the machine '%s' while it is locked"),
5047 mUserData->s.strName.c_str());
5048
5049 // wait for state dependents to drop to zero
5050 i_ensureNoStateDependencies();
5051
5052 if (!mData->mAccessible)
5053 {
5054 // inaccessible maschines can only be unregistered; uninitialize ourselves
5055 // here because currently there may be no unregistered that are inaccessible
5056 // (this state combination is not supported). Note releasing the caller and
5057 // leaving the lock before calling uninit()
5058 alock.release();
5059 autoCaller.release();
5060
5061 uninit();
5062
5063 mParent->i_unregisterMachine(this, id);
5064 // calls VirtualBox::i_saveSettings()
5065
5066 return S_OK;
5067 }
5068
5069 HRESULT rc = S_OK;
5070
5071 // discard saved state
5072 if (mData->mMachineState == MachineState_Saved)
5073 {
5074 // add the saved state file to the list of files the caller should delete
5075 Assert(!mSSData->strStateFilePath.isEmpty());
5076 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5077
5078 mSSData->strStateFilePath.setNull();
5079
5080 // unconditionally set the machine state to powered off, we now
5081 // know no session has locked the machine
5082 mData->mMachineState = MachineState_PoweredOff;
5083 }
5084
5085 size_t cSnapshots = 0;
5086 if (mData->mFirstSnapshot)
5087 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5088 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5089 // fail now before we start detaching media
5090 return setError(VBOX_E_INVALID_OBJECT_STATE,
5091 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5092 mUserData->s.strName.c_str(), cSnapshots);
5093
5094 // This list collects the medium objects from all medium attachments
5095 // which we will detach from the machine and its snapshots, in a specific
5096 // order which allows for closing all media without getting "media in use"
5097 // errors, simply by going through the list from the front to the back:
5098 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5099 // and must be closed before the parent media from the snapshots, or closing the parents
5100 // will fail because they still have children);
5101 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5102 // the root ("first") snapshot of the machine.
5103 MediaList llMedia;
5104
5105 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5106 && mMediaData->mAttachments.size()
5107 )
5108 {
5109 // we have media attachments: detach them all and add the Medium objects to our list
5110 if (aCleanupMode != CleanupMode_UnregisterOnly)
5111 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5112 else
5113 return setError(VBOX_E_INVALID_OBJECT_STATE,
5114 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5115 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5116 }
5117
5118 if (cSnapshots)
5119 {
5120 // autoCleanup must be true here, or we would have failed above
5121
5122 // add the media from the medium attachments of the snapshots to llMedia
5123 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5124 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5125 // into the children first
5126
5127 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5128 MachineState_T oldState = mData->mMachineState;
5129 mData->mMachineState = MachineState_DeletingSnapshot;
5130
5131 // make a copy of the first snapshot so the refcount does not drop to 0
5132 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5133 // because of the AutoCaller voodoo)
5134 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5135
5136 // GO!
5137 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5138
5139 mData->mMachineState = oldState;
5140 }
5141
5142 if (FAILED(rc))
5143 {
5144 i_rollbackMedia();
5145 return rc;
5146 }
5147
5148 // commit all the media changes made above
5149 i_commitMedia();
5150
5151 mData->mRegistered = false;
5152
5153 // machine lock no longer needed
5154 alock.release();
5155
5156 // return media to caller
5157 size_t i = 0;
5158 aMedia.resize(llMedia.size());
5159 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5160 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5161
5162 mParent->i_unregisterMachine(this, id);
5163 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5164
5165 return S_OK;
5166}
5167
5168struct Machine::DeleteTask
5169{
5170 ComObjPtr<Machine> pMachine;
5171 RTCList<ComPtr<IMedium> > llMediums;
5172 StringsList llFilesToDelete;
5173 ComObjPtr<Progress> pProgress;
5174};
5175
5176HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5177{
5178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5179
5180 HRESULT rc = i_checkStateDependency(MutableStateDep);
5181 if (FAILED(rc)) return rc;
5182
5183 if (mData->mRegistered)
5184 return setError(VBOX_E_INVALID_VM_STATE,
5185 tr("Cannot delete settings of a registered machine"));
5186
5187 DeleteTask *pTask = new DeleteTask;
5188 pTask->pMachine = this;
5189
5190 // collect files to delete
5191 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5192
5193 for (size_t i = 0; i < aMedia.size(); ++i)
5194 {
5195 IMedium *pIMedium(aMedia[i]);
5196 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5197 if (pMedium.isNull())
5198 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5199 SafeArray<BSTR> ids;
5200 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5201 if (FAILED(rc)) return rc;
5202 /* At this point the medium should not have any back references
5203 * anymore. If it has it is attached to another VM and *must* not
5204 * deleted. */
5205 if (ids.size() < 1)
5206 pTask->llMediums.append(pMedium);
5207 }
5208 if (mData->pMachineConfigFile->fileExists())
5209 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5210
5211 pTask->pProgress.createObject();
5212 pTask->pProgress->init(i_getVirtualBox(),
5213 static_cast<IMachine*>(this) /* aInitiator */,
5214 Bstr(tr("Deleting files")).raw(),
5215 true /* fCancellable */,
5216 (ULONG)(pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1), // cOperations
5217 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5218
5219 int vrc = RTThreadCreate(NULL,
5220 Machine::deleteThread,
5221 (void*)pTask,
5222 0,
5223 RTTHREADTYPE_MAIN_WORKER,
5224 0,
5225 "MachineDelete");
5226
5227 pTask->pProgress.queryInterfaceTo(aProgress.asOutParam());
5228
5229 if (RT_FAILURE(vrc))
5230 {
5231 delete pTask;
5232 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5233 }
5234
5235 LogFlowFuncLeave();
5236
5237 return S_OK;
5238}
5239
5240/**
5241 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5242 * calls Machine::deleteTaskWorker() on the actual machine object.
5243 * @param Thread
5244 * @param pvUser
5245 * @return
5246 */
5247/*static*/
5248DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5249{
5250 LogFlowFuncEnter();
5251
5252 DeleteTask *pTask = (DeleteTask*)pvUser;
5253 Assert(pTask);
5254 Assert(pTask->pMachine);
5255 Assert(pTask->pProgress);
5256
5257 HRESULT rc = pTask->pMachine->i_deleteTaskWorker(*pTask);
5258 pTask->pProgress->i_notifyComplete(rc);
5259
5260 delete pTask;
5261
5262 LogFlowFuncLeave();
5263
5264 NOREF(Thread);
5265
5266 return VINF_SUCCESS;
5267}
5268
5269/**
5270 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5271 * @param task
5272 * @return
5273 */
5274HRESULT Machine::i_deleteTaskWorker(DeleteTask &task)
5275{
5276 AutoCaller autoCaller(this);
5277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5278
5279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5280
5281 HRESULT rc = S_OK;
5282
5283 try
5284 {
5285 ULONG uLogHistoryCount = 3;
5286 ComPtr<ISystemProperties> systemProperties;
5287 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5288 if (FAILED(rc)) throw rc;
5289
5290 if (!systemProperties.isNull())
5291 {
5292 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5293 if (FAILED(rc)) throw rc;
5294 }
5295
5296 MachineState_T oldState = mData->mMachineState;
5297 i_setMachineState(MachineState_SettingUp);
5298 alock.release();
5299 for (size_t i = 0; i < task.llMediums.size(); ++i)
5300 {
5301 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5302 {
5303 AutoCaller mac(pMedium);
5304 if (FAILED(mac.rc())) throw mac.rc();
5305 Utf8Str strLocation = pMedium->i_getLocationFull();
5306 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5307 if (FAILED(rc)) throw rc;
5308 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5309 }
5310 if (pMedium->i_isMediumFormatFile())
5311 {
5312 ComPtr<IProgress> pProgress2;
5313 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5314 if (FAILED(rc)) throw rc;
5315 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5316 if (FAILED(rc)) throw rc;
5317 /* Check the result of the asynchronous process. */
5318 LONG iRc;
5319 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5320 if (FAILED(rc)) throw rc;
5321 /* If the thread of the progress object has an error, then
5322 * retrieve the error info from there, or it'll be lost. */
5323 if (FAILED(iRc))
5324 throw setError(ProgressErrorInfo(pProgress2));
5325 }
5326
5327 /* Close the medium, deliberately without checking the return
5328 * code, and without leaving any trace in the error info, as
5329 * a failure here is a very minor issue, which shouldn't happen
5330 * as above we even managed to delete the medium. */
5331 {
5332 ErrorInfoKeeper eik;
5333 pMedium->Close();
5334 }
5335 }
5336 i_setMachineState(oldState);
5337 alock.acquire();
5338
5339 // delete the files pushed on the task list by Machine::Delete()
5340 // (this includes saved states of the machine and snapshots and
5341 // medium storage files from the IMedium list passed in, and the
5342 // machine XML file)
5343 StringsList::const_iterator it = task.llFilesToDelete.begin();
5344 while (it != task.llFilesToDelete.end())
5345 {
5346 const Utf8Str &strFile = *it;
5347 LogFunc(("Deleting file %s\n", strFile.c_str()));
5348 int vrc = RTFileDelete(strFile.c_str());
5349 if (RT_FAILURE(vrc))
5350 throw setError(VBOX_E_IPRT_ERROR,
5351 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5352
5353 ++it;
5354 if (it == task.llFilesToDelete.end())
5355 {
5356 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5357 if (FAILED(rc)) throw rc;
5358 break;
5359 }
5360
5361 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5362 if (FAILED(rc)) throw rc;
5363 }
5364
5365 /* delete the settings only when the file actually exists */
5366 if (mData->pMachineConfigFile->fileExists())
5367 {
5368 /* Delete any backup or uncommitted XML files. Ignore failures.
5369 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5370 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5371 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5372 RTFileDelete(otherXml.c_str());
5373 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5374 RTFileDelete(otherXml.c_str());
5375
5376 /* delete the Logs folder, nothing important should be left
5377 * there (we don't check for errors because the user might have
5378 * some private files there that we don't want to delete) */
5379 Utf8Str logFolder;
5380 getLogFolder(logFolder);
5381 Assert(logFolder.length());
5382 if (RTDirExists(logFolder.c_str()))
5383 {
5384 /* Delete all VBox.log[.N] files from the Logs folder
5385 * (this must be in sync with the rotation logic in
5386 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5387 * files that may have been created by the GUI. */
5388 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5389 logFolder.c_str(), RTPATH_DELIMITER);
5390 RTFileDelete(log.c_str());
5391 log = Utf8StrFmt("%s%cVBox.png",
5392 logFolder.c_str(), RTPATH_DELIMITER);
5393 RTFileDelete(log.c_str());
5394 for (int i = uLogHistoryCount; i > 0; i--)
5395 {
5396 log = Utf8StrFmt("%s%cVBox.log.%d",
5397 logFolder.c_str(), RTPATH_DELIMITER, i);
5398 RTFileDelete(log.c_str());
5399 log = Utf8StrFmt("%s%cVBox.png.%d",
5400 logFolder.c_str(), RTPATH_DELIMITER, i);
5401 RTFileDelete(log.c_str());
5402 }
5403#if defined(RT_OS_WINDOWS)
5404 log = Utf8StrFmt("%s%cVBoxStartup.log",
5405 logFolder.c_str(), RTPATH_DELIMITER);
5406 RTFileDelete(log.c_str());
5407#endif
5408
5409 RTDirRemove(logFolder.c_str());
5410 }
5411
5412 /* delete the Snapshots folder, nothing important should be left
5413 * there (we don't check for errors because the user might have
5414 * some private files there that we don't want to delete) */
5415 Utf8Str strFullSnapshotFolder;
5416 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5417 Assert(!strFullSnapshotFolder.isEmpty());
5418 if (RTDirExists(strFullSnapshotFolder.c_str()))
5419 RTDirRemove(strFullSnapshotFolder.c_str());
5420
5421 // delete the directory that contains the settings file, but only
5422 // if it matches the VM name
5423 Utf8Str settingsDir;
5424 if (i_isInOwnDir(&settingsDir))
5425 RTDirRemove(settingsDir.c_str());
5426 }
5427
5428 alock.release();
5429
5430 mParent->i_saveModifiedRegistries();
5431 }
5432 catch (HRESULT aRC) { rc = aRC; }
5433
5434 return rc;
5435}
5436
5437HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5438{
5439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5440
5441 ComObjPtr<Snapshot> pSnapshot;
5442 HRESULT rc;
5443
5444 if (aNameOrId.isEmpty())
5445 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5446 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5447 else
5448 {
5449 Guid uuid(aNameOrId);
5450 if (uuid.isValid())
5451 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5452 else
5453 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5454 }
5455 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5456
5457 return rc;
5458}
5459
5460HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5461{
5462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5463
5464 HRESULT rc = i_checkStateDependency(MutableStateDep);
5465 if (FAILED(rc)) return rc;
5466
5467 ComObjPtr<SharedFolder> sharedFolder;
5468 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5469 if (SUCCEEDED(rc))
5470 return setError(VBOX_E_OBJECT_IN_USE,
5471 tr("Shared folder named '%s' already exists"),
5472 aName.c_str());
5473
5474 sharedFolder.createObject();
5475 rc = sharedFolder->init(i_getMachine(),
5476 aName,
5477 aHostPath,
5478 !!aWritable,
5479 !!aAutomount,
5480 true /* fFailOnError */);
5481 if (FAILED(rc)) return rc;
5482
5483 i_setModified(IsModified_SharedFolders);
5484 mHWData.backup();
5485 mHWData->mSharedFolders.push_back(sharedFolder);
5486
5487 /* inform the direct session if any */
5488 alock.release();
5489 i_onSharedFolderChange();
5490
5491 return S_OK;
5492}
5493
5494HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5495{
5496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5497
5498 HRESULT rc = i_checkStateDependency(MutableStateDep);
5499 if (FAILED(rc)) return rc;
5500
5501 ComObjPtr<SharedFolder> sharedFolder;
5502 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5503 if (FAILED(rc)) return rc;
5504
5505 i_setModified(IsModified_SharedFolders);
5506 mHWData.backup();
5507 mHWData->mSharedFolders.remove(sharedFolder);
5508
5509 /* inform the direct session if any */
5510 alock.release();
5511 i_onSharedFolderChange();
5512
5513 return S_OK;
5514}
5515
5516HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5517{
5518 /* start with No */
5519 *aCanShow = FALSE;
5520
5521 ComPtr<IInternalSessionControl> directControl;
5522 {
5523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5524
5525 if (mData->mSession.mState != SessionState_Locked)
5526 return setError(VBOX_E_INVALID_VM_STATE,
5527 tr("Machine is not locked for session (session state: %s)"),
5528 Global::stringifySessionState(mData->mSession.mState));
5529
5530 directControl = mData->mSession.mDirectControl;
5531 }
5532
5533 /* ignore calls made after #OnSessionEnd() is called */
5534 if (!directControl)
5535 return S_OK;
5536
5537 LONG64 dummy;
5538 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5539}
5540
5541HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5542{
5543 ComPtr<IInternalSessionControl> directControl;
5544 {
5545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5546
5547 if (mData->mSession.mState != SessionState_Locked)
5548 return setError(E_FAIL,
5549 tr("Machine is not locked for session (session state: %s)"),
5550 Global::stringifySessionState(mData->mSession.mState));
5551
5552 directControl = mData->mSession.mDirectControl;
5553 }
5554
5555 /* ignore calls made after #OnSessionEnd() is called */
5556 if (!directControl)
5557 return S_OK;
5558
5559 BOOL dummy;
5560 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5561}
5562
5563#ifdef VBOX_WITH_GUEST_PROPS
5564/**
5565 * Look up a guest property in VBoxSVC's internal structures.
5566 */
5567HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5568 com::Utf8Str &aValue,
5569 LONG64 *aTimestamp,
5570 com::Utf8Str &aFlags) const
5571{
5572 using namespace guestProp;
5573
5574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5575 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5576
5577 if (it != mHWData->mGuestProperties.end())
5578 {
5579 char szFlags[MAX_FLAGS_LEN + 1];
5580 aValue = it->second.strValue;
5581 *aTimestamp = it->second.mTimestamp;
5582 writeFlags(it->second.mFlags, szFlags);
5583 aFlags = Utf8Str(szFlags);
5584 }
5585
5586 return S_OK;
5587}
5588
5589/**
5590 * Query the VM that a guest property belongs to for the property.
5591 * @returns E_ACCESSDENIED if the VM process is not available or not
5592 * currently handling queries and the lookup should then be done in
5593 * VBoxSVC.
5594 */
5595HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5596 com::Utf8Str &aValue,
5597 LONG64 *aTimestamp,
5598 com::Utf8Str &aFlags) const
5599{
5600 HRESULT rc = S_OK;
5601 BSTR bValue = NULL;
5602 BSTR bFlags = NULL;
5603
5604 ComPtr<IInternalSessionControl> directControl;
5605 directControl = mData->mSession.mDirectControl;
5606
5607 /* fail if we were called after #OnSessionEnd() is called. This is a
5608 * silly race condition. */
5609
5610 /** @todo This code is bothering API clients (like python script clients) with
5611 * the AccessGuestProperty call, creating unncessary IPC. Need to
5612 * have a way of figuring out which kind of direct session it is... */
5613 if (!directControl)
5614 rc = E_ACCESSDENIED;
5615 else
5616 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5617 0 /* accessMode */,
5618 &bValue, aTimestamp, &bFlags);
5619
5620 aValue = bValue;
5621 aFlags = bFlags;
5622
5623 return rc;
5624}
5625#endif // VBOX_WITH_GUEST_PROPS
5626
5627HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5628 com::Utf8Str &aValue,
5629 LONG64 *aTimestamp,
5630 com::Utf8Str &aFlags)
5631{
5632#ifndef VBOX_WITH_GUEST_PROPS
5633 ReturnComNotImplemented();
5634#else // VBOX_WITH_GUEST_PROPS
5635
5636 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5637
5638 if (rc == E_ACCESSDENIED)
5639 /* The VM is not running or the service is not (yet) accessible */
5640 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5641 return rc;
5642#endif // VBOX_WITH_GUEST_PROPS
5643}
5644
5645HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5646{
5647 LONG64 dummyTimestamp;
5648 com::Utf8Str dummyFlags;
5649 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5650 return rc;
5651
5652}
5653HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5654{
5655 com::Utf8Str dummyFlags;
5656 com::Utf8Str dummyValue;
5657 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5658 return rc;
5659}
5660
5661#ifdef VBOX_WITH_GUEST_PROPS
5662/**
5663 * Set a guest property in VBoxSVC's internal structures.
5664 */
5665HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5666 const com::Utf8Str &aFlags, bool fDelete)
5667{
5668 using namespace guestProp;
5669
5670 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5671 HRESULT rc = S_OK;
5672
5673 rc = i_checkStateDependency(MutableOrSavedStateDep);
5674 if (FAILED(rc)) return rc;
5675
5676 try
5677 {
5678 uint32_t fFlags = NILFLAG;
5679 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5680 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5681
5682 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5683 if (it == mHWData->mGuestProperties.end())
5684 {
5685 if (!fDelete)
5686 {
5687 i_setModified(IsModified_MachineData);
5688 mHWData.backupEx();
5689
5690 RTTIMESPEC time;
5691 HWData::GuestProperty prop;
5692 prop.strValue = Bstr(aValue).raw();
5693 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5694 prop.mFlags = fFlags;
5695 mHWData->mGuestProperties[aName] = prop;
5696 }
5697 }
5698 else
5699 {
5700 if (it->second.mFlags & (RDONLYHOST))
5701 {
5702 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5703 }
5704 else
5705 {
5706 i_setModified(IsModified_MachineData);
5707 mHWData.backupEx();
5708
5709 /* The backupEx() operation invalidates our iterator,
5710 * so get a new one. */
5711 it = mHWData->mGuestProperties.find(aName);
5712 Assert(it != mHWData->mGuestProperties.end());
5713
5714 if (!fDelete)
5715 {
5716 RTTIMESPEC time;
5717 it->second.strValue = aValue;
5718 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5719 it->second.mFlags = fFlags;
5720 }
5721 else
5722 mHWData->mGuestProperties.erase(it);
5723 }
5724 }
5725
5726 if ( SUCCEEDED(rc)
5727 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5728 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5729 RTSTR_MAX,
5730 aName.c_str(),
5731 RTSTR_MAX,
5732 NULL)
5733 )
5734 )
5735 {
5736 alock.release();
5737
5738 mParent->i_onGuestPropertyChange(mData->mUuid,
5739 Bstr(aName).raw(),
5740 Bstr(aValue).raw(),
5741 Bstr(aFlags).raw());
5742 }
5743 }
5744 catch (std::bad_alloc &)
5745 {
5746 rc = E_OUTOFMEMORY;
5747 }
5748
5749 return rc;
5750}
5751
5752/**
5753 * Set a property on the VM that that property belongs to.
5754 * @returns E_ACCESSDENIED if the VM process is not available or not
5755 * currently handling queries and the setting should then be done in
5756 * VBoxSVC.
5757 */
5758HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5759 const com::Utf8Str &aFlags, bool fDelete)
5760{
5761 HRESULT rc;
5762
5763 try
5764 {
5765 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5766
5767 BSTR dummy = NULL; /* will not be changed (setter) */
5768 LONG64 dummy64;
5769 if (!directControl)
5770 rc = E_ACCESSDENIED;
5771 else
5772 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5773 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5774 fDelete? 2: 1 /* accessMode */,
5775 &dummy, &dummy64, &dummy);
5776 }
5777 catch (std::bad_alloc &)
5778 {
5779 rc = E_OUTOFMEMORY;
5780 }
5781
5782 return rc;
5783}
5784#endif // VBOX_WITH_GUEST_PROPS
5785
5786HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5787 const com::Utf8Str &aFlags)
5788{
5789#ifndef VBOX_WITH_GUEST_PROPS
5790 ReturnComNotImplemented();
5791#else // VBOX_WITH_GUEST_PROPS
5792 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5793 if (rc == E_ACCESSDENIED)
5794 /* The VM is not running or the service is not (yet) accessible */
5795 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5796 return rc;
5797#endif // VBOX_WITH_GUEST_PROPS
5798}
5799
5800HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5801{
5802 return setGuestProperty(aProperty, aValue, "");
5803}
5804
5805HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5806{
5807#ifndef VBOX_WITH_GUEST_PROPS
5808 ReturnComNotImplemented();
5809#else // VBOX_WITH_GUEST_PROPS
5810 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5811 if (rc == E_ACCESSDENIED)
5812 /* The VM is not running or the service is not (yet) accessible */
5813 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5814 return rc;
5815#endif // VBOX_WITH_GUEST_PROPS
5816}
5817
5818#ifdef VBOX_WITH_GUEST_PROPS
5819/**
5820 * Enumerate the guest properties in VBoxSVC's internal structures.
5821 */
5822HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5823 std::vector<com::Utf8Str> &aNames,
5824 std::vector<com::Utf8Str> &aValues,
5825 std::vector<LONG64> &aTimestamps,
5826 std::vector<com::Utf8Str> &aFlags)
5827{
5828 using namespace guestProp;
5829
5830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5831 Utf8Str strPatterns(aPatterns);
5832
5833 HWData::GuestPropertyMap propMap;
5834
5835 /*
5836 * Look for matching patterns and build up a list.
5837 */
5838 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5839 while (it != mHWData->mGuestProperties.end())
5840 {
5841 if ( strPatterns.isEmpty()
5842 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5843 RTSTR_MAX,
5844 it->first.c_str(),
5845 RTSTR_MAX,
5846 NULL)
5847 )
5848 propMap.insert(*it);
5849 it++;
5850 }
5851
5852 alock.release();
5853
5854 /*
5855 * And build up the arrays for returning the property information.
5856 */
5857 size_t cEntries = propMap.size();
5858
5859 aNames.resize(cEntries);
5860 aValues.resize(cEntries);
5861 aTimestamps.resize(cEntries);
5862 aFlags.resize(cEntries);
5863
5864 char szFlags[MAX_FLAGS_LEN + 1];
5865 size_t i= 0;
5866 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5867 {
5868 aNames[i] = it->first;
5869 aValues[i] = it->second.strValue;
5870 aTimestamps[i] = it->second.mTimestamp;
5871 writeFlags(it->second.mFlags, szFlags);
5872 aFlags[i] = Utf8Str(szFlags);
5873 }
5874
5875 return S_OK;
5876}
5877
5878/**
5879 * Enumerate the properties managed by a VM.
5880 * @returns E_ACCESSDENIED if the VM process is not available or not
5881 * currently handling queries and the setting should then be done in
5882 * VBoxSVC.
5883 */
5884HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5885 std::vector<com::Utf8Str> &aNames,
5886 std::vector<com::Utf8Str> &aValues,
5887 std::vector<LONG64> &aTimestamps,
5888 std::vector<com::Utf8Str> &aFlags)
5889{
5890 HRESULT rc;
5891 ComPtr<IInternalSessionControl> directControl;
5892 directControl = mData->mSession.mDirectControl;
5893
5894
5895 com::SafeArray<BSTR> bNames;
5896 com::SafeArray<BSTR> bValues;
5897 com::SafeArray<LONG64> bTimestamps;
5898 com::SafeArray<BSTR> bFlags;
5899
5900 if (!directControl)
5901 rc = E_ACCESSDENIED;
5902 else
5903 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5904 ComSafeArrayAsOutParam(bNames),
5905 ComSafeArrayAsOutParam(bValues),
5906 ComSafeArrayAsOutParam(bTimestamps),
5907 ComSafeArrayAsOutParam(bFlags));
5908 size_t i;
5909 aNames.resize(bNames.size());
5910 for (i = 0; i < bNames.size(); ++i)
5911 aNames[i] = Utf8Str(bNames[i]);
5912 aValues.resize(bValues.size());
5913 for (i = 0; i < bValues.size(); ++i)
5914 aValues[i] = Utf8Str(bValues[i]);
5915 aTimestamps.resize(bTimestamps.size());
5916 for (i = 0; i < bTimestamps.size(); ++i)
5917 aTimestamps[i] = bTimestamps[i];
5918 aFlags.resize(bFlags.size());
5919 for (i = 0; i < bFlags.size(); ++i)
5920 aFlags[i] = Utf8Str(bFlags[i]);
5921
5922 return rc;
5923}
5924#endif // VBOX_WITH_GUEST_PROPS
5925HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5926 std::vector<com::Utf8Str> &aNames,
5927 std::vector<com::Utf8Str> &aValues,
5928 std::vector<LONG64> &aTimestamps,
5929 std::vector<com::Utf8Str> &aFlags)
5930{
5931#ifndef VBOX_WITH_GUEST_PROPS
5932 ReturnComNotImplemented();
5933#else // VBOX_WITH_GUEST_PROPS
5934
5935 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5936
5937 if (rc == E_ACCESSDENIED)
5938 /* The VM is not running or the service is not (yet) accessible */
5939 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5940 return rc;
5941#endif // VBOX_WITH_GUEST_PROPS
5942}
5943
5944HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5945 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5946{
5947 MediaData::AttachmentList atts;
5948
5949 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5950 if (FAILED(rc)) return rc;
5951
5952 size_t i = 0;
5953 aMediumAttachments.resize(atts.size());
5954 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5955 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5956
5957 return S_OK;
5958}
5959
5960HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5961 LONG aControllerPort,
5962 LONG aDevice,
5963 ComPtr<IMediumAttachment> &aAttachment)
5964{
5965 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5966 aName.c_str(), aControllerPort, aDevice));
5967
5968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5969
5970 aAttachment = NULL;
5971
5972 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5973 Bstr(aName).raw(),
5974 aControllerPort,
5975 aDevice);
5976 if (pAttach.isNull())
5977 return setError(VBOX_E_OBJECT_NOT_FOUND,
5978 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5979 aDevice, aControllerPort, aName.c_str());
5980
5981 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5982
5983 return S_OK;
5984}
5985
5986
5987HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5988 StorageBus_T aConnectionType,
5989 ComPtr<IStorageController> &aController)
5990{
5991 if ( (aConnectionType <= StorageBus_Null)
5992 || (aConnectionType > StorageBus_USB))
5993 return setError(E_INVALIDARG,
5994 tr("Invalid connection type: %d"),
5995 aConnectionType);
5996
5997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5998
5999 HRESULT rc = i_checkStateDependency(MutableStateDep);
6000 if (FAILED(rc)) return rc;
6001
6002 /* try to find one with the name first. */
6003 ComObjPtr<StorageController> ctrl;
6004
6005 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6006 if (SUCCEEDED(rc))
6007 return setError(VBOX_E_OBJECT_IN_USE,
6008 tr("Storage controller named '%s' already exists"),
6009 aName.c_str());
6010
6011 ctrl.createObject();
6012
6013 /* get a new instance number for the storage controller */
6014 ULONG ulInstance = 0;
6015 bool fBootable = true;
6016 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6017 it != mStorageControllers->end();
6018 ++it)
6019 {
6020 if ((*it)->i_getStorageBus() == aConnectionType)
6021 {
6022 ULONG ulCurInst = (*it)->i_getInstance();
6023
6024 if (ulCurInst >= ulInstance)
6025 ulInstance = ulCurInst + 1;
6026
6027 /* Only one controller of each type can be marked as bootable. */
6028 if ((*it)->i_getBootable())
6029 fBootable = false;
6030 }
6031 }
6032
6033 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6034 if (FAILED(rc)) return rc;
6035
6036 i_setModified(IsModified_Storage);
6037 mStorageControllers.backup();
6038 mStorageControllers->push_back(ctrl);
6039
6040 ctrl.queryInterfaceTo(aController.asOutParam());
6041
6042 /* inform the direct session if any */
6043 alock.release();
6044 i_onStorageControllerChange();
6045
6046 return S_OK;
6047}
6048
6049HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6050 ComPtr<IStorageController> &aStorageController)
6051{
6052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6053
6054 ComObjPtr<StorageController> ctrl;
6055
6056 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6057 if (SUCCEEDED(rc))
6058 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6059
6060 return rc;
6061}
6062
6063HRESULT Machine::getStorageControllerByInstance(ULONG aInstance,
6064 ComPtr<IStorageController> &aStorageController)
6065{
6066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6067
6068 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6069 it != mStorageControllers->end();
6070 ++it)
6071 {
6072 if ((*it)->i_getInstance() == aInstance)
6073 {
6074 (*it).queryInterfaceTo(aStorageController.asOutParam());
6075 return S_OK;
6076 }
6077 }
6078
6079 return setError(VBOX_E_OBJECT_NOT_FOUND,
6080 tr("Could not find a storage controller with instance number '%lu'"),
6081 aInstance);
6082}
6083
6084HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6085{
6086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6087
6088 HRESULT rc = i_checkStateDependency(MutableStateDep);
6089 if (FAILED(rc)) return rc;
6090
6091 ComObjPtr<StorageController> ctrl;
6092
6093 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6094 if (SUCCEEDED(rc))
6095 {
6096 /* Ensure that only one controller of each type is marked as bootable. */
6097 if (aBootable == TRUE)
6098 {
6099 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6100 it != mStorageControllers->end();
6101 ++it)
6102 {
6103 ComObjPtr<StorageController> aCtrl = (*it);
6104
6105 if ( (aCtrl->i_getName() != aName)
6106 && aCtrl->i_getBootable() == TRUE
6107 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6108 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6109 {
6110 aCtrl->i_setBootable(FALSE);
6111 break;
6112 }
6113 }
6114 }
6115
6116 if (SUCCEEDED(rc))
6117 {
6118 ctrl->i_setBootable(aBootable);
6119 i_setModified(IsModified_Storage);
6120 }
6121 }
6122
6123 if (SUCCEEDED(rc))
6124 {
6125 /* inform the direct session if any */
6126 alock.release();
6127 i_onStorageControllerChange();
6128 }
6129
6130 return rc;
6131}
6132
6133HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6134{
6135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6136
6137 HRESULT rc = i_checkStateDependency(MutableStateDep);
6138 if (FAILED(rc)) return rc;
6139
6140 ComObjPtr<StorageController> ctrl;
6141 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6142 if (FAILED(rc)) return rc;
6143
6144 {
6145 /* find all attached devices to the appropriate storage controller and detach them all */
6146 // make a temporary list because detachDevice invalidates iterators into
6147 // mMediaData->mAttachments
6148 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6149
6150 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6151 it != llAttachments2.end();
6152 ++it)
6153 {
6154 MediumAttachment *pAttachTemp = *it;
6155
6156 AutoCaller localAutoCaller(pAttachTemp);
6157 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6158
6159 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6160
6161 if (pAttachTemp->i_getControllerName() == aName)
6162 {
6163 rc = i_detachDevice(pAttachTemp, alock, NULL);
6164 if (FAILED(rc)) return rc;
6165 }
6166 }
6167 }
6168
6169 /* We can remove it now. */
6170 i_setModified(IsModified_Storage);
6171 mStorageControllers.backup();
6172
6173 ctrl->i_unshare();
6174
6175 mStorageControllers->remove(ctrl);
6176
6177 /* inform the direct session if any */
6178 alock.release();
6179 i_onStorageControllerChange();
6180
6181 return S_OK;
6182}
6183
6184HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6185 ComPtr<IUSBController> &aController)
6186{
6187 if ( (aType <= USBControllerType_Null)
6188 || (aType >= USBControllerType_Last))
6189 return setError(E_INVALIDARG,
6190 tr("Invalid USB controller type: %d"),
6191 aType);
6192
6193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6194
6195 HRESULT rc = i_checkStateDependency(MutableStateDep);
6196 if (FAILED(rc)) return rc;
6197
6198 /* try to find one with the same type first. */
6199 ComObjPtr<USBController> ctrl;
6200
6201 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6202 if (SUCCEEDED(rc))
6203 return setError(VBOX_E_OBJECT_IN_USE,
6204 tr("USB controller named '%s' already exists"),
6205 aName.c_str());
6206
6207 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6208 ULONG maxInstances;
6209 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6210 if (FAILED(rc))
6211 return rc;
6212
6213 ULONG cInstances = i_getUSBControllerCountByType(aType);
6214 if (cInstances >= maxInstances)
6215 return setError(E_INVALIDARG,
6216 tr("Too many USB controllers of this type"));
6217
6218 ctrl.createObject();
6219
6220 rc = ctrl->init(this, aName, aType);
6221 if (FAILED(rc)) return rc;
6222
6223 i_setModified(IsModified_USB);
6224 mUSBControllers.backup();
6225 mUSBControllers->push_back(ctrl);
6226
6227 ctrl.queryInterfaceTo(aController.asOutParam());
6228
6229 /* inform the direct session if any */
6230 alock.release();
6231 i_onUSBControllerChange();
6232
6233 return S_OK;
6234}
6235
6236HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6237{
6238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6239
6240 ComObjPtr<USBController> ctrl;
6241
6242 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6243 if (SUCCEEDED(rc))
6244 ctrl.queryInterfaceTo(aController.asOutParam());
6245
6246 return rc;
6247}
6248
6249HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6250 ULONG *aControllers)
6251{
6252 if ( (aType <= USBControllerType_Null)
6253 || (aType >= USBControllerType_Last))
6254 return setError(E_INVALIDARG,
6255 tr("Invalid USB controller type: %d"),
6256 aType);
6257
6258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6259
6260 ComObjPtr<USBController> ctrl;
6261
6262 *aControllers = i_getUSBControllerCountByType(aType);
6263
6264 return S_OK;
6265}
6266
6267HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6268{
6269
6270 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6271
6272 HRESULT rc = i_checkStateDependency(MutableStateDep);
6273 if (FAILED(rc)) return rc;
6274
6275 ComObjPtr<USBController> ctrl;
6276 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6277 if (FAILED(rc)) return rc;
6278
6279 i_setModified(IsModified_USB);
6280 mUSBControllers.backup();
6281
6282 ctrl->i_unshare();
6283
6284 mUSBControllers->remove(ctrl);
6285
6286 /* inform the direct session if any */
6287 alock.release();
6288 i_onUSBControllerChange();
6289
6290 return S_OK;
6291}
6292
6293HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6294 ULONG *aOriginX,
6295 ULONG *aOriginY,
6296 ULONG *aWidth,
6297 ULONG *aHeight,
6298 BOOL *aEnabled)
6299{
6300 uint32_t u32OriginX= 0;
6301 uint32_t u32OriginY= 0;
6302 uint32_t u32Width = 0;
6303 uint32_t u32Height = 0;
6304 uint16_t u16Flags = 0;
6305
6306 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6307 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6308 if (RT_FAILURE(vrc))
6309 {
6310#ifdef RT_OS_WINDOWS
6311 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6312 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6313 * So just assign fEnable to TRUE again.
6314 * The right fix would be to change GUI API wrappers to make sure that parameters
6315 * are changed only if API succeeds.
6316 */
6317 *aEnabled = TRUE;
6318#endif
6319 return setError(VBOX_E_IPRT_ERROR,
6320 tr("Saved guest size is not available (%Rrc)"),
6321 vrc);
6322 }
6323
6324 *aOriginX = u32OriginX;
6325 *aOriginY = u32OriginY;
6326 *aWidth = u32Width;
6327 *aHeight = u32Height;
6328 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6329
6330 return S_OK;
6331}
6332
6333HRESULT Machine::querySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6334{
6335 if (aScreenId != 0)
6336 return E_NOTIMPL;
6337
6338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6339
6340 uint8_t *pu8Data = NULL;
6341 uint32_t cbData = 0;
6342 uint32_t u32Width = 0;
6343 uint32_t u32Height = 0;
6344
6345 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6346
6347 if (RT_FAILURE(vrc))
6348 return setError(VBOX_E_IPRT_ERROR,
6349 tr("Saved screenshot data is not available (%Rrc)"),
6350 vrc);
6351
6352 *aSize = cbData;
6353 *aWidth = u32Width;
6354 *aHeight = u32Height;
6355
6356 freeSavedDisplayScreenshot(pu8Data);
6357
6358 return S_OK;
6359}
6360
6361HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6362 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6363{
6364 if (aScreenId != 0)
6365 return E_NOTIMPL;
6366
6367 if ( aBitmapFormat != BitmapFormat_BGR0
6368 && aBitmapFormat != BitmapFormat_BGRA
6369 && aBitmapFormat != BitmapFormat_RGBA
6370 && aBitmapFormat != BitmapFormat_PNG)
6371 return setError(E_NOTIMPL,
6372 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6373
6374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6375
6376 uint8_t *pu8Data = NULL;
6377 uint32_t cbData = 0;
6378 uint32_t u32Width = 0;
6379 uint32_t u32Height = 0;
6380
6381 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6382
6383 if (RT_FAILURE(vrc))
6384 return setError(VBOX_E_IPRT_ERROR,
6385 tr("Saved thumbnail data is not available (%Rrc)"),
6386 vrc);
6387
6388 HRESULT hr = S_OK;
6389
6390 *aWidth = u32Width;
6391 *aHeight = u32Height;
6392
6393 if (cbData > 0)
6394 {
6395 /* Convert pixels to the format expected by the API caller. */
6396 if (aBitmapFormat == BitmapFormat_BGR0)
6397 {
6398 /* [0] B, [1] G, [2] R, [3] 0. */
6399 aData.resize(cbData);
6400 memcpy(&aData.front(), pu8Data, cbData);
6401 }
6402 else if (aBitmapFormat == BitmapFormat_BGRA)
6403 {
6404 /* [0] B, [1] G, [2] R, [3] A. */
6405 aData.resize(cbData);
6406 for (uint32_t i = 0; i < cbData; i += 4)
6407 {
6408 aData[i] = pu8Data[i];
6409 aData[i + 1] = pu8Data[i + 1];
6410 aData[i + 2] = pu8Data[i + 2];
6411 aData[i + 3] = 0xff;
6412 }
6413 }
6414 else if (aBitmapFormat == BitmapFormat_RGBA)
6415 {
6416 /* [0] R, [1] G, [2] B, [3] A. */
6417 aData.resize(cbData);
6418 for (uint32_t i = 0; i < cbData; i += 4)
6419 {
6420 aData[i] = pu8Data[i + 2];
6421 aData[i + 1] = pu8Data[i + 1];
6422 aData[i + 2] = pu8Data[i];
6423 aData[i + 3] = 0xff;
6424 }
6425 }
6426 else if (aBitmapFormat == BitmapFormat_PNG)
6427 {
6428 uint8_t *pu8PNG = NULL;
6429 uint32_t cbPNG = 0;
6430 uint32_t cxPNG = 0;
6431 uint32_t cyPNG = 0;
6432
6433 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6434
6435 if (RT_SUCCESS(vrc))
6436 {
6437 aData.resize(cbPNG);
6438 if (cbPNG)
6439 memcpy(&aData.front(), pu8PNG, cbPNG);
6440 }
6441 else
6442 hr = setError(VBOX_E_IPRT_ERROR,
6443 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6444 vrc);
6445
6446 RTMemFree(pu8PNG);
6447 }
6448 }
6449
6450 freeSavedDisplayScreenshot(pu8Data);
6451
6452 return hr;
6453}
6454
6455HRESULT Machine::querySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6456{
6457 if (aScreenId != 0)
6458 return E_NOTIMPL;
6459
6460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6461
6462 uint8_t *pu8Data = NULL;
6463 uint32_t cbData = 0;
6464 uint32_t u32Width = 0;
6465 uint32_t u32Height = 0;
6466
6467 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6468
6469 if (RT_FAILURE(vrc))
6470 return setError(VBOX_E_IPRT_ERROR,
6471 tr("Saved screenshot data is not available (%Rrc)"),
6472 vrc);
6473
6474 *aSize = cbData;
6475 *aWidth = u32Width;
6476 *aHeight = u32Height;
6477
6478 freeSavedDisplayScreenshot(pu8Data);
6479
6480 return S_OK;
6481}
6482
6483HRESULT Machine::readSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6484{
6485 if (aScreenId != 0)
6486 return E_NOTIMPL;
6487
6488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6489
6490 uint8_t *pu8Data = NULL;
6491 uint32_t cbData = 0;
6492 uint32_t u32Width = 0;
6493 uint32_t u32Height = 0;
6494
6495 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6496
6497 if (RT_FAILURE(vrc))
6498 return setError(VBOX_E_IPRT_ERROR,
6499 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6500 vrc);
6501
6502 *aWidth = u32Width;
6503 *aHeight = u32Height;
6504
6505 aData.resize(cbData);
6506 if (cbData)
6507 memcpy(&aData.front(), pu8Data, cbData);
6508
6509 freeSavedDisplayScreenshot(pu8Data);
6510
6511 return S_OK;
6512}
6513
6514HRESULT Machine::hotPlugCPU(ULONG aCpu)
6515{
6516 HRESULT rc = S_OK;
6517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6518
6519 if (!mHWData->mCPUHotPlugEnabled)
6520 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6521
6522 if (aCpu >= mHWData->mCPUCount)
6523 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6524
6525 if (mHWData->mCPUAttached[aCpu])
6526 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6527
6528 alock.release();
6529 rc = i_onCPUChange(aCpu, false);
6530 alock.acquire();
6531 if (FAILED(rc)) return rc;
6532
6533 i_setModified(IsModified_MachineData);
6534 mHWData.backup();
6535 mHWData->mCPUAttached[aCpu] = true;
6536
6537 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6538 if (Global::IsOnline(mData->mMachineState))
6539 i_saveSettings(NULL);
6540
6541 return S_OK;
6542}
6543
6544HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6545{
6546 HRESULT rc = S_OK;
6547
6548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6549
6550 if (!mHWData->mCPUHotPlugEnabled)
6551 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6552
6553 if (aCpu >= SchemaDefs::MaxCPUCount)
6554 return setError(E_INVALIDARG,
6555 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6556 SchemaDefs::MaxCPUCount);
6557
6558 if (!mHWData->mCPUAttached[aCpu])
6559 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6560
6561 /* CPU 0 can't be detached */
6562 if (aCpu == 0)
6563 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6564
6565 alock.release();
6566 rc = i_onCPUChange(aCpu, true);
6567 alock.acquire();
6568 if (FAILED(rc)) return rc;
6569
6570 i_setModified(IsModified_MachineData);
6571 mHWData.backup();
6572 mHWData->mCPUAttached[aCpu] = false;
6573
6574 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6575 if (Global::IsOnline(mData->mMachineState))
6576 i_saveSettings(NULL);
6577
6578 return S_OK;
6579}
6580
6581HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6582{
6583 *aAttached = false;
6584
6585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6586
6587 /* If hotplug is enabled the CPU is always enabled. */
6588 if (!mHWData->mCPUHotPlugEnabled)
6589 {
6590 if (aCpu < mHWData->mCPUCount)
6591 *aAttached = true;
6592 }
6593 else
6594 {
6595 if (aCpu < SchemaDefs::MaxCPUCount)
6596 *aAttached = mHWData->mCPUAttached[aCpu];
6597 }
6598
6599 return S_OK;
6600}
6601
6602HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6603{
6604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6605
6606 Utf8Str log = i_queryLogFilename(aIdx);
6607 if (!RTFileExists(log.c_str()))
6608 log.setNull();
6609 aFilename = log;
6610
6611 return S_OK;
6612}
6613
6614HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6615{
6616 if (aSize < 0)
6617 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6618
6619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6620
6621 HRESULT rc = S_OK;
6622 Utf8Str log = i_queryLogFilename(aIdx);
6623
6624 /* do not unnecessarily hold the lock while doing something which does
6625 * not need the lock and potentially takes a long time. */
6626 alock.release();
6627
6628 /* Limit the chunk size to 32K for now, as that gives better performance
6629 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6630 * One byte expands to approx. 25 bytes of breathtaking XML. */
6631 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6632 aData.resize(cbData);
6633
6634 RTFILE LogFile;
6635 int vrc = RTFileOpen(&LogFile, log.c_str(),
6636 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6637 if (RT_SUCCESS(vrc))
6638 {
6639 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6640 if (RT_SUCCESS(vrc))
6641 aData.resize(cbData);
6642 else
6643 rc = setError(VBOX_E_IPRT_ERROR,
6644 tr("Could not read log file '%s' (%Rrc)"),
6645 log.c_str(), vrc);
6646 RTFileClose(LogFile);
6647 }
6648 else
6649 rc = setError(VBOX_E_IPRT_ERROR,
6650 tr("Could not open log file '%s' (%Rrc)"),
6651 log.c_str(), vrc);
6652
6653 if (FAILED(rc))
6654 aData.resize(0);
6655
6656 return rc;
6657}
6658
6659
6660/**
6661 * Currently this method doesn't attach device to the running VM,
6662 * just makes sure it's plugged on next VM start.
6663 */
6664HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6665{
6666 // lock scope
6667 {
6668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6669
6670 HRESULT rc = i_checkStateDependency(MutableStateDep);
6671 if (FAILED(rc)) return rc;
6672
6673 ChipsetType_T aChipset = ChipsetType_PIIX3;
6674 COMGETTER(ChipsetType)(&aChipset);
6675
6676 if (aChipset != ChipsetType_ICH9)
6677 {
6678 return setError(E_INVALIDARG,
6679 tr("Host PCI attachment only supported with ICH9 chipset"));
6680 }
6681
6682 // check if device with this host PCI address already attached
6683 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6684 it != mHWData->mPCIDeviceAssignments.end();
6685 ++it)
6686 {
6687 LONG iHostAddress = -1;
6688 ComPtr<PCIDeviceAttachment> pAttach;
6689 pAttach = *it;
6690 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6691 if (iHostAddress == aHostAddress)
6692 return setError(E_INVALIDARG,
6693 tr("Device with host PCI address already attached to this VM"));
6694 }
6695
6696 ComObjPtr<PCIDeviceAttachment> pda;
6697 char name[32];
6698
6699 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6700 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6701 Bstr bname(name);
6702 pda.createObject();
6703 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6704 i_setModified(IsModified_MachineData);
6705 mHWData.backup();
6706 mHWData->mPCIDeviceAssignments.push_back(pda);
6707 }
6708
6709 return S_OK;
6710}
6711
6712/**
6713 * Currently this method doesn't detach device from the running VM,
6714 * just makes sure it's not plugged on next VM start.
6715 */
6716HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6717{
6718 ComObjPtr<PCIDeviceAttachment> pAttach;
6719 bool fRemoved = false;
6720 HRESULT rc;
6721
6722 // lock scope
6723 {
6724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6725
6726 rc = i_checkStateDependency(MutableStateDep);
6727 if (FAILED(rc)) return rc;
6728
6729 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6730 it != mHWData->mPCIDeviceAssignments.end();
6731 ++it)
6732 {
6733 LONG iHostAddress = -1;
6734 pAttach = *it;
6735 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6736 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6737 {
6738 i_setModified(IsModified_MachineData);
6739 mHWData.backup();
6740 mHWData->mPCIDeviceAssignments.remove(pAttach);
6741 fRemoved = true;
6742 break;
6743 }
6744 }
6745 }
6746
6747
6748 /* Fire event outside of the lock */
6749 if (fRemoved)
6750 {
6751 Assert(!pAttach.isNull());
6752 ComPtr<IEventSource> es;
6753 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6754 Assert(SUCCEEDED(rc));
6755 Bstr mid;
6756 rc = this->COMGETTER(Id)(mid.asOutParam());
6757 Assert(SUCCEEDED(rc));
6758 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6759 }
6760
6761 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6762 tr("No host PCI device %08x attached"),
6763 aHostAddress
6764 );
6765}
6766
6767HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6768{
6769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6770
6771 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6772
6773 size_t i = 0;
6774 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6775 it != mHWData->mPCIDeviceAssignments.end();
6776 ++i, ++it)
6777 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6778
6779 return S_OK;
6780}
6781
6782HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6783{
6784 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6785
6786 return S_OK;
6787}
6788
6789HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6790{
6791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6792
6793 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6794
6795 return S_OK;
6796}
6797
6798HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6799{
6800 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6801 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6802 if (SUCCEEDED(hrc))
6803 {
6804 hrc = mHWData.backupEx();
6805 if (SUCCEEDED(hrc))
6806 {
6807 i_setModified(IsModified_MachineData);
6808 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6809 }
6810 }
6811 return hrc;
6812}
6813
6814HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6815{
6816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6817 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6818 return S_OK;
6819}
6820
6821HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6822{
6823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6824 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6825 if (SUCCEEDED(hrc))
6826 {
6827 hrc = mHWData.backupEx();
6828 if (SUCCEEDED(hrc))
6829 {
6830 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6831 if (SUCCEEDED(hrc))
6832 i_setModified(IsModified_MachineData);
6833 }
6834 }
6835 return hrc;
6836}
6837
6838HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6839{
6840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6841
6842 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6843
6844 return S_OK;
6845}
6846
6847HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6848{
6849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6850 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6851 if (SUCCEEDED(hrc))
6852 {
6853 hrc = mHWData.backupEx();
6854 if (SUCCEEDED(hrc))
6855 {
6856 i_setModified(IsModified_MachineData);
6857 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6858 }
6859 }
6860 return hrc;
6861}
6862
6863HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6864{
6865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6866
6867 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6868
6869 return S_OK;
6870}
6871
6872HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6873{
6874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6875
6876 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6877 if ( SUCCEEDED(hrc)
6878 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6879 {
6880 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6881 int vrc;
6882
6883 if (aAutostartEnabled)
6884 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6885 else
6886 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6887
6888 if (RT_SUCCESS(vrc))
6889 {
6890 hrc = mHWData.backupEx();
6891 if (SUCCEEDED(hrc))
6892 {
6893 i_setModified(IsModified_MachineData);
6894 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6895 }
6896 }
6897 else if (vrc == VERR_NOT_SUPPORTED)
6898 hrc = setError(VBOX_E_NOT_SUPPORTED,
6899 tr("The VM autostart feature is not supported on this platform"));
6900 else if (vrc == VERR_PATH_NOT_FOUND)
6901 hrc = setError(E_FAIL,
6902 tr("The path to the autostart database is not set"));
6903 else
6904 hrc = setError(E_UNEXPECTED,
6905 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6906 aAutostartEnabled ? "Adding" : "Removing",
6907 mUserData->s.strName.c_str(), vrc);
6908 }
6909 return hrc;
6910}
6911
6912HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6913{
6914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6915
6916 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6917
6918 return S_OK;
6919}
6920
6921HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6922{
6923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6924 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6925 if (SUCCEEDED(hrc))
6926 {
6927 hrc = mHWData.backupEx();
6928 if (SUCCEEDED(hrc))
6929 {
6930 i_setModified(IsModified_MachineData);
6931 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6932 }
6933 }
6934 return hrc;
6935}
6936
6937HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6938{
6939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6940
6941 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6942
6943 return S_OK;
6944}
6945
6946HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6947{
6948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6949 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6950 if ( SUCCEEDED(hrc)
6951 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6952 {
6953 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6954 int vrc;
6955
6956 if (aAutostopType != AutostopType_Disabled)
6957 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6958 else
6959 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6960
6961 if (RT_SUCCESS(vrc))
6962 {
6963 hrc = mHWData.backupEx();
6964 if (SUCCEEDED(hrc))
6965 {
6966 i_setModified(IsModified_MachineData);
6967 mHWData->mAutostart.enmAutostopType = aAutostopType;
6968 }
6969 }
6970 else if (vrc == VERR_NOT_SUPPORTED)
6971 hrc = setError(VBOX_E_NOT_SUPPORTED,
6972 tr("The VM autostop feature is not supported on this platform"));
6973 else if (vrc == VERR_PATH_NOT_FOUND)
6974 hrc = setError(E_FAIL,
6975 tr("The path to the autostart database is not set"));
6976 else
6977 hrc = setError(E_UNEXPECTED,
6978 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6979 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6980 mUserData->s.strName.c_str(), vrc);
6981 }
6982 return hrc;
6983}
6984
6985HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6986{
6987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6988
6989 aDefaultFrontend = mHWData->mDefaultFrontend;
6990
6991 return S_OK;
6992}
6993
6994HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6995{
6996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6997 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6998 if (SUCCEEDED(hrc))
6999 {
7000 hrc = mHWData.backupEx();
7001 if (SUCCEEDED(hrc))
7002 {
7003 i_setModified(IsModified_MachineData);
7004 mHWData->mDefaultFrontend = aDefaultFrontend;
7005 }
7006 }
7007 return hrc;
7008}
7009
7010HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7011{
7012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7013 size_t cbIcon = mUserData->mIcon.size();
7014 aIcon.resize(cbIcon);
7015 if (cbIcon)
7016 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7017 return S_OK;
7018}
7019
7020HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7021{
7022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7023 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7024 if (SUCCEEDED(hrc))
7025 {
7026 i_setModified(IsModified_MachineData);
7027 mUserData.backup();
7028 size_t cbIcon = aIcon.size();
7029 mUserData->mIcon.resize(cbIcon);
7030 if (cbIcon)
7031 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7032 }
7033 return hrc;
7034}
7035
7036HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7037{
7038#ifdef VBOX_WITH_USB
7039 *aUSBProxyAvailable = true;
7040#else
7041 *aUSBProxyAvailable = false;
7042#endif
7043 return S_OK;
7044}
7045
7046HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7047 ComPtr<IProgress> &aProgress)
7048{
7049 ComObjPtr<Progress> pP;
7050 Progress *ppP = pP;
7051 IProgress *iP = static_cast<IProgress *>(ppP);
7052 IProgress **pProgress = &iP;
7053
7054 IMachine *pTarget = aTarget;
7055
7056 /* Convert the options. */
7057 RTCList<CloneOptions_T> optList;
7058 if (aOptions.size())
7059 for (size_t i = 0; i < aOptions.size(); ++i)
7060 optList.append(aOptions[i]);
7061
7062 if (optList.contains(CloneOptions_Link))
7063 {
7064 if (!i_isSnapshotMachine())
7065 return setError(E_INVALIDARG,
7066 tr("Linked clone can only be created from a snapshot"));
7067 if (aMode != CloneMode_MachineState)
7068 return setError(E_INVALIDARG,
7069 tr("Linked clone can only be created for a single machine state"));
7070 }
7071 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7072
7073 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7074
7075 HRESULT rc = pWorker->start(pProgress);
7076
7077 pP = static_cast<Progress *>(*pProgress);
7078 pP.queryInterfaceTo(aProgress.asOutParam());
7079
7080 return rc;
7081
7082}
7083
7084// public methods for internal purposes
7085/////////////////////////////////////////////////////////////////////////////
7086
7087/**
7088 * Adds the given IsModified_* flag to the dirty flags of the machine.
7089 * This must be called either during i_loadSettings or under the machine write lock.
7090 * @param fl
7091 */
7092void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7093{
7094 mData->flModifications |= fl;
7095 if (fAllowStateModification && i_isStateModificationAllowed())
7096 mData->mCurrentStateModified = true;
7097}
7098
7099/**
7100 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7101 * care of the write locking.
7102 *
7103 * @param fModifications The flag to add.
7104 */
7105void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7106{
7107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7108 i_setModified(fModification, fAllowStateModification);
7109}
7110
7111/**
7112 * Saves the registry entry of this machine to the given configuration node.
7113 *
7114 * @param aEntryNode Node to save the registry entry to.
7115 *
7116 * @note locks this object for reading.
7117 */
7118HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7119{
7120 AutoLimitedCaller autoCaller(this);
7121 AssertComRCReturnRC(autoCaller.rc());
7122
7123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7124
7125 data.uuid = mData->mUuid;
7126 data.strSettingsFile = mData->m_strConfigFile;
7127
7128 return S_OK;
7129}
7130
7131/**
7132 * Calculates the absolute path of the given path taking the directory of the
7133 * machine settings file as the current directory.
7134 *
7135 * @param aPath Path to calculate the absolute path for.
7136 * @param aResult Where to put the result (used only on success, can be the
7137 * same Utf8Str instance as passed in @a aPath).
7138 * @return IPRT result.
7139 *
7140 * @note Locks this object for reading.
7141 */
7142int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7143{
7144 AutoCaller autoCaller(this);
7145 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7146
7147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7148
7149 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7150
7151 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7152
7153 strSettingsDir.stripFilename();
7154 char folder[RTPATH_MAX];
7155 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7156 if (RT_SUCCESS(vrc))
7157 aResult = folder;
7158
7159 return vrc;
7160}
7161
7162/**
7163 * Copies strSource to strTarget, making it relative to the machine folder
7164 * if it is a subdirectory thereof, or simply copying it otherwise.
7165 *
7166 * @param strSource Path to evaluate and copy.
7167 * @param strTarget Buffer to receive target path.
7168 *
7169 * @note Locks this object for reading.
7170 */
7171void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7172 Utf8Str &strTarget)
7173{
7174 AutoCaller autoCaller(this);
7175 AssertComRCReturn(autoCaller.rc(), (void)0);
7176
7177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7178
7179 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7180 // use strTarget as a temporary buffer to hold the machine settings dir
7181 strTarget = mData->m_strConfigFileFull;
7182 strTarget.stripFilename();
7183 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7184 {
7185 // is relative: then append what's left
7186 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7187 // for empty paths (only possible for subdirs) use "." to avoid
7188 // triggering default settings for not present config attributes.
7189 if (strTarget.isEmpty())
7190 strTarget = ".";
7191 }
7192 else
7193 // is not relative: then overwrite
7194 strTarget = strSource;
7195}
7196
7197/**
7198 * Returns the full path to the machine's log folder in the
7199 * \a aLogFolder argument.
7200 */
7201void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7202{
7203 AutoCaller autoCaller(this);
7204 AssertComRCReturnVoid(autoCaller.rc());
7205
7206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7207
7208 char szTmp[RTPATH_MAX];
7209 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7210 if (RT_SUCCESS(vrc))
7211 {
7212 if (szTmp[0] && !mUserData.isNull())
7213 {
7214 char szTmp2[RTPATH_MAX];
7215 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7216 if (RT_SUCCESS(vrc))
7217 aLogFolder = BstrFmt("%s%c%s",
7218 szTmp2,
7219 RTPATH_DELIMITER,
7220 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7221 }
7222 else
7223 vrc = VERR_PATH_IS_RELATIVE;
7224 }
7225
7226 if (RT_FAILURE(vrc))
7227 {
7228 // fallback if VBOX_USER_LOGHOME is not set or invalid
7229 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7230 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7231 aLogFolder.append(RTPATH_DELIMITER);
7232 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7233 }
7234}
7235
7236/**
7237 * Returns the full path to the machine's log file for an given index.
7238 */
7239Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7240 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7241{
7242 Utf8Str logFolder;
7243 getLogFolder(logFolder);
7244 Assert(logFolder.length());
7245 Utf8Str log;
7246 if (idx == 0)
7247 log = Utf8StrFmt("%s%cVBox.log",
7248 logFolder.c_str(), RTPATH_DELIMITER);
7249 else
7250 log = Utf8StrFmt("%s%cVBox.log.%d",
7251 logFolder.c_str(), RTPATH_DELIMITER, idx);
7252 return log;
7253}
7254
7255/**
7256 * Returns the full path to the machine's (hardened) startup log file.
7257 */
7258Utf8Str Machine::i_getStartupLogFilename(void)
7259{
7260 Utf8Str strFilename;
7261 getLogFolder(strFilename);
7262 Assert(strFilename.length());
7263 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7264 return strFilename;
7265}
7266
7267
7268/**
7269 * Composes a unique saved state filename based on the current system time. The filename is
7270 * granular to the second so this will work so long as no more than one snapshot is taken on
7271 * a machine per second.
7272 *
7273 * Before version 4.1, we used this formula for saved state files:
7274 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7275 * which no longer works because saved state files can now be shared between the saved state of the
7276 * "saved" machine and an online snapshot, and the following would cause problems:
7277 * 1) save machine
7278 * 2) create online snapshot from that machine state --> reusing saved state file
7279 * 3) save machine again --> filename would be reused, breaking the online snapshot
7280 *
7281 * So instead we now use a timestamp.
7282 *
7283 * @param str
7284 */
7285
7286void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7287{
7288 AutoCaller autoCaller(this);
7289 AssertComRCReturnVoid(autoCaller.rc());
7290
7291 {
7292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7293 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7294 }
7295
7296 RTTIMESPEC ts;
7297 RTTimeNow(&ts);
7298 RTTIME time;
7299 RTTimeExplode(&time, &ts);
7300
7301 strStateFilePath += RTPATH_DELIMITER;
7302 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7303 time.i32Year, time.u8Month, time.u8MonthDay,
7304 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7305}
7306
7307/**
7308 * Returns the full path to the default video capture file.
7309 */
7310void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7311{
7312 AutoCaller autoCaller(this);
7313 AssertComRCReturnVoid(autoCaller.rc());
7314
7315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7316
7317 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7318 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7319 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7320}
7321
7322/**
7323 * Returns whether at least one USB controller is present for the VM.
7324 */
7325bool Machine::i_isUSBControllerPresent()
7326{
7327 AutoCaller autoCaller(this);
7328 AssertComRCReturn(autoCaller.rc(), false);
7329
7330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7331
7332 return (mUSBControllers->size() > 0);
7333}
7334
7335/**
7336 * @note Locks this object for writing, calls the client process
7337 * (inside the lock).
7338 */
7339HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7340 const Utf8Str &strFrontend,
7341 const Utf8Str &strEnvironment,
7342 ProgressProxy *aProgress)
7343{
7344 LogFlowThisFuncEnter();
7345
7346 AssertReturn(aControl, E_FAIL);
7347 AssertReturn(aProgress, E_FAIL);
7348 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7349
7350 AutoCaller autoCaller(this);
7351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7352
7353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7354
7355 if (!mData->mRegistered)
7356 return setError(E_UNEXPECTED,
7357 tr("The machine '%s' is not registered"),
7358 mUserData->s.strName.c_str());
7359
7360 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7361
7362 if ( mData->mSession.mState == SessionState_Locked
7363 || mData->mSession.mState == SessionState_Spawning
7364 || mData->mSession.mState == SessionState_Unlocking)
7365 return setError(VBOX_E_INVALID_OBJECT_STATE,
7366 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7367 mUserData->s.strName.c_str());
7368
7369 /* may not be busy */
7370 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7371
7372 /* get the path to the executable */
7373 char szPath[RTPATH_MAX];
7374 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7375 size_t cchBufLeft = strlen(szPath);
7376 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7377 szPath[cchBufLeft] = 0;
7378 char *pszNamePart = szPath + cchBufLeft;
7379 cchBufLeft = sizeof(szPath) - cchBufLeft;
7380
7381 int vrc = VINF_SUCCESS;
7382 RTPROCESS pid = NIL_RTPROCESS;
7383
7384 RTENV env = RTENV_DEFAULT;
7385
7386 if (!strEnvironment.isEmpty())
7387 {
7388 char *newEnvStr = NULL;
7389
7390 do
7391 {
7392 /* clone the current environment */
7393 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7394 AssertRCBreakStmt(vrc2, vrc = vrc2);
7395
7396 newEnvStr = RTStrDup(strEnvironment.c_str());
7397 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7398
7399 /* put new variables to the environment
7400 * (ignore empty variable names here since RTEnv API
7401 * intentionally doesn't do that) */
7402 char *var = newEnvStr;
7403 for (char *p = newEnvStr; *p; ++p)
7404 {
7405 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7406 {
7407 *p = '\0';
7408 if (*var)
7409 {
7410 char *val = strchr(var, '=');
7411 if (val)
7412 {
7413 *val++ = '\0';
7414 vrc2 = RTEnvSetEx(env, var, val);
7415 }
7416 else
7417 vrc2 = RTEnvUnsetEx(env, var);
7418 if (RT_FAILURE(vrc2))
7419 break;
7420 }
7421 var = p + 1;
7422 }
7423 }
7424 if (RT_SUCCESS(vrc2) && *var)
7425 vrc2 = RTEnvPutEx(env, var);
7426
7427 AssertRCBreakStmt(vrc2, vrc = vrc2);
7428 }
7429 while (0);
7430
7431 if (newEnvStr != NULL)
7432 RTStrFree(newEnvStr);
7433 }
7434
7435 /* Hardened startup logging */
7436#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7437 Utf8Str strSupStartLogArg("--sup-startup-log=");
7438 {
7439 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7440 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7441 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7442 {
7443 Utf8Str strStartupLogDir = strStartupLogFile;
7444 strStartupLogDir.stripFilename();
7445 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7446 file without stripping the file. */
7447 }
7448 strSupStartLogArg.append(strStartupLogFile);
7449 }
7450 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7451#else
7452 const char *pszSupStartupLogArg = NULL;
7453#endif
7454
7455
7456#ifdef VBOX_WITH_QTGUI
7457 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7458 {
7459# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7460 /* Modify the base path so that we don't need to use ".." below. */
7461 RTPathStripTrailingSlash(szPath);
7462 RTPathStripFilename(szPath);
7463 cchBufLeft = strlen(szPath);
7464 pszNamePart = szPath + cchBufLeft;
7465 cchBufLeft = sizeof(szPath) - cchBufLeft;
7466
7467# define OSX_APP_NAME "VirtualBoxVM"
7468# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7469
7470 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7471 if ( strAppOverride.contains(".")
7472 || strAppOverride.contains("/")
7473 || strAppOverride.contains("\\")
7474 || strAppOverride.contains(":"))
7475 strAppOverride.setNull();
7476 Utf8Str strAppPath;
7477 if (!strAppOverride.isEmpty())
7478 {
7479 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7480 Utf8Str strFullPath(szPath);
7481 strFullPath.append(strAppPath);
7482 /* there is a race, but people using this deserve the failure */
7483 if (!RTFileExists(strFullPath.c_str()))
7484 strAppOverride.setNull();
7485 }
7486 if (strAppOverride.isEmpty())
7487 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7488 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7489 strcpy(pszNamePart, strAppPath.c_str());
7490# else
7491 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7492 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7493 strcpy(pszNamePart, s_szVirtualBox_exe);
7494# endif
7495
7496 Utf8Str idStr = mData->mUuid.toString();
7497 const char *apszArgs[] =
7498 {
7499 szPath,
7500 "--comment", mUserData->s.strName.c_str(),
7501 "--startvm", idStr.c_str(),
7502 "--no-startvm-errormsgbox",
7503 pszSupStartupLogArg,
7504 NULL
7505 };
7506 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7507 }
7508#else /* !VBOX_WITH_QTGUI */
7509 if (0)
7510 ;
7511#endif /* VBOX_WITH_QTGUI */
7512
7513 else
7514
7515#ifdef VBOX_WITH_VBOXSDL
7516 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7517 {
7518 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7519 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7520 strcpy(pszNamePart, s_szVBoxSDL_exe);
7521
7522 Utf8Str idStr = mData->mUuid.toString();
7523 const char *apszArgs[] =
7524 {
7525 szPath,
7526 "--comment", mUserData->s.strName.c_str(),
7527 "--startvm", idStr.c_str(),
7528 pszSupStartupLogArg,
7529 NULL
7530 };
7531 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7532 }
7533#else /* !VBOX_WITH_VBOXSDL */
7534 if (0)
7535 ;
7536#endif /* !VBOX_WITH_VBOXSDL */
7537
7538 else
7539
7540#ifdef VBOX_WITH_HEADLESS
7541 if ( strFrontend == "headless"
7542 || strFrontend == "capture"
7543 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7544 )
7545 {
7546 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7547 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7548 * and a VM works even if the server has not been installed.
7549 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7550 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7551 * differently in 4.0 and 3.x.
7552 */
7553 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7554 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7555 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7556
7557 Utf8Str idStr = mData->mUuid.toString();
7558 const char *apszArgs[] =
7559 {
7560 szPath,
7561 "--comment", mUserData->s.strName.c_str(),
7562 "--startvm", idStr.c_str(),
7563 "--vrde", "config",
7564 0, /* For "--capture". */
7565 0, /* For "--sup-startup-log". */
7566 0
7567 };
7568 unsigned iArg = 7;
7569 if (strFrontend == "capture")
7570 apszArgs[iArg++] = "--capture";
7571 apszArgs[iArg++] = pszSupStartupLogArg;
7572
7573# ifdef RT_OS_WINDOWS
7574 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7575# else
7576 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7577# endif
7578 }
7579#else /* !VBOX_WITH_HEADLESS */
7580 if (0)
7581 ;
7582#endif /* !VBOX_WITH_HEADLESS */
7583 else
7584 {
7585 RTEnvDestroy(env);
7586 return setError(E_INVALIDARG,
7587 tr("Invalid frontend name: '%s'"),
7588 strFrontend.c_str());
7589 }
7590
7591 RTEnvDestroy(env);
7592
7593 if (RT_FAILURE(vrc))
7594 return setError(VBOX_E_IPRT_ERROR,
7595 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7596 mUserData->s.strName.c_str(), vrc);
7597
7598 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7599
7600 /*
7601 * Note that we don't release the lock here before calling the client,
7602 * because it doesn't need to call us back if called with a NULL argument.
7603 * Releasing the lock here is dangerous because we didn't prepare the
7604 * launch data yet, but the client we've just started may happen to be
7605 * too fast and call LockMachine() that will fail (because of PID, etc.),
7606 * so that the Machine will never get out of the Spawning session state.
7607 */
7608
7609 /* inform the session that it will be a remote one */
7610 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7611#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7612 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7613#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7614 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7615#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7616 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7617
7618 if (FAILED(rc))
7619 {
7620 /* restore the session state */
7621 mData->mSession.mState = SessionState_Unlocked;
7622 alock.release();
7623 mParent->i_addProcessToReap(pid);
7624 /* The failure may occur w/o any error info (from RPC), so provide one */
7625 return setError(VBOX_E_VM_ERROR,
7626 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7627 }
7628
7629 /* attach launch data to the machine */
7630 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7631 mData->mSession.mRemoteControls.push_back(aControl);
7632 mData->mSession.mProgress = aProgress;
7633 mData->mSession.mPID = pid;
7634 mData->mSession.mState = SessionState_Spawning;
7635 mData->mSession.mType = strFrontend;
7636
7637 alock.release();
7638 mParent->i_addProcessToReap(pid);
7639
7640 LogFlowThisFuncLeave();
7641 return S_OK;
7642}
7643
7644/**
7645 * Returns @c true if the given session machine instance has an open direct
7646 * session (and optionally also for direct sessions which are closing) and
7647 * returns the session control machine instance if so.
7648 *
7649 * Note that when the method returns @c false, the arguments remain unchanged.
7650 *
7651 * @param aMachine Session machine object.
7652 * @param aControl Direct session control object (optional).
7653 * @param aAllowClosing If true then additionally a session which is currently
7654 * being closed will also be allowed.
7655 *
7656 * @note locks this object for reading.
7657 */
7658bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7659 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7660 bool aAllowClosing /*= false*/)
7661{
7662 AutoLimitedCaller autoCaller(this);
7663 AssertComRCReturn(autoCaller.rc(), false);
7664
7665 /* just return false for inaccessible machines */
7666 if (getObjectState().getState() != ObjectState::Ready)
7667 return false;
7668
7669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7670
7671 if ( mData->mSession.mState == SessionState_Locked
7672 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7673 )
7674 {
7675 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7676
7677 aMachine = mData->mSession.mMachine;
7678
7679 if (aControl != NULL)
7680 *aControl = mData->mSession.mDirectControl;
7681
7682 return true;
7683 }
7684
7685 return false;
7686}
7687
7688/**
7689 * Returns @c true if the given machine has an spawning direct session.
7690 *
7691 * @note locks this object for reading.
7692 */
7693bool Machine::i_isSessionSpawning()
7694{
7695 AutoLimitedCaller autoCaller(this);
7696 AssertComRCReturn(autoCaller.rc(), false);
7697
7698 /* just return false for inaccessible machines */
7699 if (getObjectState().getState() != ObjectState::Ready)
7700 return false;
7701
7702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7703
7704 if (mData->mSession.mState == SessionState_Spawning)
7705 return true;
7706
7707 return false;
7708}
7709
7710/**
7711 * Called from the client watcher thread to check for unexpected client process
7712 * death during Session_Spawning state (e.g. before it successfully opened a
7713 * direct session).
7714 *
7715 * On Win32 and on OS/2, this method is called only when we've got the
7716 * direct client's process termination notification, so it always returns @c
7717 * true.
7718 *
7719 * On other platforms, this method returns @c true if the client process is
7720 * terminated and @c false if it's still alive.
7721 *
7722 * @note Locks this object for writing.
7723 */
7724bool Machine::i_checkForSpawnFailure()
7725{
7726 AutoCaller autoCaller(this);
7727 if (!autoCaller.isOk())
7728 {
7729 /* nothing to do */
7730 LogFlowThisFunc(("Already uninitialized!\n"));
7731 return true;
7732 }
7733
7734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7735
7736 if (mData->mSession.mState != SessionState_Spawning)
7737 {
7738 /* nothing to do */
7739 LogFlowThisFunc(("Not spawning any more!\n"));
7740 return true;
7741 }
7742
7743 HRESULT rc = S_OK;
7744
7745 /* PID not yet initialized, skip check. */
7746 if (mData->mSession.mPID == NIL_RTPROCESS)
7747 return false;
7748
7749 RTPROCSTATUS status;
7750 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7751
7752 if (vrc != VERR_PROCESS_RUNNING)
7753 {
7754 Utf8Str strExtraInfo;
7755
7756#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7757 /* If the startup logfile exists and is of non-zero length, tell the
7758 user to look there for more details to encourage them to attach it
7759 when reporting startup issues. */
7760 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7761 uint64_t cbStartupLogFile = 0;
7762 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7763 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7764 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7765#endif
7766
7767 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7768 rc = setError(E_FAIL,
7769 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7770 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7771 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7772 rc = setError(E_FAIL,
7773 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7774 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7775 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7776 rc = setError(E_FAIL,
7777 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7778 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7779 else
7780 rc = setError(E_FAIL,
7781 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7782 i_getName().c_str(), vrc, strExtraInfo.c_str());
7783 }
7784
7785 if (FAILED(rc))
7786 {
7787 /* Close the remote session, remove the remote control from the list
7788 * and reset session state to Closed (@note keep the code in sync with
7789 * the relevant part in LockMachine()). */
7790
7791 Assert(mData->mSession.mRemoteControls.size() == 1);
7792 if (mData->mSession.mRemoteControls.size() == 1)
7793 {
7794 ErrorInfoKeeper eik;
7795 mData->mSession.mRemoteControls.front()->Uninitialize();
7796 }
7797
7798 mData->mSession.mRemoteControls.clear();
7799 mData->mSession.mState = SessionState_Unlocked;
7800
7801 /* finalize the progress after setting the state */
7802 if (!mData->mSession.mProgress.isNull())
7803 {
7804 mData->mSession.mProgress->notifyComplete(rc);
7805 mData->mSession.mProgress.setNull();
7806 }
7807
7808 mData->mSession.mPID = NIL_RTPROCESS;
7809
7810 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7811 return true;
7812 }
7813
7814 return false;
7815}
7816
7817/**
7818 * Checks whether the machine can be registered. If so, commits and saves
7819 * all settings.
7820 *
7821 * @note Must be called from mParent's write lock. Locks this object and
7822 * children for writing.
7823 */
7824HRESULT Machine::i_prepareRegister()
7825{
7826 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7827
7828 AutoLimitedCaller autoCaller(this);
7829 AssertComRCReturnRC(autoCaller.rc());
7830
7831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7832
7833 /* wait for state dependents to drop to zero */
7834 i_ensureNoStateDependencies();
7835
7836 if (!mData->mAccessible)
7837 return setError(VBOX_E_INVALID_OBJECT_STATE,
7838 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7839 mUserData->s.strName.c_str(),
7840 mData->mUuid.toString().c_str());
7841
7842 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7843
7844 if (mData->mRegistered)
7845 return setError(VBOX_E_INVALID_OBJECT_STATE,
7846 tr("The machine '%s' with UUID {%s} is already registered"),
7847 mUserData->s.strName.c_str(),
7848 mData->mUuid.toString().c_str());
7849
7850 HRESULT rc = S_OK;
7851
7852 // Ensure the settings are saved. If we are going to be registered and
7853 // no config file exists yet, create it by calling i_saveSettings() too.
7854 if ( (mData->flModifications)
7855 || (!mData->pMachineConfigFile->fileExists())
7856 )
7857 {
7858 rc = i_saveSettings(NULL);
7859 // no need to check whether VirtualBox.xml needs saving too since
7860 // we can't have a machine XML file rename pending
7861 if (FAILED(rc)) return rc;
7862 }
7863
7864 /* more config checking goes here */
7865
7866 if (SUCCEEDED(rc))
7867 {
7868 /* we may have had implicit modifications we want to fix on success */
7869 i_commit();
7870
7871 mData->mRegistered = true;
7872 }
7873 else
7874 {
7875 /* we may have had implicit modifications we want to cancel on failure*/
7876 i_rollback(false /* aNotify */);
7877 }
7878
7879 return rc;
7880}
7881
7882/**
7883 * Increases the number of objects dependent on the machine state or on the
7884 * registered state. Guarantees that these two states will not change at least
7885 * until #releaseStateDependency() is called.
7886 *
7887 * Depending on the @a aDepType value, additional state checks may be made.
7888 * These checks will set extended error info on failure. See
7889 * #checkStateDependency() for more info.
7890 *
7891 * If this method returns a failure, the dependency is not added and the caller
7892 * is not allowed to rely on any particular machine state or registration state
7893 * value and may return the failed result code to the upper level.
7894 *
7895 * @param aDepType Dependency type to add.
7896 * @param aState Current machine state (NULL if not interested).
7897 * @param aRegistered Current registered state (NULL if not interested).
7898 *
7899 * @note Locks this object for writing.
7900 */
7901HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7902 MachineState_T *aState /* = NULL */,
7903 BOOL *aRegistered /* = NULL */)
7904{
7905 AutoCaller autoCaller(this);
7906 AssertComRCReturnRC(autoCaller.rc());
7907
7908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7909
7910 HRESULT rc = i_checkStateDependency(aDepType);
7911 if (FAILED(rc)) return rc;
7912
7913 {
7914 if (mData->mMachineStateChangePending != 0)
7915 {
7916 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7917 * drop to zero so don't add more. It may make sense to wait a bit
7918 * and retry before reporting an error (since the pending state
7919 * transition should be really quick) but let's just assert for
7920 * now to see if it ever happens on practice. */
7921
7922 AssertFailed();
7923
7924 return setError(E_ACCESSDENIED,
7925 tr("Machine state change is in progress. Please retry the operation later."));
7926 }
7927
7928 ++mData->mMachineStateDeps;
7929 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7930 }
7931
7932 if (aState)
7933 *aState = mData->mMachineState;
7934 if (aRegistered)
7935 *aRegistered = mData->mRegistered;
7936
7937 return S_OK;
7938}
7939
7940/**
7941 * Decreases the number of objects dependent on the machine state.
7942 * Must always complete the #addStateDependency() call after the state
7943 * dependency is no more necessary.
7944 */
7945void Machine::i_releaseStateDependency()
7946{
7947 AutoCaller autoCaller(this);
7948 AssertComRCReturnVoid(autoCaller.rc());
7949
7950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7951
7952 /* releaseStateDependency() w/o addStateDependency()? */
7953 AssertReturnVoid(mData->mMachineStateDeps != 0);
7954 -- mData->mMachineStateDeps;
7955
7956 if (mData->mMachineStateDeps == 0)
7957 {
7958 /* inform i_ensureNoStateDependencies() that there are no more deps */
7959 if (mData->mMachineStateChangePending != 0)
7960 {
7961 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7962 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7963 }
7964 }
7965}
7966
7967Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7968{
7969 /* start with nothing found */
7970 Utf8Str strResult("");
7971
7972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7973
7974 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7975 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7976 // found:
7977 strResult = it->second; // source is a Utf8Str
7978
7979 return strResult;
7980}
7981
7982// protected methods
7983/////////////////////////////////////////////////////////////////////////////
7984
7985/**
7986 * Performs machine state checks based on the @a aDepType value. If a check
7987 * fails, this method will set extended error info, otherwise it will return
7988 * S_OK. It is supposed, that on failure, the caller will immediately return
7989 * the return value of this method to the upper level.
7990 *
7991 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7992 *
7993 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7994 * current state of this machine object allows to change settings of the
7995 * machine (i.e. the machine is not registered, or registered but not running
7996 * and not saved). It is useful to call this method from Machine setters
7997 * before performing any change.
7998 *
7999 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8000 * as for MutableStateDep except that if the machine is saved, S_OK is also
8001 * returned. This is useful in setters which allow changing machine
8002 * properties when it is in the saved state.
8003 *
8004 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8005 * if the current state of this machine object allows to change runtime
8006 * changeable settings of the machine (i.e. the machine is not registered, or
8007 * registered but either running or not running and not saved). It is useful
8008 * to call this method from Machine setters before performing any changes to
8009 * runtime changeable settings.
8010 *
8011 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8012 * the same as for MutableOrRunningStateDep except that if the machine is
8013 * saved, S_OK is also returned. This is useful in setters which allow
8014 * changing runtime and saved state changeable machine properties.
8015 *
8016 * @param aDepType Dependency type to check.
8017 *
8018 * @note Non Machine based classes should use #addStateDependency() and
8019 * #releaseStateDependency() methods or the smart AutoStateDependency
8020 * template.
8021 *
8022 * @note This method must be called from under this object's read or write
8023 * lock.
8024 */
8025HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8026{
8027 switch (aDepType)
8028 {
8029 case AnyStateDep:
8030 {
8031 break;
8032 }
8033 case MutableStateDep:
8034 {
8035 if ( mData->mRegistered
8036 && ( !i_isSessionMachine()
8037 || ( mData->mMachineState != MachineState_Aborted
8038 && mData->mMachineState != MachineState_Teleported
8039 && mData->mMachineState != MachineState_PoweredOff
8040 )
8041 )
8042 )
8043 return setError(VBOX_E_INVALID_VM_STATE,
8044 tr("The machine is not mutable (state is %s)"),
8045 Global::stringifyMachineState(mData->mMachineState));
8046 break;
8047 }
8048 case MutableOrSavedStateDep:
8049 {
8050 if ( mData->mRegistered
8051 && ( !i_isSessionMachine()
8052 || ( mData->mMachineState != MachineState_Aborted
8053 && mData->mMachineState != MachineState_Teleported
8054 && mData->mMachineState != MachineState_Saved
8055 && mData->mMachineState != MachineState_PoweredOff
8056 )
8057 )
8058 )
8059 return setError(VBOX_E_INVALID_VM_STATE,
8060 tr("The machine is not mutable (state is %s)"),
8061 Global::stringifyMachineState(mData->mMachineState));
8062 break;
8063 }
8064 case MutableOrRunningStateDep:
8065 {
8066 if ( mData->mRegistered
8067 && ( !i_isSessionMachine()
8068 || ( mData->mMachineState != MachineState_Aborted
8069 && mData->mMachineState != MachineState_Teleported
8070 && mData->mMachineState != MachineState_PoweredOff
8071 && !Global::IsOnline(mData->mMachineState)
8072 )
8073 )
8074 )
8075 return setError(VBOX_E_INVALID_VM_STATE,
8076 tr("The machine is not mutable (state is %s)"),
8077 Global::stringifyMachineState(mData->mMachineState));
8078 break;
8079 }
8080 case MutableOrSavedOrRunningStateDep:
8081 {
8082 if ( mData->mRegistered
8083 && ( !i_isSessionMachine()
8084 || ( mData->mMachineState != MachineState_Aborted
8085 && mData->mMachineState != MachineState_Teleported
8086 && mData->mMachineState != MachineState_Saved
8087 && mData->mMachineState != MachineState_PoweredOff
8088 && !Global::IsOnline(mData->mMachineState)
8089 )
8090 )
8091 )
8092 return setError(VBOX_E_INVALID_VM_STATE,
8093 tr("The machine is not mutable (state is %s)"),
8094 Global::stringifyMachineState(mData->mMachineState));
8095 break;
8096 }
8097 }
8098
8099 return S_OK;
8100}
8101
8102/**
8103 * Helper to initialize all associated child objects and allocate data
8104 * structures.
8105 *
8106 * This method must be called as a part of the object's initialization procedure
8107 * (usually done in the #init() method).
8108 *
8109 * @note Must be called only from #init() or from #registeredInit().
8110 */
8111HRESULT Machine::initDataAndChildObjects()
8112{
8113 AutoCaller autoCaller(this);
8114 AssertComRCReturnRC(autoCaller.rc());
8115 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8116 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8117
8118 AssertReturn(!mData->mAccessible, E_FAIL);
8119
8120 /* allocate data structures */
8121 mSSData.allocate();
8122 mUserData.allocate();
8123 mHWData.allocate();
8124 mMediaData.allocate();
8125 mStorageControllers.allocate();
8126 mUSBControllers.allocate();
8127
8128 /* initialize mOSTypeId */
8129 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8130
8131 /* create associated BIOS settings object */
8132 unconst(mBIOSSettings).createObject();
8133 mBIOSSettings->init(this);
8134
8135 /* create an associated VRDE object (default is disabled) */
8136 unconst(mVRDEServer).createObject();
8137 mVRDEServer->init(this);
8138
8139 /* create associated serial port objects */
8140 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8141 {
8142 unconst(mSerialPorts[slot]).createObject();
8143 mSerialPorts[slot]->init(this, slot);
8144 }
8145
8146 /* create associated parallel port objects */
8147 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8148 {
8149 unconst(mParallelPorts[slot]).createObject();
8150 mParallelPorts[slot]->init(this, slot);
8151 }
8152
8153 /* create the audio adapter object (always present, default is disabled) */
8154 unconst(mAudioAdapter).createObject();
8155 mAudioAdapter->init(this);
8156
8157 /* create the USB device filters object (always present) */
8158 unconst(mUSBDeviceFilters).createObject();
8159 mUSBDeviceFilters->init(this);
8160
8161 /* create associated network adapter objects */
8162 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8163 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8164 {
8165 unconst(mNetworkAdapters[slot]).createObject();
8166 mNetworkAdapters[slot]->init(this, slot);
8167 }
8168
8169 /* create the bandwidth control */
8170 unconst(mBandwidthControl).createObject();
8171 mBandwidthControl->init(this);
8172
8173 return S_OK;
8174}
8175
8176/**
8177 * Helper to uninitialize all associated child objects and to free all data
8178 * structures.
8179 *
8180 * This method must be called as a part of the object's uninitialization
8181 * procedure (usually done in the #uninit() method).
8182 *
8183 * @note Must be called only from #uninit() or from #registeredInit().
8184 */
8185void Machine::uninitDataAndChildObjects()
8186{
8187 AutoCaller autoCaller(this);
8188 AssertComRCReturnVoid(autoCaller.rc());
8189 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8190 || getObjectState().getState() == ObjectState::Limited);
8191
8192 /* tell all our other child objects we've been uninitialized */
8193 if (mBandwidthControl)
8194 {
8195 mBandwidthControl->uninit();
8196 unconst(mBandwidthControl).setNull();
8197 }
8198
8199 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8200 {
8201 if (mNetworkAdapters[slot])
8202 {
8203 mNetworkAdapters[slot]->uninit();
8204 unconst(mNetworkAdapters[slot]).setNull();
8205 }
8206 }
8207
8208 if (mUSBDeviceFilters)
8209 {
8210 mUSBDeviceFilters->uninit();
8211 unconst(mUSBDeviceFilters).setNull();
8212 }
8213
8214 if (mAudioAdapter)
8215 {
8216 mAudioAdapter->uninit();
8217 unconst(mAudioAdapter).setNull();
8218 }
8219
8220 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8221 {
8222 if (mParallelPorts[slot])
8223 {
8224 mParallelPorts[slot]->uninit();
8225 unconst(mParallelPorts[slot]).setNull();
8226 }
8227 }
8228
8229 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8230 {
8231 if (mSerialPorts[slot])
8232 {
8233 mSerialPorts[slot]->uninit();
8234 unconst(mSerialPorts[slot]).setNull();
8235 }
8236 }
8237
8238 if (mVRDEServer)
8239 {
8240 mVRDEServer->uninit();
8241 unconst(mVRDEServer).setNull();
8242 }
8243
8244 if (mBIOSSettings)
8245 {
8246 mBIOSSettings->uninit();
8247 unconst(mBIOSSettings).setNull();
8248 }
8249
8250 /* Deassociate media (only when a real Machine or a SnapshotMachine
8251 * instance is uninitialized; SessionMachine instances refer to real
8252 * Machine media). This is necessary for a clean re-initialization of
8253 * the VM after successfully re-checking the accessibility state. Note
8254 * that in case of normal Machine or SnapshotMachine uninitialization (as
8255 * a result of unregistering or deleting the snapshot), outdated media
8256 * attachments will already be uninitialized and deleted, so this
8257 * code will not affect them. */
8258 if ( !!mMediaData
8259 && (!i_isSessionMachine())
8260 )
8261 {
8262 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8263 it != mMediaData->mAttachments.end();
8264 ++it)
8265 {
8266 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8267 if (pMedium.isNull())
8268 continue;
8269 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8270 AssertComRC(rc);
8271 }
8272 }
8273
8274 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8275 {
8276 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8277 if (mData->mFirstSnapshot)
8278 {
8279 // snapshots tree is protected by machine write lock; strictly
8280 // this isn't necessary here since we're deleting the entire
8281 // machine, but otherwise we assert in Snapshot::uninit()
8282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8283 mData->mFirstSnapshot->uninit();
8284 mData->mFirstSnapshot.setNull();
8285 }
8286
8287 mData->mCurrentSnapshot.setNull();
8288 }
8289
8290 /* free data structures (the essential mData structure is not freed here
8291 * since it may be still in use) */
8292 mMediaData.free();
8293 mStorageControllers.free();
8294 mUSBControllers.free();
8295 mHWData.free();
8296 mUserData.free();
8297 mSSData.free();
8298}
8299
8300/**
8301 * Returns a pointer to the Machine object for this machine that acts like a
8302 * parent for complex machine data objects such as shared folders, etc.
8303 *
8304 * For primary Machine objects and for SnapshotMachine objects, returns this
8305 * object's pointer itself. For SessionMachine objects, returns the peer
8306 * (primary) machine pointer.
8307 */
8308Machine* Machine::i_getMachine()
8309{
8310 if (i_isSessionMachine())
8311 return (Machine*)mPeer;
8312 return this;
8313}
8314
8315/**
8316 * Makes sure that there are no machine state dependents. If necessary, waits
8317 * for the number of dependents to drop to zero.
8318 *
8319 * Make sure this method is called from under this object's write lock to
8320 * guarantee that no new dependents may be added when this method returns
8321 * control to the caller.
8322 *
8323 * @note Locks this object for writing. The lock will be released while waiting
8324 * (if necessary).
8325 *
8326 * @warning To be used only in methods that change the machine state!
8327 */
8328void Machine::i_ensureNoStateDependencies()
8329{
8330 AssertReturnVoid(isWriteLockOnCurrentThread());
8331
8332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8333
8334 /* Wait for all state dependents if necessary */
8335 if (mData->mMachineStateDeps != 0)
8336 {
8337 /* lazy semaphore creation */
8338 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8339 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8340
8341 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8342 mData->mMachineStateDeps));
8343
8344 ++mData->mMachineStateChangePending;
8345
8346 /* reset the semaphore before waiting, the last dependent will signal
8347 * it */
8348 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8349
8350 alock.release();
8351
8352 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8353
8354 alock.acquire();
8355
8356 -- mData->mMachineStateChangePending;
8357 }
8358}
8359
8360/**
8361 * Changes the machine state and informs callbacks.
8362 *
8363 * This method is not intended to fail so it either returns S_OK or asserts (and
8364 * returns a failure).
8365 *
8366 * @note Locks this object for writing.
8367 */
8368HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8369{
8370 LogFlowThisFuncEnter();
8371 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8372
8373 AutoCaller autoCaller(this);
8374 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8375
8376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8377
8378 /* wait for state dependents to drop to zero */
8379 i_ensureNoStateDependencies();
8380
8381 MachineState_T const enmOldState = mData->mMachineState;
8382 if (enmOldState != aMachineState)
8383 {
8384 mData->mMachineState = aMachineState;
8385 RTTimeNow(&mData->mLastStateChange);
8386
8387#ifdef VBOX_WITH_DTRACE_R3_MAIN
8388 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8389#endif
8390 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8391 }
8392
8393 LogFlowThisFuncLeave();
8394 return S_OK;
8395}
8396
8397/**
8398 * Searches for a shared folder with the given logical name
8399 * in the collection of shared folders.
8400 *
8401 * @param aName logical name of the shared folder
8402 * @param aSharedFolder where to return the found object
8403 * @param aSetError whether to set the error info if the folder is
8404 * not found
8405 * @return
8406 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8407 *
8408 * @note
8409 * must be called from under the object's lock!
8410 */
8411HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8412 ComObjPtr<SharedFolder> &aSharedFolder,
8413 bool aSetError /* = false */)
8414{
8415 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8416 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8417 it != mHWData->mSharedFolders.end();
8418 ++it)
8419 {
8420 SharedFolder *pSF = *it;
8421 AutoCaller autoCaller(pSF);
8422 if (pSF->i_getName() == aName)
8423 {
8424 aSharedFolder = pSF;
8425 rc = S_OK;
8426 break;
8427 }
8428 }
8429
8430 if (aSetError && FAILED(rc))
8431 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8432
8433 return rc;
8434}
8435
8436/**
8437 * Initializes all machine instance data from the given settings structures
8438 * from XML. The exception is the machine UUID which needs special handling
8439 * depending on the caller's use case, so the caller needs to set that herself.
8440 *
8441 * This gets called in several contexts during machine initialization:
8442 *
8443 * -- When machine XML exists on disk already and needs to be loaded into memory,
8444 * for example, from registeredInit() to load all registered machines on
8445 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8446 * attached to the machine should be part of some media registry already.
8447 *
8448 * -- During OVF import, when a machine config has been constructed from an
8449 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8450 * ensure that the media listed as attachments in the config (which have
8451 * been imported from the OVF) receive the correct registry ID.
8452 *
8453 * -- During VM cloning.
8454 *
8455 * @param config Machine settings from XML.
8456 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8457 * for each attached medium in the config.
8458 * @return
8459 */
8460HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8461 const Guid *puuidRegistry)
8462{
8463 // copy name, description, OS type, teleporter, UTC etc.
8464 mUserData->s = config.machineUserData;
8465
8466 // Decode the Icon overide data from config userdata and set onto Machine.
8467 #define DECODE_STR_MAX _1M
8468 const char* pszStr = config.machineUserData.ovIcon.c_str();
8469 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8470 if (cbOut > DECODE_STR_MAX)
8471 return setError(E_FAIL,
8472 tr("Icon Data too long.'%d' > '%d'"),
8473 cbOut,
8474 DECODE_STR_MAX);
8475 mUserData->mIcon.resize(cbOut);
8476 int vrc = VINF_SUCCESS;
8477 if (cbOut)
8478 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8479 if (RT_FAILURE(vrc))
8480 {
8481 mUserData->mIcon.resize(0);
8482 return setError(E_FAIL,
8483 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8484 pszStr,
8485 vrc);
8486 }
8487
8488 // look up the object by Id to check it is valid
8489 ComPtr<IGuestOSType> guestOSType;
8490 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8491 guestOSType.asOutParam());
8492 if (FAILED(rc)) return rc;
8493
8494 // stateFile (optional)
8495 if (config.strStateFile.isEmpty())
8496 mSSData->strStateFilePath.setNull();
8497 else
8498 {
8499 Utf8Str stateFilePathFull(config.strStateFile);
8500 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8501 if (RT_FAILURE(vrc))
8502 return setError(E_FAIL,
8503 tr("Invalid saved state file path '%s' (%Rrc)"),
8504 config.strStateFile.c_str(),
8505 vrc);
8506 mSSData->strStateFilePath = stateFilePathFull;
8507 }
8508
8509 // snapshot folder needs special processing so set it again
8510 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8511 if (FAILED(rc)) return rc;
8512
8513 /* Copy the extra data items (Not in any case config is already the same as
8514 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8515 * make sure the extra data map is copied). */
8516 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8517
8518 /* currentStateModified (optional, default is true) */
8519 mData->mCurrentStateModified = config.fCurrentStateModified;
8520
8521 mData->mLastStateChange = config.timeLastStateChange;
8522
8523 /*
8524 * note: all mUserData members must be assigned prior this point because
8525 * we need to commit changes in order to let mUserData be shared by all
8526 * snapshot machine instances.
8527 */
8528 mUserData.commitCopy();
8529
8530 // machine registry, if present (must be loaded before snapshots)
8531 if (config.canHaveOwnMediaRegistry())
8532 {
8533 // determine machine folder
8534 Utf8Str strMachineFolder = i_getSettingsFileFull();
8535 strMachineFolder.stripFilename();
8536 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8537 config.mediaRegistry,
8538 strMachineFolder);
8539 if (FAILED(rc)) return rc;
8540 }
8541
8542 /* Snapshot node (optional) */
8543 size_t cRootSnapshots;
8544 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8545 {
8546 // there must be only one root snapshot
8547 Assert(cRootSnapshots == 1);
8548
8549 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8550
8551 rc = i_loadSnapshot(snap,
8552 config.uuidCurrentSnapshot,
8553 NULL); // no parent == first snapshot
8554 if (FAILED(rc)) return rc;
8555 }
8556
8557 // hardware data
8558 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8559 if (FAILED(rc)) return rc;
8560
8561 // load storage controllers
8562 rc = i_loadStorageControllers(config.storageMachine,
8563 puuidRegistry,
8564 NULL /* puuidSnapshot */);
8565 if (FAILED(rc)) return rc;
8566
8567 /*
8568 * NOTE: the assignment below must be the last thing to do,
8569 * otherwise it will be not possible to change the settings
8570 * somewhere in the code above because all setters will be
8571 * blocked by i_checkStateDependency(MutableStateDep).
8572 */
8573
8574 /* set the machine state to Aborted or Saved when appropriate */
8575 if (config.fAborted)
8576 {
8577 mSSData->strStateFilePath.setNull();
8578
8579 /* no need to use i_setMachineState() during init() */
8580 mData->mMachineState = MachineState_Aborted;
8581 }
8582 else if (!mSSData->strStateFilePath.isEmpty())
8583 {
8584 /* no need to use i_setMachineState() during init() */
8585 mData->mMachineState = MachineState_Saved;
8586 }
8587
8588 // after loading settings, we are no longer different from the XML on disk
8589 mData->flModifications = 0;
8590
8591 return S_OK;
8592}
8593
8594/**
8595 * Recursively loads all snapshots starting from the given.
8596 *
8597 * @param aNode <Snapshot> node.
8598 * @param aCurSnapshotId Current snapshot ID from the settings file.
8599 * @param aParentSnapshot Parent snapshot.
8600 */
8601HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8602 const Guid &aCurSnapshotId,
8603 Snapshot *aParentSnapshot)
8604{
8605 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8606 AssertReturn(!i_isSessionMachine(), E_FAIL);
8607
8608 HRESULT rc = S_OK;
8609
8610 Utf8Str strStateFile;
8611 if (!data.strStateFile.isEmpty())
8612 {
8613 /* optional */
8614 strStateFile = data.strStateFile;
8615 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8616 if (RT_FAILURE(vrc))
8617 return setError(E_FAIL,
8618 tr("Invalid saved state file path '%s' (%Rrc)"),
8619 strStateFile.c_str(),
8620 vrc);
8621 }
8622
8623 /* create a snapshot machine object */
8624 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8625 pSnapshotMachine.createObject();
8626 rc = pSnapshotMachine->initFromSettings(this,
8627 data.hardware,
8628 &data.debugging,
8629 &data.autostart,
8630 data.storage,
8631 data.uuid.ref(),
8632 strStateFile);
8633 if (FAILED(rc)) return rc;
8634
8635 /* create a snapshot object */
8636 ComObjPtr<Snapshot> pSnapshot;
8637 pSnapshot.createObject();
8638 /* initialize the snapshot */
8639 rc = pSnapshot->init(mParent, // VirtualBox object
8640 data.uuid,
8641 data.strName,
8642 data.strDescription,
8643 data.timestamp,
8644 pSnapshotMachine,
8645 aParentSnapshot);
8646 if (FAILED(rc)) return rc;
8647
8648 /* memorize the first snapshot if necessary */
8649 if (!mData->mFirstSnapshot)
8650 mData->mFirstSnapshot = pSnapshot;
8651
8652 /* memorize the current snapshot when appropriate */
8653 if ( !mData->mCurrentSnapshot
8654 && pSnapshot->i_getId() == aCurSnapshotId
8655 )
8656 mData->mCurrentSnapshot = pSnapshot;
8657
8658 // now create the children
8659 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8660 it != data.llChildSnapshots.end();
8661 ++it)
8662 {
8663 const settings::Snapshot &childData = *it;
8664 // recurse
8665 rc = i_loadSnapshot(childData,
8666 aCurSnapshotId,
8667 pSnapshot); // parent = the one we created above
8668 if (FAILED(rc)) return rc;
8669 }
8670
8671 return rc;
8672}
8673
8674/**
8675 * Loads settings into mHWData.
8676 *
8677 * @param data Reference to the hardware settings.
8678 * @param pDbg Pointer to the debugging settings.
8679 * @param pAutostart Pointer to the autostart settings.
8680 */
8681HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8682 const settings::Autostart *pAutostart)
8683{
8684 AssertReturn(!i_isSessionMachine(), E_FAIL);
8685
8686 HRESULT rc = S_OK;
8687
8688 try
8689 {
8690 /* The hardware version attribute (optional). */
8691 mHWData->mHWVersion = data.strVersion;
8692 mHWData->mHardwareUUID = data.uuid;
8693
8694 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8695 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8696 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8697 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8698 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8699 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8700 mHWData->mPAEEnabled = data.fPAE;
8701 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8702 mHWData->mLongMode = data.enmLongMode;
8703 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8704 mHWData->mCPUCount = data.cCPUs;
8705 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8706 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8707
8708 // cpu
8709 if (mHWData->mCPUHotPlugEnabled)
8710 {
8711 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8712 it != data.llCpus.end();
8713 ++it)
8714 {
8715 const settings::Cpu &cpu = *it;
8716
8717 mHWData->mCPUAttached[cpu.ulId] = true;
8718 }
8719 }
8720
8721 // cpuid leafs
8722 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8723 it != data.llCpuIdLeafs.end();
8724 ++it)
8725 {
8726 const settings::CpuIdLeaf &leaf = *it;
8727
8728 switch (leaf.ulId)
8729 {
8730 case 0x0:
8731 case 0x1:
8732 case 0x2:
8733 case 0x3:
8734 case 0x4:
8735 case 0x5:
8736 case 0x6:
8737 case 0x7:
8738 case 0x8:
8739 case 0x9:
8740 case 0xA:
8741 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8742 break;
8743
8744 case 0x80000000:
8745 case 0x80000001:
8746 case 0x80000002:
8747 case 0x80000003:
8748 case 0x80000004:
8749 case 0x80000005:
8750 case 0x80000006:
8751 case 0x80000007:
8752 case 0x80000008:
8753 case 0x80000009:
8754 case 0x8000000A:
8755 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8756 break;
8757
8758 default:
8759 /* just ignore */
8760 break;
8761 }
8762 }
8763
8764 mHWData->mMemorySize = data.ulMemorySizeMB;
8765 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8766
8767 // boot order
8768 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8769 {
8770 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8771 if (it == data.mapBootOrder.end())
8772 mHWData->mBootOrder[i] = DeviceType_Null;
8773 else
8774 mHWData->mBootOrder[i] = it->second;
8775 }
8776
8777 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8778 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8779 mHWData->mMonitorCount = data.cMonitors;
8780 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8781 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8782 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8783 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8784 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8785 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8786 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8787 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8788 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8789 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8790 if (!data.strVideoCaptureFile.isEmpty())
8791 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8792 else
8793 mHWData->mVideoCaptureFile.setNull();
8794 mHWData->mFirmwareType = data.firmwareType;
8795 mHWData->mPointingHIDType = data.pointingHIDType;
8796 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8797 mHWData->mChipsetType = data.chipsetType;
8798 mHWData->mParavirtProvider = data.paravirtProvider;
8799 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8800 mHWData->mHPETEnabled = data.fHPETEnabled;
8801
8802 /* VRDEServer */
8803 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8804 if (FAILED(rc)) return rc;
8805
8806 /* BIOS */
8807 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8808 if (FAILED(rc)) return rc;
8809
8810 // Bandwidth control (must come before network adapters)
8811 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8812 if (FAILED(rc)) return rc;
8813
8814 /* Shared folders */
8815 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8816 it != data.usbSettings.llUSBControllers.end();
8817 ++it)
8818 {
8819 const settings::USBController &settingsCtrl = *it;
8820 ComObjPtr<USBController> newCtrl;
8821
8822 newCtrl.createObject();
8823 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8824 mUSBControllers->push_back(newCtrl);
8825 }
8826
8827 /* USB device filters */
8828 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8829 if (FAILED(rc)) return rc;
8830
8831 // network adapters
8832 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8833 size_t oldCount = mNetworkAdapters.size();
8834 if (newCount > oldCount)
8835 {
8836 mNetworkAdapters.resize(newCount);
8837 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8838 {
8839 unconst(mNetworkAdapters[slot]).createObject();
8840 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8841 }
8842 }
8843 else if (newCount < oldCount)
8844 mNetworkAdapters.resize(newCount);
8845 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8846 it != data.llNetworkAdapters.end();
8847 ++it)
8848 {
8849 const settings::NetworkAdapter &nic = *it;
8850
8851 /* slot unicity is guaranteed by XML Schema */
8852 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8853 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8854 if (FAILED(rc)) return rc;
8855 }
8856
8857 // serial ports
8858 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8859 it != data.llSerialPorts.end();
8860 ++it)
8861 {
8862 const settings::SerialPort &s = *it;
8863
8864 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8865 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8866 if (FAILED(rc)) return rc;
8867 }
8868
8869 // parallel ports (optional)
8870 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8871 it != data.llParallelPorts.end();
8872 ++it)
8873 {
8874 const settings::ParallelPort &p = *it;
8875
8876 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8877 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8878 if (FAILED(rc)) return rc;
8879 }
8880
8881 /* AudioAdapter */
8882 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8883 if (FAILED(rc)) return rc;
8884
8885 /* Shared folders */
8886 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8887 it != data.llSharedFolders.end();
8888 ++it)
8889 {
8890 const settings::SharedFolder &sf = *it;
8891
8892 ComObjPtr<SharedFolder> sharedFolder;
8893 /* Check for double entries. Not allowed! */
8894 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8895 if (SUCCEEDED(rc))
8896 return setError(VBOX_E_OBJECT_IN_USE,
8897 tr("Shared folder named '%s' already exists"),
8898 sf.strName.c_str());
8899
8900 /* Create the new shared folder. Don't break on error. This will be
8901 * reported when the machine starts. */
8902 sharedFolder.createObject();
8903 rc = sharedFolder->init(i_getMachine(),
8904 sf.strName,
8905 sf.strHostPath,
8906 RT_BOOL(sf.fWritable),
8907 RT_BOOL(sf.fAutoMount),
8908 false /* fFailOnError */);
8909 if (FAILED(rc)) return rc;
8910 mHWData->mSharedFolders.push_back(sharedFolder);
8911 }
8912
8913 // Clipboard
8914 mHWData->mClipboardMode = data.clipboardMode;
8915
8916 // drag'n'drop
8917 mHWData->mDnDMode = data.dndMode;
8918
8919 // guest settings
8920 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8921
8922 // IO settings
8923 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8924 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8925
8926 // Host PCI devices
8927 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8928 it != data.pciAttachments.end();
8929 ++it)
8930 {
8931 const settings::HostPCIDeviceAttachment &hpda = *it;
8932 ComObjPtr<PCIDeviceAttachment> pda;
8933
8934 pda.createObject();
8935 pda->i_loadSettings(this, hpda);
8936 mHWData->mPCIDeviceAssignments.push_back(pda);
8937 }
8938
8939 /*
8940 * (The following isn't really real hardware, but it lives in HWData
8941 * for reasons of convenience.)
8942 */
8943
8944#ifdef VBOX_WITH_GUEST_PROPS
8945 /* Guest properties (optional) */
8946 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8947 it != data.llGuestProperties.end();
8948 ++it)
8949 {
8950 const settings::GuestProperty &prop = *it;
8951 uint32_t fFlags = guestProp::NILFLAG;
8952 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8953 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8954 mHWData->mGuestProperties[prop.strName] = property;
8955 }
8956
8957 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8958#endif /* VBOX_WITH_GUEST_PROPS defined */
8959
8960 rc = i_loadDebugging(pDbg);
8961 if (FAILED(rc))
8962 return rc;
8963
8964 mHWData->mAutostart = *pAutostart;
8965
8966 /* default frontend */
8967 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8968 }
8969 catch(std::bad_alloc &)
8970 {
8971 return E_OUTOFMEMORY;
8972 }
8973
8974 AssertComRC(rc);
8975 return rc;
8976}
8977
8978/**
8979 * Called from Machine::loadHardware() to load the debugging settings of the
8980 * machine.
8981 *
8982 * @param pDbg Pointer to the settings.
8983 */
8984HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8985{
8986 mHWData->mDebugging = *pDbg;
8987 /* no more processing currently required, this will probably change. */
8988 return S_OK;
8989}
8990
8991/**
8992 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8993 *
8994 * @param data
8995 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
8996 * @param puuidSnapshot
8997 * @return
8998 */
8999HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9000 const Guid *puuidRegistry,
9001 const Guid *puuidSnapshot)
9002{
9003 AssertReturn(!i_isSessionMachine(), E_FAIL);
9004
9005 HRESULT rc = S_OK;
9006
9007 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9008 it != data.llStorageControllers.end();
9009 ++it)
9010 {
9011 const settings::StorageController &ctlData = *it;
9012
9013 ComObjPtr<StorageController> pCtl;
9014 /* Try to find one with the name first. */
9015 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9016 if (SUCCEEDED(rc))
9017 return setError(VBOX_E_OBJECT_IN_USE,
9018 tr("Storage controller named '%s' already exists"),
9019 ctlData.strName.c_str());
9020
9021 pCtl.createObject();
9022 rc = pCtl->init(this,
9023 ctlData.strName,
9024 ctlData.storageBus,
9025 ctlData.ulInstance,
9026 ctlData.fBootable);
9027 if (FAILED(rc)) return rc;
9028
9029 mStorageControllers->push_back(pCtl);
9030
9031 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9032 if (FAILED(rc)) return rc;
9033
9034 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9035 if (FAILED(rc)) return rc;
9036
9037 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9038 if (FAILED(rc)) return rc;
9039
9040 /* Set IDE emulation settings (only for AHCI controller). */
9041 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9042 {
9043 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9044 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9045 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9046 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9047 )
9048 return rc;
9049 }
9050
9051 /* Load the attached devices now. */
9052 rc = i_loadStorageDevices(pCtl,
9053 ctlData,
9054 puuidRegistry,
9055 puuidSnapshot);
9056 if (FAILED(rc)) return rc;
9057 }
9058
9059 return S_OK;
9060}
9061
9062/**
9063 * Called from i_loadStorageControllers for a controller's devices.
9064 *
9065 * @param aStorageController
9066 * @param data
9067 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9068 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9069 * @return
9070 */
9071HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9072 const settings::StorageController &data,
9073 const Guid *puuidRegistry,
9074 const Guid *puuidSnapshot)
9075{
9076 HRESULT rc = S_OK;
9077
9078 /* paranoia: detect duplicate attachments */
9079 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9080 it != data.llAttachedDevices.end();
9081 ++it)
9082 {
9083 const settings::AttachedDevice &ad = *it;
9084
9085 for (settings::AttachedDevicesList::const_iterator it2 = it;
9086 it2 != data.llAttachedDevices.end();
9087 ++it2)
9088 {
9089 if (it == it2)
9090 continue;
9091
9092 const settings::AttachedDevice &ad2 = *it2;
9093
9094 if ( ad.lPort == ad2.lPort
9095 && ad.lDevice == ad2.lDevice)
9096 {
9097 return setError(E_FAIL,
9098 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9099 aStorageController->i_getName().c_str(),
9100 ad.lPort,
9101 ad.lDevice,
9102 mUserData->s.strName.c_str());
9103 }
9104 }
9105 }
9106
9107 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9108 it != data.llAttachedDevices.end();
9109 ++it)
9110 {
9111 const settings::AttachedDevice &dev = *it;
9112 ComObjPtr<Medium> medium;
9113
9114 switch (dev.deviceType)
9115 {
9116 case DeviceType_Floppy:
9117 case DeviceType_DVD:
9118 if (dev.strHostDriveSrc.isNotEmpty())
9119 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9120 false /* fRefresh */, medium);
9121 else
9122 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9123 dev.uuid,
9124 false /* fRefresh */,
9125 false /* aSetError */,
9126 medium);
9127 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9128 // This is not an error. The host drive or UUID might have vanished, so just go
9129 // ahead without this removeable medium attachment
9130 rc = S_OK;
9131 break;
9132
9133 case DeviceType_HardDisk:
9134 {
9135 /* find a hard disk by UUID */
9136 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9137 if (FAILED(rc))
9138 {
9139 if (i_isSnapshotMachine())
9140 {
9141 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9142 // so the user knows that the bad disk is in a snapshot somewhere
9143 com::ErrorInfo info;
9144 return setError(E_FAIL,
9145 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9146 puuidSnapshot->raw(),
9147 info.getText().raw());
9148 }
9149 else
9150 return rc;
9151 }
9152
9153 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9154
9155 if (medium->i_getType() == MediumType_Immutable)
9156 {
9157 if (i_isSnapshotMachine())
9158 return setError(E_FAIL,
9159 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9160 "of the virtual machine '%s' ('%s')"),
9161 medium->i_getLocationFull().c_str(),
9162 dev.uuid.raw(),
9163 puuidSnapshot->raw(),
9164 mUserData->s.strName.c_str(),
9165 mData->m_strConfigFileFull.c_str());
9166
9167 return setError(E_FAIL,
9168 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9169 medium->i_getLocationFull().c_str(),
9170 dev.uuid.raw(),
9171 mUserData->s.strName.c_str(),
9172 mData->m_strConfigFileFull.c_str());
9173 }
9174
9175 if (medium->i_getType() == MediumType_MultiAttach)
9176 {
9177 if (i_isSnapshotMachine())
9178 return setError(E_FAIL,
9179 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9180 "of the virtual machine '%s' ('%s')"),
9181 medium->i_getLocationFull().c_str(),
9182 dev.uuid.raw(),
9183 puuidSnapshot->raw(),
9184 mUserData->s.strName.c_str(),
9185 mData->m_strConfigFileFull.c_str());
9186
9187 return setError(E_FAIL,
9188 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9189 medium->i_getLocationFull().c_str(),
9190 dev.uuid.raw(),
9191 mUserData->s.strName.c_str(),
9192 mData->m_strConfigFileFull.c_str());
9193 }
9194
9195 if ( !i_isSnapshotMachine()
9196 && medium->i_getChildren().size() != 0
9197 )
9198 return setError(E_FAIL,
9199 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9200 "because it has %d differencing child hard disks"),
9201 medium->i_getLocationFull().c_str(),
9202 dev.uuid.raw(),
9203 mUserData->s.strName.c_str(),
9204 mData->m_strConfigFileFull.c_str(),
9205 medium->i_getChildren().size());
9206
9207 if (i_findAttachment(mMediaData->mAttachments,
9208 medium))
9209 return setError(E_FAIL,
9210 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9211 medium->i_getLocationFull().c_str(),
9212 dev.uuid.raw(),
9213 mUserData->s.strName.c_str(),
9214 mData->m_strConfigFileFull.c_str());
9215
9216 break;
9217 }
9218
9219 default:
9220 return setError(E_FAIL,
9221 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9222 medium->i_getLocationFull().c_str(),
9223 mUserData->s.strName.c_str(),
9224 mData->m_strConfigFileFull.c_str());
9225 }
9226
9227 if (FAILED(rc))
9228 break;
9229
9230 /* Bandwidth groups are loaded at this point. */
9231 ComObjPtr<BandwidthGroup> pBwGroup;
9232
9233 if (!dev.strBwGroup.isEmpty())
9234 {
9235 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9236 if (FAILED(rc))
9237 return setError(E_FAIL,
9238 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9239 medium->i_getLocationFull().c_str(),
9240 dev.strBwGroup.c_str(),
9241 mUserData->s.strName.c_str(),
9242 mData->m_strConfigFileFull.c_str());
9243 pBwGroup->i_reference();
9244 }
9245
9246 const Bstr controllerName = aStorageController->i_getName();
9247 ComObjPtr<MediumAttachment> pAttachment;
9248 pAttachment.createObject();
9249 rc = pAttachment->init(this,
9250 medium,
9251 controllerName,
9252 dev.lPort,
9253 dev.lDevice,
9254 dev.deviceType,
9255 false,
9256 dev.fPassThrough,
9257 dev.fTempEject,
9258 dev.fNonRotational,
9259 dev.fDiscard,
9260 dev.fHotPluggable,
9261 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9262 if (FAILED(rc)) break;
9263
9264 /* associate the medium with this machine and snapshot */
9265 if (!medium.isNull())
9266 {
9267 AutoCaller medCaller(medium);
9268 if (FAILED(medCaller.rc())) return medCaller.rc();
9269 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9270
9271 if (i_isSnapshotMachine())
9272 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9273 else
9274 rc = medium->i_addBackReference(mData->mUuid);
9275 /* If the medium->addBackReference fails it sets an appropriate
9276 * error message, so no need to do any guesswork here. */
9277
9278 if (puuidRegistry)
9279 // caller wants registry ID to be set on all attached media (OVF import case)
9280 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9281 }
9282
9283 if (FAILED(rc))
9284 break;
9285
9286 /* back up mMediaData to let registeredInit() properly rollback on failure
9287 * (= limited accessibility) */
9288 i_setModified(IsModified_Storage);
9289 mMediaData.backup();
9290 mMediaData->mAttachments.push_back(pAttachment);
9291 }
9292
9293 return rc;
9294}
9295
9296/**
9297 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9298 *
9299 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9300 * @param aSnapshot where to return the found snapshot
9301 * @param aSetError true to set extended error info on failure
9302 */
9303HRESULT Machine::i_findSnapshotById(const Guid &aId,
9304 ComObjPtr<Snapshot> &aSnapshot,
9305 bool aSetError /* = false */)
9306{
9307 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9308
9309 if (!mData->mFirstSnapshot)
9310 {
9311 if (aSetError)
9312 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9313 return E_FAIL;
9314 }
9315
9316 if (aId.isZero())
9317 aSnapshot = mData->mFirstSnapshot;
9318 else
9319 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9320
9321 if (!aSnapshot)
9322 {
9323 if (aSetError)
9324 return setError(E_FAIL,
9325 tr("Could not find a snapshot with UUID {%s}"),
9326 aId.toString().c_str());
9327 return E_FAIL;
9328 }
9329
9330 return S_OK;
9331}
9332
9333/**
9334 * Returns the snapshot with the given name or fails of no such snapshot.
9335 *
9336 * @param aName snapshot name to find
9337 * @param aSnapshot where to return the found snapshot
9338 * @param aSetError true to set extended error info on failure
9339 */
9340HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9341 ComObjPtr<Snapshot> &aSnapshot,
9342 bool aSetError /* = false */)
9343{
9344 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9345
9346 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9347
9348 if (!mData->mFirstSnapshot)
9349 {
9350 if (aSetError)
9351 return setError(VBOX_E_OBJECT_NOT_FOUND,
9352 tr("This machine does not have any snapshots"));
9353 return VBOX_E_OBJECT_NOT_FOUND;
9354 }
9355
9356 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9357
9358 if (!aSnapshot)
9359 {
9360 if (aSetError)
9361 return setError(VBOX_E_OBJECT_NOT_FOUND,
9362 tr("Could not find a snapshot named '%s'"), strName.c_str());
9363 return VBOX_E_OBJECT_NOT_FOUND;
9364 }
9365
9366 return S_OK;
9367}
9368
9369/**
9370 * Returns a storage controller object with the given name.
9371 *
9372 * @param aName storage controller name to find
9373 * @param aStorageController where to return the found storage controller
9374 * @param aSetError true to set extended error info on failure
9375 */
9376HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9377 ComObjPtr<StorageController> &aStorageController,
9378 bool aSetError /* = false */)
9379{
9380 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9381
9382 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9383 it != mStorageControllers->end();
9384 ++it)
9385 {
9386 if ((*it)->i_getName() == aName)
9387 {
9388 aStorageController = (*it);
9389 return S_OK;
9390 }
9391 }
9392
9393 if (aSetError)
9394 return setError(VBOX_E_OBJECT_NOT_FOUND,
9395 tr("Could not find a storage controller named '%s'"),
9396 aName.c_str());
9397 return VBOX_E_OBJECT_NOT_FOUND;
9398}
9399
9400/**
9401 * Returns a USB controller object with the given name.
9402 *
9403 * @param aName USB controller name to find
9404 * @param aUSBController where to return the found USB controller
9405 * @param aSetError true to set extended error info on failure
9406 */
9407HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9408 ComObjPtr<USBController> &aUSBController,
9409 bool aSetError /* = false */)
9410{
9411 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9412
9413 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9414 it != mUSBControllers->end();
9415 ++it)
9416 {
9417 if ((*it)->i_getName() == aName)
9418 {
9419 aUSBController = (*it);
9420 return S_OK;
9421 }
9422 }
9423
9424 if (aSetError)
9425 return setError(VBOX_E_OBJECT_NOT_FOUND,
9426 tr("Could not find a storage controller named '%s'"),
9427 aName.c_str());
9428 return VBOX_E_OBJECT_NOT_FOUND;
9429}
9430
9431/**
9432 * Returns the number of USB controller instance of the given type.
9433 *
9434 * @param enmType USB controller type.
9435 */
9436ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9437{
9438 ULONG cCtrls = 0;
9439
9440 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9441 it != mUSBControllers->end();
9442 ++it)
9443 {
9444 if ((*it)->i_getControllerType() == enmType)
9445 cCtrls++;
9446 }
9447
9448 return cCtrls;
9449}
9450
9451HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9452 MediaData::AttachmentList &atts)
9453{
9454 AutoCaller autoCaller(this);
9455 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9456
9457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9458
9459 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9460 it != mMediaData->mAttachments.end();
9461 ++it)
9462 {
9463 const ComObjPtr<MediumAttachment> &pAtt = *it;
9464 // should never happen, but deal with NULL pointers in the list.
9465 AssertStmt(!pAtt.isNull(), continue);
9466
9467 // getControllerName() needs caller+read lock
9468 AutoCaller autoAttCaller(pAtt);
9469 if (FAILED(autoAttCaller.rc()))
9470 {
9471 atts.clear();
9472 return autoAttCaller.rc();
9473 }
9474 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9475
9476 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9477 atts.push_back(pAtt);
9478 }
9479
9480 return S_OK;
9481}
9482
9483
9484/**
9485 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9486 * file if the machine name was changed and about creating a new settings file
9487 * if this is a new machine.
9488 *
9489 * @note Must be never called directly but only from #saveSettings().
9490 */
9491HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9492{
9493 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9494
9495 HRESULT rc = S_OK;
9496
9497 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9498
9499 /// @todo need to handle primary group change, too
9500
9501 /* attempt to rename the settings file if machine name is changed */
9502 if ( mUserData->s.fNameSync
9503 && mUserData.isBackedUp()
9504 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9505 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9506 )
9507 {
9508 bool dirRenamed = false;
9509 bool fileRenamed = false;
9510
9511 Utf8Str configFile, newConfigFile;
9512 Utf8Str configFilePrev, newConfigFilePrev;
9513 Utf8Str configDir, newConfigDir;
9514
9515 do
9516 {
9517 int vrc = VINF_SUCCESS;
9518
9519 Utf8Str name = mUserData.backedUpData()->s.strName;
9520 Utf8Str newName = mUserData->s.strName;
9521 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9522 if (group == "/")
9523 group.setNull();
9524 Utf8Str newGroup = mUserData->s.llGroups.front();
9525 if (newGroup == "/")
9526 newGroup.setNull();
9527
9528 configFile = mData->m_strConfigFileFull;
9529
9530 /* first, rename the directory if it matches the group and machine name */
9531 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9532 group.c_str(), RTPATH_DELIMITER, name.c_str());
9533 /** @todo hack, make somehow use of ComposeMachineFilename */
9534 if (mUserData->s.fDirectoryIncludesUUID)
9535 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9536 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9537 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9538 /** @todo hack, make somehow use of ComposeMachineFilename */
9539 if (mUserData->s.fDirectoryIncludesUUID)
9540 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9541 configDir = configFile;
9542 configDir.stripFilename();
9543 newConfigDir = configDir;
9544 if ( configDir.length() >= groupPlusName.length()
9545 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9546 groupPlusName.c_str()))
9547 {
9548 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9549 Utf8Str newConfigBaseDir(newConfigDir);
9550 newConfigDir.append(newGroupPlusName);
9551 /* consistency: use \ if appropriate on the platform */
9552 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9553 /* new dir and old dir cannot be equal here because of 'if'
9554 * above and because name != newName */
9555 Assert(configDir != newConfigDir);
9556 if (!fSettingsFileIsNew)
9557 {
9558 /* perform real rename only if the machine is not new */
9559 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9560 if ( vrc == VERR_FILE_NOT_FOUND
9561 || vrc == VERR_PATH_NOT_FOUND)
9562 {
9563 /* create the parent directory, then retry renaming */
9564 Utf8Str parent(newConfigDir);
9565 parent.stripFilename();
9566 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9567 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9568 }
9569 if (RT_FAILURE(vrc))
9570 {
9571 rc = setError(E_FAIL,
9572 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9573 configDir.c_str(),
9574 newConfigDir.c_str(),
9575 vrc);
9576 break;
9577 }
9578 /* delete subdirectories which are no longer needed */
9579 Utf8Str dir(configDir);
9580 dir.stripFilename();
9581 while (dir != newConfigBaseDir && dir != ".")
9582 {
9583 vrc = RTDirRemove(dir.c_str());
9584 if (RT_FAILURE(vrc))
9585 break;
9586 dir.stripFilename();
9587 }
9588 dirRenamed = true;
9589 }
9590 }
9591
9592 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9593 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9594
9595 /* then try to rename the settings file itself */
9596 if (newConfigFile != configFile)
9597 {
9598 /* get the path to old settings file in renamed directory */
9599 configFile = Utf8StrFmt("%s%c%s",
9600 newConfigDir.c_str(),
9601 RTPATH_DELIMITER,
9602 RTPathFilename(configFile.c_str()));
9603 if (!fSettingsFileIsNew)
9604 {
9605 /* perform real rename only if the machine is not new */
9606 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9607 if (RT_FAILURE(vrc))
9608 {
9609 rc = setError(E_FAIL,
9610 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9611 configFile.c_str(),
9612 newConfigFile.c_str(),
9613 vrc);
9614 break;
9615 }
9616 fileRenamed = true;
9617 configFilePrev = configFile;
9618 configFilePrev += "-prev";
9619 newConfigFilePrev = newConfigFile;
9620 newConfigFilePrev += "-prev";
9621 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9622 }
9623 }
9624
9625 // update m_strConfigFileFull amd mConfigFile
9626 mData->m_strConfigFileFull = newConfigFile;
9627 // compute the relative path too
9628 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9629
9630 // store the old and new so that VirtualBox::i_saveSettings() can update
9631 // the media registry
9632 if ( mData->mRegistered
9633 && (configDir != newConfigDir || configFile != newConfigFile))
9634 {
9635 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9636
9637 if (pfNeedsGlobalSaveSettings)
9638 *pfNeedsGlobalSaveSettings = true;
9639 }
9640
9641 // in the saved state file path, replace the old directory with the new directory
9642 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9643 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9644
9645 // and do the same thing for the saved state file paths of all the online snapshots
9646 if (mData->mFirstSnapshot)
9647 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9648 newConfigDir.c_str());
9649 }
9650 while (0);
9651
9652 if (FAILED(rc))
9653 {
9654 /* silently try to rename everything back */
9655 if (fileRenamed)
9656 {
9657 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9658 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9659 }
9660 if (dirRenamed)
9661 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9662 }
9663
9664 if (FAILED(rc)) return rc;
9665 }
9666
9667 if (fSettingsFileIsNew)
9668 {
9669 /* create a virgin config file */
9670 int vrc = VINF_SUCCESS;
9671
9672 /* ensure the settings directory exists */
9673 Utf8Str path(mData->m_strConfigFileFull);
9674 path.stripFilename();
9675 if (!RTDirExists(path.c_str()))
9676 {
9677 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9678 if (RT_FAILURE(vrc))
9679 {
9680 return setError(E_FAIL,
9681 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9682 path.c_str(),
9683 vrc);
9684 }
9685 }
9686
9687 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9688 path = Utf8Str(mData->m_strConfigFileFull);
9689 RTFILE f = NIL_RTFILE;
9690 vrc = RTFileOpen(&f, path.c_str(),
9691 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9692 if (RT_FAILURE(vrc))
9693 return setError(E_FAIL,
9694 tr("Could not create the settings file '%s' (%Rrc)"),
9695 path.c_str(),
9696 vrc);
9697 RTFileClose(f);
9698 }
9699
9700 return rc;
9701}
9702
9703/**
9704 * Saves and commits machine data, user data and hardware data.
9705 *
9706 * Note that on failure, the data remains uncommitted.
9707 *
9708 * @a aFlags may combine the following flags:
9709 *
9710 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9711 * Used when saving settings after an operation that makes them 100%
9712 * correspond to the settings from the current snapshot.
9713 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9714 * #isReallyModified() returns false. This is necessary for cases when we
9715 * change machine data directly, not through the backup()/commit() mechanism.
9716 * - SaveS_Force: settings will be saved without doing a deep compare of the
9717 * settings structures. This is used when this is called because snapshots
9718 * have changed to avoid the overhead of the deep compare.
9719 *
9720 * @note Must be called from under this object's write lock. Locks children for
9721 * writing.
9722 *
9723 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9724 * initialized to false and that will be set to true by this function if
9725 * the caller must invoke VirtualBox::i_saveSettings() because the global
9726 * settings have changed. This will happen if a machine rename has been
9727 * saved and the global machine and media registries will therefore need
9728 * updating.
9729 */
9730HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9731 int aFlags /*= 0*/)
9732{
9733 LogFlowThisFuncEnter();
9734
9735 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9736
9737 /* make sure child objects are unable to modify the settings while we are
9738 * saving them */
9739 i_ensureNoStateDependencies();
9740
9741 AssertReturn(!i_isSnapshotMachine(),
9742 E_FAIL);
9743
9744 HRESULT rc = S_OK;
9745 bool fNeedsWrite = false;
9746
9747 /* First, prepare to save settings. It will care about renaming the
9748 * settings directory and file if the machine name was changed and about
9749 * creating a new settings file if this is a new machine. */
9750 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9751 if (FAILED(rc)) return rc;
9752
9753 // keep a pointer to the current settings structures
9754 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9755 settings::MachineConfigFile *pNewConfig = NULL;
9756
9757 try
9758 {
9759 // make a fresh one to have everyone write stuff into
9760 pNewConfig = new settings::MachineConfigFile(NULL);
9761 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9762
9763 // now go and copy all the settings data from COM to the settings structures
9764 // (this calles i_saveSettings() on all the COM objects in the machine)
9765 i_copyMachineDataToSettings(*pNewConfig);
9766
9767 if (aFlags & SaveS_ResetCurStateModified)
9768 {
9769 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9770 mData->mCurrentStateModified = FALSE;
9771 fNeedsWrite = true; // always, no need to compare
9772 }
9773 else if (aFlags & SaveS_Force)
9774 {
9775 fNeedsWrite = true; // always, no need to compare
9776 }
9777 else
9778 {
9779 if (!mData->mCurrentStateModified)
9780 {
9781 // do a deep compare of the settings that we just saved with the settings
9782 // previously stored in the config file; this invokes MachineConfigFile::operator==
9783 // which does a deep compare of all the settings, which is expensive but less expensive
9784 // than writing out XML in vain
9785 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9786
9787 // could still be modified if any settings changed
9788 mData->mCurrentStateModified = fAnySettingsChanged;
9789
9790 fNeedsWrite = fAnySettingsChanged;
9791 }
9792 else
9793 fNeedsWrite = true;
9794 }
9795
9796 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9797
9798 if (fNeedsWrite)
9799 // now spit it all out!
9800 pNewConfig->write(mData->m_strConfigFileFull);
9801
9802 mData->pMachineConfigFile = pNewConfig;
9803 delete pOldConfig;
9804 i_commit();
9805
9806 // after saving settings, we are no longer different from the XML on disk
9807 mData->flModifications = 0;
9808 }
9809 catch (HRESULT err)
9810 {
9811 // we assume that error info is set by the thrower
9812 rc = err;
9813
9814 // restore old config
9815 delete pNewConfig;
9816 mData->pMachineConfigFile = pOldConfig;
9817 }
9818 catch (...)
9819 {
9820 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9821 }
9822
9823 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9824 {
9825 /* Fire the data change event, even on failure (since we've already
9826 * committed all data). This is done only for SessionMachines because
9827 * mutable Machine instances are always not registered (i.e. private
9828 * to the client process that creates them) and thus don't need to
9829 * inform callbacks. */
9830 if (i_isSessionMachine())
9831 mParent->i_onMachineDataChange(mData->mUuid);
9832 }
9833
9834 LogFlowThisFunc(("rc=%08X\n", rc));
9835 LogFlowThisFuncLeave();
9836 return rc;
9837}
9838
9839/**
9840 * Implementation for saving the machine settings into the given
9841 * settings::MachineConfigFile instance. This copies machine extradata
9842 * from the previous machine config file in the instance data, if any.
9843 *
9844 * This gets called from two locations:
9845 *
9846 * -- Machine::i_saveSettings(), during the regular XML writing;
9847 *
9848 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9849 * exported to OVF and we write the VirtualBox proprietary XML
9850 * into a <vbox:Machine> tag.
9851 *
9852 * This routine fills all the fields in there, including snapshots, *except*
9853 * for the following:
9854 *
9855 * -- fCurrentStateModified. There is some special logic associated with that.
9856 *
9857 * The caller can then call MachineConfigFile::write() or do something else
9858 * with it.
9859 *
9860 * Caller must hold the machine lock!
9861 *
9862 * This throws XML errors and HRESULT, so the caller must have a catch block!
9863 */
9864void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9865{
9866 // deep copy extradata
9867 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9868
9869 config.uuid = mData->mUuid;
9870
9871 // copy name, description, OS type, teleport, UTC etc.
9872 config.machineUserData = mUserData->s;
9873
9874 // Encode the Icon Override data from Machine and store on config userdata.
9875 std::vector<BYTE> iconByte;
9876 getIcon(iconByte);
9877 ssize_t cbData = iconByte.size();
9878 if (cbData > 0)
9879 {
9880 ssize_t cchOut = RTBase64EncodedLength(cbData);
9881 Utf8Str strIconData;
9882 strIconData.reserve(cchOut+1);
9883 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9884 strIconData.mutableRaw(), strIconData.capacity(),
9885 NULL);
9886 if (RT_FAILURE(vrc))
9887 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9888 strIconData.jolt();
9889 config.machineUserData.ovIcon = strIconData;
9890 }
9891 else
9892 config.machineUserData.ovIcon.setNull();
9893
9894 if ( mData->mMachineState == MachineState_Saved
9895 || mData->mMachineState == MachineState_Restoring
9896 // when deleting a snapshot we may or may not have a saved state in the current state,
9897 // so let's not assert here please
9898 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9899 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9900 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9901 && (!mSSData->strStateFilePath.isEmpty())
9902 )
9903 )
9904 {
9905 Assert(!mSSData->strStateFilePath.isEmpty());
9906 /* try to make the file name relative to the settings file dir */
9907 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9908 }
9909 else
9910 {
9911 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9912 config.strStateFile.setNull();
9913 }
9914
9915 if (mData->mCurrentSnapshot)
9916 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9917 else
9918 config.uuidCurrentSnapshot.clear();
9919
9920 config.timeLastStateChange = mData->mLastStateChange;
9921 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9922 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9923
9924 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9925 if (FAILED(rc)) throw rc;
9926
9927 rc = i_saveStorageControllers(config.storageMachine);
9928 if (FAILED(rc)) throw rc;
9929
9930 // save machine's media registry if this is VirtualBox 4.0 or later
9931 if (config.canHaveOwnMediaRegistry())
9932 {
9933 // determine machine folder
9934 Utf8Str strMachineFolder = i_getSettingsFileFull();
9935 strMachineFolder.stripFilename();
9936 mParent->i_saveMediaRegistry(config.mediaRegistry,
9937 i_getId(), // only media with registry ID == machine UUID
9938 strMachineFolder);
9939 // this throws HRESULT
9940 }
9941
9942 // save snapshots
9943 rc = i_saveAllSnapshots(config);
9944 if (FAILED(rc)) throw rc;
9945}
9946
9947/**
9948 * Saves all snapshots of the machine into the given machine config file. Called
9949 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9950 * @param config
9951 * @return
9952 */
9953HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9954{
9955 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9956
9957 HRESULT rc = S_OK;
9958
9959 try
9960 {
9961 config.llFirstSnapshot.clear();
9962
9963 if (mData->mFirstSnapshot)
9964 {
9965 settings::Snapshot snapNew;
9966 config.llFirstSnapshot.push_back(snapNew);
9967
9968 // get reference to the fresh copy of the snapshot on the list and
9969 // work on that copy directly to avoid excessive copying later
9970 settings::Snapshot &snap = config.llFirstSnapshot.front();
9971
9972 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
9973 if (FAILED(rc)) throw rc;
9974 }
9975
9976// if (mType == IsSessionMachine)
9977// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9978
9979 }
9980 catch (HRESULT err)
9981 {
9982 /* we assume that error info is set by the thrower */
9983 rc = err;
9984 }
9985 catch (...)
9986 {
9987 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9988 }
9989
9990 return rc;
9991}
9992
9993/**
9994 * Saves the VM hardware configuration. It is assumed that the
9995 * given node is empty.
9996 *
9997 * @param data Reference to the settings object for the hardware config.
9998 * @param pDbg Pointer to the settings object for the debugging config
9999 * which happens to live in mHWData.
10000 * @param pAutostart Pointer to the settings object for the autostart config
10001 * which happens to live in mHWData.
10002 */
10003HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10004 settings::Autostart *pAutostart)
10005{
10006 HRESULT rc = S_OK;
10007
10008 try
10009 {
10010 /* The hardware version attribute (optional).
10011 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10012 if ( mHWData->mHWVersion == "1"
10013 && mSSData->strStateFilePath.isEmpty()
10014 )
10015 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10016 other point needs to be found where this can be done. */
10017
10018 data.strVersion = mHWData->mHWVersion;
10019 data.uuid = mHWData->mHardwareUUID;
10020
10021 // CPU
10022 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10023 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10024 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10025 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10026 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10027 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10028 data.fPAE = !!mHWData->mPAEEnabled;
10029 data.enmLongMode = mHWData->mLongMode;
10030 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10031 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10032
10033 /* Standard and Extended CPUID leafs. */
10034 data.llCpuIdLeafs.clear();
10035 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10036 {
10037 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10038 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10039 }
10040 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10041 {
10042 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10043 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10044 }
10045
10046 data.cCPUs = mHWData->mCPUCount;
10047 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10048 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10049
10050 data.llCpus.clear();
10051 if (data.fCpuHotPlug)
10052 {
10053 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10054 {
10055 if (mHWData->mCPUAttached[idx])
10056 {
10057 settings::Cpu cpu;
10058 cpu.ulId = idx;
10059 data.llCpus.push_back(cpu);
10060 }
10061 }
10062 }
10063
10064 // memory
10065 data.ulMemorySizeMB = mHWData->mMemorySize;
10066 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10067
10068 // firmware
10069 data.firmwareType = mHWData->mFirmwareType;
10070
10071 // HID
10072 data.pointingHIDType = mHWData->mPointingHIDType;
10073 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10074
10075 // chipset
10076 data.chipsetType = mHWData->mChipsetType;
10077
10078 // paravirt
10079 data.paravirtProvider = mHWData->mParavirtProvider;
10080
10081
10082 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10083
10084 // HPET
10085 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10086
10087 // boot order
10088 data.mapBootOrder.clear();
10089 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10090 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10091
10092 // display
10093 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10094 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10095 data.cMonitors = mHWData->mMonitorCount;
10096 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10097 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10098 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10099 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10100 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10101 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10102 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10103 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10104 {
10105 if (mHWData->maVideoCaptureScreens[i])
10106 ASMBitSet(&data.u64VideoCaptureScreens, i);
10107 else
10108 ASMBitClear(&data.u64VideoCaptureScreens, i);
10109 }
10110 /* store relative video capture file if possible */
10111 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10112
10113 /* VRDEServer settings (optional) */
10114 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10115 if (FAILED(rc)) throw rc;
10116
10117 /* BIOS (required) */
10118 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10119 if (FAILED(rc)) throw rc;
10120
10121 /* USB Controller (required) */
10122 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10123 {
10124 ComObjPtr<USBController> ctrl = *it;
10125 settings::USBController settingsCtrl;
10126
10127 settingsCtrl.strName = ctrl->i_getName();
10128 settingsCtrl.enmType = ctrl->i_getControllerType();
10129
10130 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10131 }
10132
10133 /* USB device filters (required) */
10134 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10135 if (FAILED(rc)) throw rc;
10136
10137 /* Network adapters (required) */
10138 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10139 data.llNetworkAdapters.clear();
10140 /* Write out only the nominal number of network adapters for this
10141 * chipset type. Since Machine::commit() hasn't been called there
10142 * may be extra NIC settings in the vector. */
10143 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10144 {
10145 settings::NetworkAdapter nic;
10146 nic.ulSlot = (uint32_t)slot;
10147 /* paranoia check... must not be NULL, but must not crash either. */
10148 if (mNetworkAdapters[slot])
10149 {
10150 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10151 if (FAILED(rc)) throw rc;
10152
10153 data.llNetworkAdapters.push_back(nic);
10154 }
10155 }
10156
10157 /* Serial ports */
10158 data.llSerialPorts.clear();
10159 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10160 {
10161 settings::SerialPort s;
10162 s.ulSlot = slot;
10163 rc = mSerialPorts[slot]->i_saveSettings(s);
10164 if (FAILED(rc)) return rc;
10165
10166 data.llSerialPorts.push_back(s);
10167 }
10168
10169 /* Parallel ports */
10170 data.llParallelPorts.clear();
10171 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10172 {
10173 settings::ParallelPort p;
10174 p.ulSlot = slot;
10175 rc = mParallelPorts[slot]->i_saveSettings(p);
10176 if (FAILED(rc)) return rc;
10177
10178 data.llParallelPorts.push_back(p);
10179 }
10180
10181 /* Audio adapter */
10182 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10183 if (FAILED(rc)) return rc;
10184
10185 /* Shared folders */
10186 data.llSharedFolders.clear();
10187 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10188 it != mHWData->mSharedFolders.end();
10189 ++it)
10190 {
10191 SharedFolder *pSF = *it;
10192 AutoCaller sfCaller(pSF);
10193 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10194 settings::SharedFolder sf;
10195 sf.strName = pSF->i_getName();
10196 sf.strHostPath = pSF->i_getHostPath();
10197 sf.fWritable = !!pSF->i_isWritable();
10198 sf.fAutoMount = !!pSF->i_isAutoMounted();
10199
10200 data.llSharedFolders.push_back(sf);
10201 }
10202
10203 // clipboard
10204 data.clipboardMode = mHWData->mClipboardMode;
10205
10206 // drag'n'drop
10207 data.dndMode = mHWData->mDnDMode;
10208
10209 /* Guest */
10210 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10211
10212 // IO settings
10213 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10214 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10215
10216 /* BandwidthControl (required) */
10217 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10218 if (FAILED(rc)) throw rc;
10219
10220 /* Host PCI devices */
10221 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10222 it != mHWData->mPCIDeviceAssignments.end();
10223 ++it)
10224 {
10225 ComObjPtr<PCIDeviceAttachment> pda = *it;
10226 settings::HostPCIDeviceAttachment hpda;
10227
10228 rc = pda->i_saveSettings(hpda);
10229 if (FAILED(rc)) throw rc;
10230
10231 data.pciAttachments.push_back(hpda);
10232 }
10233
10234
10235 // guest properties
10236 data.llGuestProperties.clear();
10237#ifdef VBOX_WITH_GUEST_PROPS
10238 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10239 it != mHWData->mGuestProperties.end();
10240 ++it)
10241 {
10242 HWData::GuestProperty property = it->second;
10243
10244 /* Remove transient guest properties at shutdown unless we
10245 * are saving state */
10246 if ( ( mData->mMachineState == MachineState_PoweredOff
10247 || mData->mMachineState == MachineState_Aborted
10248 || mData->mMachineState == MachineState_Teleported)
10249 && ( property.mFlags & guestProp::TRANSIENT
10250 || property.mFlags & guestProp::TRANSRESET))
10251 continue;
10252 settings::GuestProperty prop;
10253 prop.strName = it->first;
10254 prop.strValue = property.strValue;
10255 prop.timestamp = property.mTimestamp;
10256 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10257 guestProp::writeFlags(property.mFlags, szFlags);
10258 prop.strFlags = szFlags;
10259
10260 data.llGuestProperties.push_back(prop);
10261 }
10262
10263 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10264 /* I presume this doesn't require a backup(). */
10265 mData->mGuestPropertiesModified = FALSE;
10266#endif /* VBOX_WITH_GUEST_PROPS defined */
10267
10268 *pDbg = mHWData->mDebugging;
10269 *pAutostart = mHWData->mAutostart;
10270
10271 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10272 }
10273 catch(std::bad_alloc &)
10274 {
10275 return E_OUTOFMEMORY;
10276 }
10277
10278 AssertComRC(rc);
10279 return rc;
10280}
10281
10282/**
10283 * Saves the storage controller configuration.
10284 *
10285 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10286 */
10287HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10288{
10289 data.llStorageControllers.clear();
10290
10291 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10292 it != mStorageControllers->end();
10293 ++it)
10294 {
10295 HRESULT rc;
10296 ComObjPtr<StorageController> pCtl = *it;
10297
10298 settings::StorageController ctl;
10299 ctl.strName = pCtl->i_getName();
10300 ctl.controllerType = pCtl->i_getControllerType();
10301 ctl.storageBus = pCtl->i_getStorageBus();
10302 ctl.ulInstance = pCtl->i_getInstance();
10303 ctl.fBootable = pCtl->i_getBootable();
10304
10305 /* Save the port count. */
10306 ULONG portCount;
10307 rc = pCtl->COMGETTER(PortCount)(&portCount);
10308 ComAssertComRCRet(rc, rc);
10309 ctl.ulPortCount = portCount;
10310
10311 /* Save fUseHostIOCache */
10312 BOOL fUseHostIOCache;
10313 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10314 ComAssertComRCRet(rc, rc);
10315 ctl.fUseHostIOCache = !!fUseHostIOCache;
10316
10317 /* Save IDE emulation settings. */
10318 if (ctl.controllerType == StorageControllerType_IntelAhci)
10319 {
10320 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10321 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10322 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10323 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10324 )
10325 ComAssertComRCRet(rc, rc);
10326 }
10327
10328 /* save the devices now. */
10329 rc = i_saveStorageDevices(pCtl, ctl);
10330 ComAssertComRCRet(rc, rc);
10331
10332 data.llStorageControllers.push_back(ctl);
10333 }
10334
10335 return S_OK;
10336}
10337
10338/**
10339 * Saves the hard disk configuration.
10340 */
10341HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10342 settings::StorageController &data)
10343{
10344 MediaData::AttachmentList atts;
10345
10346 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10347 if (FAILED(rc)) return rc;
10348
10349 data.llAttachedDevices.clear();
10350 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10351 it != atts.end();
10352 ++it)
10353 {
10354 settings::AttachedDevice dev;
10355 IMediumAttachment *iA = *it;
10356 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10357 Medium *pMedium = pAttach->i_getMedium();
10358
10359 dev.deviceType = pAttach->i_getType();
10360 dev.lPort = pAttach->i_getPort();
10361 dev.lDevice = pAttach->i_getDevice();
10362 dev.fPassThrough = pAttach->i_getPassthrough();
10363 dev.fHotPluggable = pAttach->i_getHotPluggable();
10364 if (pMedium)
10365 {
10366 if (pMedium->i_isHostDrive())
10367 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10368 else
10369 dev.uuid = pMedium->i_getId();
10370 dev.fTempEject = pAttach->i_getTempEject();
10371 dev.fNonRotational = pAttach->i_getNonRotational();
10372 dev.fDiscard = pAttach->i_getDiscard();
10373 }
10374
10375 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10376
10377 data.llAttachedDevices.push_back(dev);
10378 }
10379
10380 return S_OK;
10381}
10382
10383/**
10384 * Saves machine state settings as defined by aFlags
10385 * (SaveSTS_* values).
10386 *
10387 * @param aFlags Combination of SaveSTS_* flags.
10388 *
10389 * @note Locks objects for writing.
10390 */
10391HRESULT Machine::i_saveStateSettings(int aFlags)
10392{
10393 if (aFlags == 0)
10394 return S_OK;
10395
10396 AutoCaller autoCaller(this);
10397 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10398
10399 /* This object's write lock is also necessary to serialize file access
10400 * (prevent concurrent reads and writes) */
10401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10402
10403 HRESULT rc = S_OK;
10404
10405 Assert(mData->pMachineConfigFile);
10406
10407 try
10408 {
10409 if (aFlags & SaveSTS_CurStateModified)
10410 mData->pMachineConfigFile->fCurrentStateModified = true;
10411
10412 if (aFlags & SaveSTS_StateFilePath)
10413 {
10414 if (!mSSData->strStateFilePath.isEmpty())
10415 /* try to make the file name relative to the settings file dir */
10416 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10417 else
10418 mData->pMachineConfigFile->strStateFile.setNull();
10419 }
10420
10421 if (aFlags & SaveSTS_StateTimeStamp)
10422 {
10423 Assert( mData->mMachineState != MachineState_Aborted
10424 || mSSData->strStateFilePath.isEmpty());
10425
10426 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10427
10428 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10429//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10430 }
10431
10432 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10433 }
10434 catch (...)
10435 {
10436 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10437 }
10438
10439 return rc;
10440}
10441
10442/**
10443 * Ensures that the given medium is added to a media registry. If this machine
10444 * was created with 4.0 or later, then the machine registry is used. Otherwise
10445 * the global VirtualBox media registry is used.
10446 *
10447 * Caller must NOT hold machine lock, media tree or any medium locks!
10448 *
10449 * @param pMedium
10450 */
10451void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10452{
10453 /* Paranoia checks: do not hold machine or media tree locks. */
10454 AssertReturnVoid(!isWriteLockOnCurrentThread());
10455 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10456
10457 ComObjPtr<Medium> pBase;
10458 {
10459 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10460 pBase = pMedium->i_getBase();
10461 }
10462
10463 /* Paranoia checks: do not hold medium locks. */
10464 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10465 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10466
10467 // decide which medium registry to use now that the medium is attached:
10468 Guid uuid;
10469 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10470 // machine XML is VirtualBox 4.0 or higher:
10471 uuid = i_getId(); // machine UUID
10472 else
10473 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10474
10475 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
10476 mParent->i_markRegistryModified(uuid);
10477
10478 /* For more complex hard disk structures it can happen that the base
10479 * medium isn't yet associated with any medium registry. Do that now. */
10480 if (pMedium != pBase)
10481 {
10482 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
10483 mParent->i_markRegistryModified(uuid);
10484 }
10485}
10486
10487/**
10488 * Creates differencing hard disks for all normal hard disks attached to this
10489 * machine and a new set of attachments to refer to created disks.
10490 *
10491 * Used when taking a snapshot or when deleting the current state. Gets called
10492 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10493 *
10494 * This method assumes that mMediaData contains the original hard disk attachments
10495 * it needs to create diffs for. On success, these attachments will be replaced
10496 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10497 * called to delete created diffs which will also rollback mMediaData and restore
10498 * whatever was backed up before calling this method.
10499 *
10500 * Attachments with non-normal hard disks are left as is.
10501 *
10502 * If @a aOnline is @c false then the original hard disks that require implicit
10503 * diffs will be locked for reading. Otherwise it is assumed that they are
10504 * already locked for writing (when the VM was started). Note that in the latter
10505 * case it is responsibility of the caller to lock the newly created diffs for
10506 * writing if this method succeeds.
10507 *
10508 * @param aProgress Progress object to run (must contain at least as
10509 * many operations left as the number of hard disks
10510 * attached).
10511 * @param aOnline Whether the VM was online prior to this operation.
10512 *
10513 * @note The progress object is not marked as completed, neither on success nor
10514 * on failure. This is a responsibility of the caller.
10515 *
10516 * @note Locks this object and the media tree for writing.
10517 */
10518HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10519 ULONG aWeight,
10520 bool aOnline)
10521{
10522 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10523
10524 AutoCaller autoCaller(this);
10525 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10526
10527 AutoMultiWriteLock2 alock(this->lockHandle(),
10528 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10529
10530 /* must be in a protective state because we release the lock below */
10531 AssertReturn( mData->mMachineState == MachineState_Saving
10532 || mData->mMachineState == MachineState_LiveSnapshotting
10533 || mData->mMachineState == MachineState_RestoringSnapshot
10534 || mData->mMachineState == MachineState_DeletingSnapshot
10535 , E_FAIL);
10536
10537 HRESULT rc = S_OK;
10538
10539 // use appropriate locked media map (online or offline)
10540 MediumLockListMap lockedMediaOffline;
10541 MediumLockListMap *lockedMediaMap;
10542 if (aOnline)
10543 lockedMediaMap = &mData->mSession.mLockedMedia;
10544 else
10545 lockedMediaMap = &lockedMediaOffline;
10546
10547 try
10548 {
10549 if (!aOnline)
10550 {
10551 /* lock all attached hard disks early to detect "in use"
10552 * situations before creating actual diffs */
10553 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10554 it != mMediaData->mAttachments.end();
10555 ++it)
10556 {
10557 MediumAttachment* pAtt = *it;
10558 if (pAtt->i_getType() == DeviceType_HardDisk)
10559 {
10560 Medium* pMedium = pAtt->i_getMedium();
10561 Assert(pMedium);
10562
10563 MediumLockList *pMediumLockList(new MediumLockList());
10564 alock.release();
10565 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10566 false /* fMediumLockWrite */,
10567 false /* fMediumLockWriteAll */,
10568 NULL,
10569 *pMediumLockList);
10570 alock.acquire();
10571 if (FAILED(rc))
10572 {
10573 delete pMediumLockList;
10574 throw rc;
10575 }
10576 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10577 if (FAILED(rc))
10578 {
10579 throw setError(rc,
10580 tr("Collecting locking information for all attached media failed"));
10581 }
10582 }
10583 }
10584
10585 /* Now lock all media. If this fails, nothing is locked. */
10586 alock.release();
10587 rc = lockedMediaMap->Lock();
10588 alock.acquire();
10589 if (FAILED(rc))
10590 {
10591 throw setError(rc,
10592 tr("Locking of attached media failed"));
10593 }
10594 }
10595
10596 /* remember the current list (note that we don't use backup() since
10597 * mMediaData may be already backed up) */
10598 MediaData::AttachmentList atts = mMediaData->mAttachments;
10599
10600 /* start from scratch */
10601 mMediaData->mAttachments.clear();
10602
10603 /* go through remembered attachments and create diffs for normal hard
10604 * disks and attach them */
10605 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10606 it != atts.end();
10607 ++it)
10608 {
10609 MediumAttachment* pAtt = *it;
10610
10611 DeviceType_T devType = pAtt->i_getType();
10612 Medium* pMedium = pAtt->i_getMedium();
10613
10614 if ( devType != DeviceType_HardDisk
10615 || pMedium == NULL
10616 || pMedium->i_getType() != MediumType_Normal)
10617 {
10618 /* copy the attachment as is */
10619
10620 /** @todo the progress object created in Console::TakeSnaphot
10621 * only expects operations for hard disks. Later other
10622 * device types need to show up in the progress as well. */
10623 if (devType == DeviceType_HardDisk)
10624 {
10625 if (pMedium == NULL)
10626 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10627 aWeight); // weight
10628 else
10629 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10630 pMedium->i_getBase()->i_getName().c_str()).raw(),
10631 aWeight); // weight
10632 }
10633
10634 mMediaData->mAttachments.push_back(pAtt);
10635 continue;
10636 }
10637
10638 /* need a diff */
10639 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10640 pMedium->i_getBase()->i_getName().c_str()).raw(),
10641 aWeight); // weight
10642
10643 Utf8Str strFullSnapshotFolder;
10644 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10645
10646 ComObjPtr<Medium> diff;
10647 diff.createObject();
10648 // store the diff in the same registry as the parent
10649 // (this cannot fail here because we can't create implicit diffs for
10650 // unregistered images)
10651 Guid uuidRegistryParent;
10652 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10653 Assert(fInRegistry); NOREF(fInRegistry);
10654 rc = diff->init(mParent,
10655 pMedium->i_getPreferredDiffFormat(),
10656 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10657 uuidRegistryParent,
10658 DeviceType_HardDisk);
10659 if (FAILED(rc)) throw rc;
10660
10661 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10662 * the push_back? Looks like we're going to release medium with the
10663 * wrong kind of lock (general issue with if we fail anywhere at all)
10664 * and an orphaned VDI in the snapshots folder. */
10665
10666 /* update the appropriate lock list */
10667 MediumLockList *pMediumLockList;
10668 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10669 AssertComRCThrowRC(rc);
10670 if (aOnline)
10671 {
10672 alock.release();
10673 /* The currently attached medium will be read-only, change
10674 * the lock type to read. */
10675 rc = pMediumLockList->Update(pMedium, false);
10676 alock.acquire();
10677 AssertComRCThrowRC(rc);
10678 }
10679
10680 /* release the locks before the potentially lengthy operation */
10681 alock.release();
10682 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10683 pMediumLockList,
10684 NULL /* aProgress */,
10685 true /* aWait */);
10686 alock.acquire();
10687 if (FAILED(rc)) throw rc;
10688
10689 /* actual lock list update is done in Medium::commitMedia */
10690
10691 rc = diff->i_addBackReference(mData->mUuid);
10692 AssertComRCThrowRC(rc);
10693
10694 /* add a new attachment */
10695 ComObjPtr<MediumAttachment> attachment;
10696 attachment.createObject();
10697 rc = attachment->init(this,
10698 diff,
10699 pAtt->i_getControllerName(),
10700 pAtt->i_getPort(),
10701 pAtt->i_getDevice(),
10702 DeviceType_HardDisk,
10703 true /* aImplicit */,
10704 false /* aPassthrough */,
10705 false /* aTempEject */,
10706 pAtt->i_getNonRotational(),
10707 pAtt->i_getDiscard(),
10708 pAtt->i_getHotPluggable(),
10709 pAtt->i_getBandwidthGroup());
10710 if (FAILED(rc)) throw rc;
10711
10712 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10713 AssertComRCThrowRC(rc);
10714 mMediaData->mAttachments.push_back(attachment);
10715 }
10716 }
10717 catch (HRESULT aRC) { rc = aRC; }
10718
10719 /* unlock all hard disks we locked when there is no VM */
10720 if (!aOnline)
10721 {
10722 ErrorInfoKeeper eik;
10723
10724 HRESULT rc1 = lockedMediaMap->Clear();
10725 AssertComRC(rc1);
10726 }
10727
10728 return rc;
10729}
10730
10731/**
10732 * Deletes implicit differencing hard disks created either by
10733 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10734 *
10735 * Note that to delete hard disks created by #AttachDevice() this method is
10736 * called from #fixupMedia() when the changes are rolled back.
10737 *
10738 * @note Locks this object and the media tree for writing.
10739 */
10740HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10741{
10742 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10743
10744 AutoCaller autoCaller(this);
10745 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10746
10747 AutoMultiWriteLock2 alock(this->lockHandle(),
10748 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10749
10750 /* We absolutely must have backed up state. */
10751 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10752
10753 /* Check if there are any implicitly created diff images. */
10754 bool fImplicitDiffs = false;
10755 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10756 it != mMediaData->mAttachments.end();
10757 ++it)
10758 {
10759 const ComObjPtr<MediumAttachment> &pAtt = *it;
10760 if (pAtt->i_isImplicit())
10761 {
10762 fImplicitDiffs = true;
10763 break;
10764 }
10765 }
10766 /* If there is nothing to do, leave early. This saves lots of image locking
10767 * effort. It also avoids a MachineStateChanged event without real reason.
10768 * This is important e.g. when loading a VM config, because there should be
10769 * no events. Otherwise API clients can become thoroughly confused for
10770 * inaccessible VMs (the code for loading VM configs uses this method for
10771 * cleanup if the config makes no sense), as they take such events as an
10772 * indication that the VM is alive, and they would force the VM config to
10773 * be reread, leading to an endless loop. */
10774 if (!fImplicitDiffs)
10775 return S_OK;
10776
10777 HRESULT rc = S_OK;
10778 MachineState_T oldState = mData->mMachineState;
10779
10780 /* will release the lock before the potentially lengthy operation,
10781 * so protect with the special state (unless already protected) */
10782 if ( oldState != MachineState_Saving
10783 && oldState != MachineState_LiveSnapshotting
10784 && oldState != MachineState_RestoringSnapshot
10785 && oldState != MachineState_DeletingSnapshot
10786 && oldState != MachineState_DeletingSnapshotOnline
10787 && oldState != MachineState_DeletingSnapshotPaused
10788 )
10789 i_setMachineState(MachineState_SettingUp);
10790
10791 // use appropriate locked media map (online or offline)
10792 MediumLockListMap lockedMediaOffline;
10793 MediumLockListMap *lockedMediaMap;
10794 if (aOnline)
10795 lockedMediaMap = &mData->mSession.mLockedMedia;
10796 else
10797 lockedMediaMap = &lockedMediaOffline;
10798
10799 try
10800 {
10801 if (!aOnline)
10802 {
10803 /* lock all attached hard disks early to detect "in use"
10804 * situations before deleting actual diffs */
10805 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10806 it != mMediaData->mAttachments.end();
10807 ++it)
10808 {
10809 MediumAttachment* pAtt = *it;
10810 if (pAtt->i_getType() == DeviceType_HardDisk)
10811 {
10812 Medium* pMedium = pAtt->i_getMedium();
10813 Assert(pMedium);
10814
10815 MediumLockList *pMediumLockList(new MediumLockList());
10816 alock.release();
10817 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10818 false /* fMediumLockWrite */,
10819 false /* fMediumLockWriteAll */,
10820 NULL,
10821 *pMediumLockList);
10822 alock.acquire();
10823
10824 if (FAILED(rc))
10825 {
10826 delete pMediumLockList;
10827 throw rc;
10828 }
10829
10830 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10831 if (FAILED(rc))
10832 throw rc;
10833 }
10834 }
10835
10836 if (FAILED(rc))
10837 throw rc;
10838 } // end of offline
10839
10840 /* Lock lists are now up to date and include implicitly created media */
10841
10842 /* Go through remembered attachments and delete all implicitly created
10843 * diffs and fix up the attachment information */
10844 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10845 MediaData::AttachmentList implicitAtts;
10846 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10847 it != mMediaData->mAttachments.end();
10848 ++it)
10849 {
10850 ComObjPtr<MediumAttachment> pAtt = *it;
10851 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10852 if (pMedium.isNull())
10853 continue;
10854
10855 // Implicit attachments go on the list for deletion and back references are removed.
10856 if (pAtt->i_isImplicit())
10857 {
10858 /* Deassociate and mark for deletion */
10859 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10860 rc = pMedium->i_removeBackReference(mData->mUuid);
10861 if (FAILED(rc))
10862 throw rc;
10863 implicitAtts.push_back(pAtt);
10864 continue;
10865 }
10866
10867 /* Was this medium attached before? */
10868 if (!i_findAttachment(oldAtts, pMedium))
10869 {
10870 /* no: de-associate */
10871 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10872 rc = pMedium->i_removeBackReference(mData->mUuid);
10873 if (FAILED(rc))
10874 throw rc;
10875 continue;
10876 }
10877 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10878 }
10879
10880 /* If there are implicit attachments to delete, throw away the lock
10881 * map contents (which will unlock all media) since the medium
10882 * attachments will be rolled back. Below we need to completely
10883 * recreate the lock map anyway since it is infinitely complex to
10884 * do this incrementally (would need reconstructing each attachment
10885 * change, which would be extremely hairy). */
10886 if (implicitAtts.size() != 0)
10887 {
10888 ErrorInfoKeeper eik;
10889
10890 HRESULT rc1 = lockedMediaMap->Clear();
10891 AssertComRC(rc1);
10892 }
10893
10894 /* rollback hard disk changes */
10895 mMediaData.rollback();
10896
10897 MultiResult mrc(S_OK);
10898
10899 // Delete unused implicit diffs.
10900 if (implicitAtts.size() != 0)
10901 {
10902 alock.release();
10903
10904 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10905 {
10906 // Remove medium associated with this attachment.
10907 ComObjPtr<MediumAttachment> pAtt = *it;
10908 Assert(pAtt);
10909 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10910 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10911 Assert(pMedium);
10912
10913 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10914 // continue on delete failure, just collect error messages
10915 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10916 pMedium->i_getLocationFull().c_str() ));
10917 mrc = rc;
10918 }
10919
10920 alock.acquire();
10921
10922 /* if there is a VM recreate media lock map as mentioned above,
10923 * otherwise it is a waste of time and we leave things unlocked */
10924 if (aOnline)
10925 {
10926 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10927 /* must never be NULL, but better safe than sorry */
10928 if (!pMachine.isNull())
10929 {
10930 alock.release();
10931 rc = mData->mSession.mMachine->i_lockMedia();
10932 alock.acquire();
10933 if (FAILED(rc))
10934 throw rc;
10935 }
10936 }
10937 }
10938 }
10939 catch (HRESULT aRC) {rc = aRC;}
10940
10941 if (mData->mMachineState == MachineState_SettingUp)
10942 i_setMachineState(oldState);
10943
10944 /* unlock all hard disks we locked when there is no VM */
10945 if (!aOnline)
10946 {
10947 ErrorInfoKeeper eik;
10948
10949 HRESULT rc1 = lockedMediaMap->Clear();
10950 AssertComRC(rc1);
10951 }
10952
10953 return rc;
10954}
10955
10956
10957/**
10958 * Looks through the given list of media attachments for one with the given parameters
10959 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10960 * can be searched as well if needed.
10961 *
10962 * @param list
10963 * @param aControllerName
10964 * @param aControllerPort
10965 * @param aDevice
10966 * @return
10967 */
10968MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10969 IN_BSTR aControllerName,
10970 LONG aControllerPort,
10971 LONG aDevice)
10972{
10973 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10974 {
10975 MediumAttachment *pAttach = *it;
10976 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
10977 return pAttach;
10978 }
10979
10980 return NULL;
10981}
10982
10983/**
10984 * Looks through the given list of media attachments for one with the given parameters
10985 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10986 * can be searched as well if needed.
10987 *
10988 * @param list
10989 * @param aControllerName
10990 * @param aControllerPort
10991 * @param aDevice
10992 * @return
10993 */
10994MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
10995 ComObjPtr<Medium> pMedium)
10996{
10997 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
10998 {
10999 MediumAttachment *pAttach = *it;
11000 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11001 if (pMediumThis == pMedium)
11002 return pAttach;
11003 }
11004
11005 return NULL;
11006}
11007
11008/**
11009 * Looks through the given list of media attachments for one with the given parameters
11010 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11011 * can be searched as well if needed.
11012 *
11013 * @param list
11014 * @param aControllerName
11015 * @param aControllerPort
11016 * @param aDevice
11017 * @return
11018 */
11019MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11020 Guid &id)
11021{
11022 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11023 {
11024 MediumAttachment *pAttach = *it;
11025 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11026 if (pMediumThis->i_getId() == id)
11027 return pAttach;
11028 }
11029
11030 return NULL;
11031}
11032
11033/**
11034 * Main implementation for Machine::DetachDevice. This also gets called
11035 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11036 *
11037 * @param pAttach Medium attachment to detach.
11038 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11039 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11040 * SnapshotMachine, and this must be its snapshot.
11041 * @return
11042 */
11043HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11044 AutoWriteLock &writeLock,
11045 Snapshot *pSnapshot)
11046{
11047 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11048 DeviceType_T mediumType = pAttach->i_getType();
11049
11050 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11051
11052 if (pAttach->i_isImplicit())
11053 {
11054 /* attempt to implicitly delete the implicitly created diff */
11055
11056 /// @todo move the implicit flag from MediumAttachment to Medium
11057 /// and forbid any hard disk operation when it is implicit. Or maybe
11058 /// a special media state for it to make it even more simple.
11059
11060 Assert(mMediaData.isBackedUp());
11061
11062 /* will release the lock before the potentially lengthy operation, so
11063 * protect with the special state */
11064 MachineState_T oldState = mData->mMachineState;
11065 i_setMachineState(MachineState_SettingUp);
11066
11067 writeLock.release();
11068
11069 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11070 true /*aWait*/);
11071
11072 writeLock.acquire();
11073
11074 i_setMachineState(oldState);
11075
11076 if (FAILED(rc)) return rc;
11077 }
11078
11079 i_setModified(IsModified_Storage);
11080 mMediaData.backup();
11081 mMediaData->mAttachments.remove(pAttach);
11082
11083 if (!oldmedium.isNull())
11084 {
11085 // if this is from a snapshot, do not defer detachment to commitMedia()
11086 if (pSnapshot)
11087 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11088 // else if non-hard disk media, do not defer detachment to commitMedia() either
11089 else if (mediumType != DeviceType_HardDisk)
11090 oldmedium->i_removeBackReference(mData->mUuid);
11091 }
11092
11093 return S_OK;
11094}
11095
11096/**
11097 * Goes thru all media of the given list and
11098 *
11099 * 1) calls i_detachDevice() on each of them for this machine and
11100 * 2) adds all Medium objects found in the process to the given list,
11101 * depending on cleanupMode.
11102 *
11103 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11104 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11105 * media to the list.
11106 *
11107 * This gets called from Machine::Unregister, both for the actual Machine and
11108 * the SnapshotMachine objects that might be found in the snapshots.
11109 *
11110 * Requires caller and locking. The machine lock must be passed in because it
11111 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11112 *
11113 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11114 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11115 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11116 * Full, then all media get added;
11117 * otherwise no media get added.
11118 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11119 * @return
11120 */
11121HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11122 Snapshot *pSnapshot,
11123 CleanupMode_T cleanupMode,
11124 MediaList &llMedia)
11125{
11126 Assert(isWriteLockOnCurrentThread());
11127
11128 HRESULT rc;
11129
11130 // make a temporary list because i_detachDevice invalidates iterators into
11131 // mMediaData->mAttachments
11132 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11133
11134 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11135 {
11136 ComObjPtr<MediumAttachment> &pAttach = *it;
11137 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11138
11139 if (!pMedium.isNull())
11140 {
11141 AutoCaller mac(pMedium);
11142 if (FAILED(mac.rc())) return mac.rc();
11143 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11144 DeviceType_T devType = pMedium->i_getDeviceType();
11145 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11146 && devType == DeviceType_HardDisk)
11147 || (cleanupMode == CleanupMode_Full)
11148 )
11149 {
11150 llMedia.push_back(pMedium);
11151 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11152 /* Not allowed to keep this lock as below we need the parent
11153 * medium lock, and the lock order is parent to child. */
11154 lock.release();
11155 /*
11156 * Search for medias which are not attached to any machine, but
11157 * in the chain to an attached disk. Mediums are only consided
11158 * if they are:
11159 * - have only one child
11160 * - no references to any machines
11161 * - are of normal medium type
11162 */
11163 while (!pParent.isNull())
11164 {
11165 AutoCaller mac1(pParent);
11166 if (FAILED(mac1.rc())) return mac1.rc();
11167 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11168 if (pParent->i_getChildren().size() == 1)
11169 {
11170 if ( pParent->i_getMachineBackRefCount() == 0
11171 && pParent->i_getType() == MediumType_Normal
11172 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11173 llMedia.push_back(pParent);
11174 }
11175 else
11176 break;
11177 pParent = pParent->i_getParent();
11178 }
11179 }
11180 }
11181
11182 // real machine: then we need to use the proper method
11183 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11184
11185 if (FAILED(rc))
11186 return rc;
11187 }
11188
11189 return S_OK;
11190}
11191
11192/**
11193 * Perform deferred hard disk detachments.
11194 *
11195 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11196 * backed up).
11197 *
11198 * If @a aOnline is @c true then this method will also unlock the old hard disks
11199 * for which the new implicit diffs were created and will lock these new diffs for
11200 * writing.
11201 *
11202 * @param aOnline Whether the VM was online prior to this operation.
11203 *
11204 * @note Locks this object for writing!
11205 */
11206void Machine::i_commitMedia(bool aOnline /*= false*/)
11207{
11208 AutoCaller autoCaller(this);
11209 AssertComRCReturnVoid(autoCaller.rc());
11210
11211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11212
11213 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11214
11215 HRESULT rc = S_OK;
11216
11217 /* no attach/detach operations -- nothing to do */
11218 if (!mMediaData.isBackedUp())
11219 return;
11220
11221 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11222 bool fMediaNeedsLocking = false;
11223
11224 /* enumerate new attachments */
11225 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11226 it != mMediaData->mAttachments.end();
11227 ++it)
11228 {
11229 MediumAttachment *pAttach = *it;
11230
11231 pAttach->i_commit();
11232
11233 Medium* pMedium = pAttach->i_getMedium();
11234 bool fImplicit = pAttach->i_isImplicit();
11235
11236 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11237 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11238 fImplicit));
11239
11240 /** @todo convert all this Machine-based voodoo to MediumAttachment
11241 * based commit logic. */
11242 if (fImplicit)
11243 {
11244 /* convert implicit attachment to normal */
11245 pAttach->i_setImplicit(false);
11246
11247 if ( aOnline
11248 && pMedium
11249 && pAttach->i_getType() == DeviceType_HardDisk
11250 )
11251 {
11252 /* update the appropriate lock list */
11253 MediumLockList *pMediumLockList;
11254 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11255 AssertComRC(rc);
11256 if (pMediumLockList)
11257 {
11258 /* unlock if there's a need to change the locking */
11259 if (!fMediaNeedsLocking)
11260 {
11261 rc = mData->mSession.mLockedMedia.Unlock();
11262 AssertComRC(rc);
11263 fMediaNeedsLocking = true;
11264 }
11265 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11266 AssertComRC(rc);
11267 rc = pMediumLockList->Append(pMedium, true);
11268 AssertComRC(rc);
11269 }
11270 }
11271
11272 continue;
11273 }
11274
11275 if (pMedium)
11276 {
11277 /* was this medium attached before? */
11278 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11279 {
11280 MediumAttachment *pOldAttach = *oldIt;
11281 if (pOldAttach->i_getMedium() == pMedium)
11282 {
11283 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11284
11285 /* yes: remove from old to avoid de-association */
11286 oldAtts.erase(oldIt);
11287 break;
11288 }
11289 }
11290 }
11291 }
11292
11293 /* enumerate remaining old attachments and de-associate from the
11294 * current machine state */
11295 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11296 {
11297 MediumAttachment *pAttach = *it;
11298 Medium* pMedium = pAttach->i_getMedium();
11299
11300 /* Detach only hard disks, since DVD/floppy media is detached
11301 * instantly in MountMedium. */
11302 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11303 {
11304 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11305
11306 /* now de-associate from the current machine state */
11307 rc = pMedium->i_removeBackReference(mData->mUuid);
11308 AssertComRC(rc);
11309
11310 if (aOnline)
11311 {
11312 /* unlock since medium is not used anymore */
11313 MediumLockList *pMediumLockList;
11314 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11315 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11316 {
11317 /* this happens for online snapshots, there the attachment
11318 * is changing, but only to a diff image created under
11319 * the old one, so there is no separate lock list */
11320 Assert(!pMediumLockList);
11321 }
11322 else
11323 {
11324 AssertComRC(rc);
11325 if (pMediumLockList)
11326 {
11327 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11328 AssertComRC(rc);
11329 }
11330 }
11331 }
11332 }
11333 }
11334
11335 /* take media locks again so that the locking state is consistent */
11336 if (fMediaNeedsLocking)
11337 {
11338 Assert(aOnline);
11339 rc = mData->mSession.mLockedMedia.Lock();
11340 AssertComRC(rc);
11341 }
11342
11343 /* commit the hard disk changes */
11344 mMediaData.commit();
11345
11346 if (i_isSessionMachine())
11347 {
11348 /*
11349 * Update the parent machine to point to the new owner.
11350 * This is necessary because the stored parent will point to the
11351 * session machine otherwise and cause crashes or errors later
11352 * when the session machine gets invalid.
11353 */
11354 /** @todo Change the MediumAttachment class to behave like any other
11355 * class in this regard by creating peer MediumAttachment
11356 * objects for session machines and share the data with the peer
11357 * machine.
11358 */
11359 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11360 it != mMediaData->mAttachments.end();
11361 ++it)
11362 (*it)->i_updateParentMachine(mPeer);
11363
11364 /* attach new data to the primary machine and reshare it */
11365 mPeer->mMediaData.attach(mMediaData);
11366 }
11367
11368 return;
11369}
11370
11371/**
11372 * Perform deferred deletion of implicitly created diffs.
11373 *
11374 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11375 * backed up).
11376 *
11377 * @note Locks this object for writing!
11378 */
11379void Machine::i_rollbackMedia()
11380{
11381 AutoCaller autoCaller(this);
11382 AssertComRCReturnVoid(autoCaller.rc());
11383
11384 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11385 LogFlowThisFunc(("Entering rollbackMedia\n"));
11386
11387 HRESULT rc = S_OK;
11388
11389 /* no attach/detach operations -- nothing to do */
11390 if (!mMediaData.isBackedUp())
11391 return;
11392
11393 /* enumerate new attachments */
11394 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11395 it != mMediaData->mAttachments.end();
11396 ++it)
11397 {
11398 MediumAttachment *pAttach = *it;
11399 /* Fix up the backrefs for DVD/floppy media. */
11400 if (pAttach->i_getType() != DeviceType_HardDisk)
11401 {
11402 Medium* pMedium = pAttach->i_getMedium();
11403 if (pMedium)
11404 {
11405 rc = pMedium->i_removeBackReference(mData->mUuid);
11406 AssertComRC(rc);
11407 }
11408 }
11409
11410 (*it)->i_rollback();
11411
11412 pAttach = *it;
11413 /* Fix up the backrefs for DVD/floppy media. */
11414 if (pAttach->i_getType() != DeviceType_HardDisk)
11415 {
11416 Medium* pMedium = pAttach->i_getMedium();
11417 if (pMedium)
11418 {
11419 rc = pMedium->i_addBackReference(mData->mUuid);
11420 AssertComRC(rc);
11421 }
11422 }
11423 }
11424
11425 /** @todo convert all this Machine-based voodoo to MediumAttachment
11426 * based rollback logic. */
11427 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11428
11429 return;
11430}
11431
11432/**
11433 * Returns true if the settings file is located in the directory named exactly
11434 * as the machine; this means, among other things, that the machine directory
11435 * should be auto-renamed.
11436 *
11437 * @param aSettingsDir if not NULL, the full machine settings file directory
11438 * name will be assigned there.
11439 *
11440 * @note Doesn't lock anything.
11441 * @note Not thread safe (must be called from this object's lock).
11442 */
11443bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11444{
11445 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11446 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11447 if (aSettingsDir)
11448 *aSettingsDir = strMachineDirName;
11449 strMachineDirName.stripPath(); // vmname
11450 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11451 strConfigFileOnly.stripPath() // vmname.vbox
11452 .stripSuffix(); // vmname
11453 /** @todo hack, make somehow use of ComposeMachineFilename */
11454 if (mUserData->s.fDirectoryIncludesUUID)
11455 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11456
11457 AssertReturn(!strMachineDirName.isEmpty(), false);
11458 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11459
11460 return strMachineDirName == strConfigFileOnly;
11461}
11462
11463/**
11464 * Discards all changes to machine settings.
11465 *
11466 * @param aNotify Whether to notify the direct session about changes or not.
11467 *
11468 * @note Locks objects for writing!
11469 */
11470void Machine::i_rollback(bool aNotify)
11471{
11472 AutoCaller autoCaller(this);
11473 AssertComRCReturn(autoCaller.rc(), (void)0);
11474
11475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11476
11477 if (!mStorageControllers.isNull())
11478 {
11479 if (mStorageControllers.isBackedUp())
11480 {
11481 /* unitialize all new devices (absent in the backed up list). */
11482 StorageControllerList::const_iterator it = mStorageControllers->begin();
11483 StorageControllerList *backedList = mStorageControllers.backedUpData();
11484 while (it != mStorageControllers->end())
11485 {
11486 if ( std::find(backedList->begin(), backedList->end(), *it)
11487 == backedList->end()
11488 )
11489 {
11490 (*it)->uninit();
11491 }
11492 ++it;
11493 }
11494
11495 /* restore the list */
11496 mStorageControllers.rollback();
11497 }
11498
11499 /* rollback any changes to devices after restoring the list */
11500 if (mData->flModifications & IsModified_Storage)
11501 {
11502 StorageControllerList::const_iterator it = mStorageControllers->begin();
11503 while (it != mStorageControllers->end())
11504 {
11505 (*it)->i_rollback();
11506 ++it;
11507 }
11508 }
11509 }
11510
11511 if (!mUSBControllers.isNull())
11512 {
11513 if (mUSBControllers.isBackedUp())
11514 {
11515 /* unitialize all new devices (absent in the backed up list). */
11516 USBControllerList::const_iterator it = mUSBControllers->begin();
11517 USBControllerList *backedList = mUSBControllers.backedUpData();
11518 while (it != mUSBControllers->end())
11519 {
11520 if ( std::find(backedList->begin(), backedList->end(), *it)
11521 == backedList->end()
11522 )
11523 {
11524 (*it)->uninit();
11525 }
11526 ++it;
11527 }
11528
11529 /* restore the list */
11530 mUSBControllers.rollback();
11531 }
11532
11533 /* rollback any changes to devices after restoring the list */
11534 if (mData->flModifications & IsModified_USB)
11535 {
11536 USBControllerList::const_iterator it = mUSBControllers->begin();
11537 while (it != mUSBControllers->end())
11538 {
11539 (*it)->i_rollback();
11540 ++it;
11541 }
11542 }
11543 }
11544
11545 mUserData.rollback();
11546
11547 mHWData.rollback();
11548
11549 if (mData->flModifications & IsModified_Storage)
11550 i_rollbackMedia();
11551
11552 if (mBIOSSettings)
11553 mBIOSSettings->i_rollback();
11554
11555 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11556 mVRDEServer->i_rollback();
11557
11558 if (mAudioAdapter)
11559 mAudioAdapter->i_rollback();
11560
11561 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11562 mUSBDeviceFilters->i_rollback();
11563
11564 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11565 mBandwidthControl->i_rollback();
11566
11567 if (!mHWData.isNull())
11568 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11569 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11570 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11571 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11572
11573 if (mData->flModifications & IsModified_NetworkAdapters)
11574 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11575 if ( mNetworkAdapters[slot]
11576 && mNetworkAdapters[slot]->i_isModified())
11577 {
11578 mNetworkAdapters[slot]->i_rollback();
11579 networkAdapters[slot] = mNetworkAdapters[slot];
11580 }
11581
11582 if (mData->flModifications & IsModified_SerialPorts)
11583 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11584 if ( mSerialPorts[slot]
11585 && mSerialPorts[slot]->i_isModified())
11586 {
11587 mSerialPorts[slot]->i_rollback();
11588 serialPorts[slot] = mSerialPorts[slot];
11589 }
11590
11591 if (mData->flModifications & IsModified_ParallelPorts)
11592 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11593 if ( mParallelPorts[slot]
11594 && mParallelPorts[slot]->i_isModified())
11595 {
11596 mParallelPorts[slot]->i_rollback();
11597 parallelPorts[slot] = mParallelPorts[slot];
11598 }
11599
11600 if (aNotify)
11601 {
11602 /* inform the direct session about changes */
11603
11604 ComObjPtr<Machine> that = this;
11605 uint32_t flModifications = mData->flModifications;
11606 alock.release();
11607
11608 if (flModifications & IsModified_SharedFolders)
11609 that->i_onSharedFolderChange();
11610
11611 if (flModifications & IsModified_VRDEServer)
11612 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11613 if (flModifications & IsModified_USB)
11614 that->i_onUSBControllerChange();
11615
11616 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11617 if (networkAdapters[slot])
11618 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11619 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11620 if (serialPorts[slot])
11621 that->i_onSerialPortChange(serialPorts[slot]);
11622 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11623 if (parallelPorts[slot])
11624 that->i_onParallelPortChange(parallelPorts[slot]);
11625
11626 if (flModifications & IsModified_Storage)
11627 that->i_onStorageControllerChange();
11628
11629#if 0
11630 if (flModifications & IsModified_BandwidthControl)
11631 that->onBandwidthControlChange();
11632#endif
11633 }
11634}
11635
11636/**
11637 * Commits all the changes to machine settings.
11638 *
11639 * Note that this operation is supposed to never fail.
11640 *
11641 * @note Locks this object and children for writing.
11642 */
11643void Machine::i_commit()
11644{
11645 AutoCaller autoCaller(this);
11646 AssertComRCReturnVoid(autoCaller.rc());
11647
11648 AutoCaller peerCaller(mPeer);
11649 AssertComRCReturnVoid(peerCaller.rc());
11650
11651 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11652
11653 /*
11654 * use safe commit to ensure Snapshot machines (that share mUserData)
11655 * will still refer to a valid memory location
11656 */
11657 mUserData.commitCopy();
11658
11659 mHWData.commit();
11660
11661 if (mMediaData.isBackedUp())
11662 i_commitMedia(Global::IsOnline(mData->mMachineState));
11663
11664 mBIOSSettings->i_commit();
11665 mVRDEServer->i_commit();
11666 mAudioAdapter->i_commit();
11667 mUSBDeviceFilters->i_commit();
11668 mBandwidthControl->i_commit();
11669
11670 /* Since mNetworkAdapters is a list which might have been changed (resized)
11671 * without using the Backupable<> template we need to handle the copying
11672 * of the list entries manually, including the creation of peers for the
11673 * new objects. */
11674 bool commitNetworkAdapters = false;
11675 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11676 if (mPeer)
11677 {
11678 /* commit everything, even the ones which will go away */
11679 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11680 mNetworkAdapters[slot]->i_commit();
11681 /* copy over the new entries, creating a peer and uninit the original */
11682 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11683 for (size_t slot = 0; slot < newSize; slot++)
11684 {
11685 /* look if this adapter has a peer device */
11686 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11687 if (!peer)
11688 {
11689 /* no peer means the adapter is a newly created one;
11690 * create a peer owning data this data share it with */
11691 peer.createObject();
11692 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11693 }
11694 mPeer->mNetworkAdapters[slot] = peer;
11695 }
11696 /* uninit any no longer needed network adapters */
11697 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11698 mNetworkAdapters[slot]->uninit();
11699 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11700 {
11701 if (mPeer->mNetworkAdapters[slot])
11702 mPeer->mNetworkAdapters[slot]->uninit();
11703 }
11704 /* Keep the original network adapter count until this point, so that
11705 * discarding a chipset type change will not lose settings. */
11706 mNetworkAdapters.resize(newSize);
11707 mPeer->mNetworkAdapters.resize(newSize);
11708 }
11709 else
11710 {
11711 /* we have no peer (our parent is the newly created machine);
11712 * just commit changes to the network adapters */
11713 commitNetworkAdapters = true;
11714 }
11715 if (commitNetworkAdapters)
11716 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11717 mNetworkAdapters[slot]->i_commit();
11718
11719 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11720 mSerialPorts[slot]->i_commit();
11721 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11722 mParallelPorts[slot]->i_commit();
11723
11724 bool commitStorageControllers = false;
11725
11726 if (mStorageControllers.isBackedUp())
11727 {
11728 mStorageControllers.commit();
11729
11730 if (mPeer)
11731 {
11732 /* Commit all changes to new controllers (this will reshare data with
11733 * peers for those who have peers) */
11734 StorageControllerList *newList = new StorageControllerList();
11735 StorageControllerList::const_iterator it = mStorageControllers->begin();
11736 while (it != mStorageControllers->end())
11737 {
11738 (*it)->i_commit();
11739
11740 /* look if this controller has a peer device */
11741 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11742 if (!peer)
11743 {
11744 /* no peer means the device is a newly created one;
11745 * create a peer owning data this device share it with */
11746 peer.createObject();
11747 peer->init(mPeer, *it, true /* aReshare */);
11748 }
11749 else
11750 {
11751 /* remove peer from the old list */
11752 mPeer->mStorageControllers->remove(peer);
11753 }
11754 /* and add it to the new list */
11755 newList->push_back(peer);
11756
11757 ++it;
11758 }
11759
11760 /* uninit old peer's controllers that are left */
11761 it = mPeer->mStorageControllers->begin();
11762 while (it != mPeer->mStorageControllers->end())
11763 {
11764 (*it)->uninit();
11765 ++it;
11766 }
11767
11768 /* attach new list of controllers to our peer */
11769 mPeer->mStorageControllers.attach(newList);
11770 }
11771 else
11772 {
11773 /* we have no peer (our parent is the newly created machine);
11774 * just commit changes to devices */
11775 commitStorageControllers = true;
11776 }
11777 }
11778 else
11779 {
11780 /* the list of controllers itself is not changed,
11781 * just commit changes to controllers themselves */
11782 commitStorageControllers = true;
11783 }
11784
11785 if (commitStorageControllers)
11786 {
11787 StorageControllerList::const_iterator it = mStorageControllers->begin();
11788 while (it != mStorageControllers->end())
11789 {
11790 (*it)->i_commit();
11791 ++it;
11792 }
11793 }
11794
11795 bool commitUSBControllers = false;
11796
11797 if (mUSBControllers.isBackedUp())
11798 {
11799 mUSBControllers.commit();
11800
11801 if (mPeer)
11802 {
11803 /* Commit all changes to new controllers (this will reshare data with
11804 * peers for those who have peers) */
11805 USBControllerList *newList = new USBControllerList();
11806 USBControllerList::const_iterator it = mUSBControllers->begin();
11807 while (it != mUSBControllers->end())
11808 {
11809 (*it)->i_commit();
11810
11811 /* look if this controller has a peer device */
11812 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11813 if (!peer)
11814 {
11815 /* no peer means the device is a newly created one;
11816 * create a peer owning data this device share it with */
11817 peer.createObject();
11818 peer->init(mPeer, *it, true /* aReshare */);
11819 }
11820 else
11821 {
11822 /* remove peer from the old list */
11823 mPeer->mUSBControllers->remove(peer);
11824 }
11825 /* and add it to the new list */
11826 newList->push_back(peer);
11827
11828 ++it;
11829 }
11830
11831 /* uninit old peer's controllers that are left */
11832 it = mPeer->mUSBControllers->begin();
11833 while (it != mPeer->mUSBControllers->end())
11834 {
11835 (*it)->uninit();
11836 ++it;
11837 }
11838
11839 /* attach new list of controllers to our peer */
11840 mPeer->mUSBControllers.attach(newList);
11841 }
11842 else
11843 {
11844 /* we have no peer (our parent is the newly created machine);
11845 * just commit changes to devices */
11846 commitUSBControllers = true;
11847 }
11848 }
11849 else
11850 {
11851 /* the list of controllers itself is not changed,
11852 * just commit changes to controllers themselves */
11853 commitUSBControllers = true;
11854 }
11855
11856 if (commitUSBControllers)
11857 {
11858 USBControllerList::const_iterator it = mUSBControllers->begin();
11859 while (it != mUSBControllers->end())
11860 {
11861 (*it)->i_commit();
11862 ++it;
11863 }
11864 }
11865
11866 if (i_isSessionMachine())
11867 {
11868 /* attach new data to the primary machine and reshare it */
11869 mPeer->mUserData.attach(mUserData);
11870 mPeer->mHWData.attach(mHWData);
11871 /* mMediaData is reshared by fixupMedia */
11872 // mPeer->mMediaData.attach(mMediaData);
11873 Assert(mPeer->mMediaData.data() == mMediaData.data());
11874 }
11875}
11876
11877/**
11878 * Copies all the hardware data from the given machine.
11879 *
11880 * Currently, only called when the VM is being restored from a snapshot. In
11881 * particular, this implies that the VM is not running during this method's
11882 * call.
11883 *
11884 * @note This method must be called from under this object's lock.
11885 *
11886 * @note This method doesn't call #commit(), so all data remains backed up and
11887 * unsaved.
11888 */
11889void Machine::i_copyFrom(Machine *aThat)
11890{
11891 AssertReturnVoid(!i_isSnapshotMachine());
11892 AssertReturnVoid(aThat->i_isSnapshotMachine());
11893
11894 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11895
11896 mHWData.assignCopy(aThat->mHWData);
11897
11898 // create copies of all shared folders (mHWData after attaching a copy
11899 // contains just references to original objects)
11900 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11901 it != mHWData->mSharedFolders.end();
11902 ++it)
11903 {
11904 ComObjPtr<SharedFolder> folder;
11905 folder.createObject();
11906 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11907 AssertComRC(rc);
11908 *it = folder;
11909 }
11910
11911 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11912 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11913 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11914 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11915 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11916
11917 /* create private copies of all controllers */
11918 mStorageControllers.backup();
11919 mStorageControllers->clear();
11920 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11921 it != aThat->mStorageControllers->end();
11922 ++it)
11923 {
11924 ComObjPtr<StorageController> ctrl;
11925 ctrl.createObject();
11926 ctrl->initCopy(this, *it);
11927 mStorageControllers->push_back(ctrl);
11928 }
11929
11930 /* create private copies of all USB controllers */
11931 mUSBControllers.backup();
11932 mUSBControllers->clear();
11933 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
11934 it != aThat->mUSBControllers->end();
11935 ++it)
11936 {
11937 ComObjPtr<USBController> ctrl;
11938 ctrl.createObject();
11939 ctrl->initCopy(this, *it);
11940 mUSBControllers->push_back(ctrl);
11941 }
11942
11943 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11944 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11945 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
11946 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11947 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
11948 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11949 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
11950}
11951
11952/**
11953 * Returns whether the given storage controller is hotplug capable.
11954 *
11955 * @returns true if the controller supports hotplugging
11956 * false otherwise.
11957 * @param enmCtrlType The controller type to check for.
11958 */
11959bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11960{
11961 ComPtr<ISystemProperties> systemProperties;
11962 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
11963 if (FAILED(rc))
11964 return false;
11965
11966 BOOL aHotplugCapable = FALSE;
11967 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
11968
11969 return RT_BOOL(aHotplugCapable);
11970}
11971
11972#ifdef VBOX_WITH_RESOURCE_USAGE_API
11973
11974void Machine::i_getDiskList(MediaList &list)
11975{
11976 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11977 it != mMediaData->mAttachments.end();
11978 ++it)
11979 {
11980 MediumAttachment* pAttach = *it;
11981 /* just in case */
11982 AssertStmt(pAttach, continue);
11983
11984 AutoCaller localAutoCallerA(pAttach);
11985 if (FAILED(localAutoCallerA.rc())) continue;
11986
11987 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11988
11989 if (pAttach->i_getType() == DeviceType_HardDisk)
11990 list.push_back(pAttach->i_getMedium());
11991 }
11992}
11993
11994void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11995{
11996 AssertReturnVoid(isWriteLockOnCurrentThread());
11997 AssertPtrReturnVoid(aCollector);
11998
11999 pm::CollectorHAL *hal = aCollector->getHAL();
12000 /* Create sub metrics */
12001 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12002 "Percentage of processor time spent in user mode by the VM process.");
12003 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12004 "Percentage of processor time spent in kernel mode by the VM process.");
12005 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12006 "Size of resident portion of VM process in memory.");
12007 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12008 "Actual size of all VM disks combined.");
12009 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12010 "Network receive rate.");
12011 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12012 "Network transmit rate.");
12013 /* Create and register base metrics */
12014 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12015 cpuLoadUser, cpuLoadKernel);
12016 aCollector->registerBaseMetric(cpuLoad);
12017 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12018 ramUsageUsed);
12019 aCollector->registerBaseMetric(ramUsage);
12020 MediaList disks;
12021 i_getDiskList(disks);
12022 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12023 diskUsageUsed);
12024 aCollector->registerBaseMetric(diskUsage);
12025
12026 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12027 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12028 new pm::AggregateAvg()));
12029 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12030 new pm::AggregateMin()));
12031 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12032 new pm::AggregateMax()));
12033 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12034 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12035 new pm::AggregateAvg()));
12036 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12037 new pm::AggregateMin()));
12038 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12039 new pm::AggregateMax()));
12040
12041 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12042 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12043 new pm::AggregateAvg()));
12044 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12045 new pm::AggregateMin()));
12046 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12047 new pm::AggregateMax()));
12048
12049 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12050 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12051 new pm::AggregateAvg()));
12052 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12053 new pm::AggregateMin()));
12054 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12055 new pm::AggregateMax()));
12056
12057
12058 /* Guest metrics collector */
12059 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12060 aCollector->registerGuest(mCollectorGuest);
12061 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12062 this, __PRETTY_FUNCTION__, mCollectorGuest));
12063
12064 /* Create sub metrics */
12065 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12066 "Percentage of processor time spent in user mode as seen by the guest.");
12067 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12068 "Percentage of processor time spent in kernel mode as seen by the guest.");
12069 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12070 "Percentage of processor time spent idling as seen by the guest.");
12071
12072 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12073 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12074 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12075 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12076 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12077 pm::SubMetric *guestMemCache = new pm::SubMetric(
12078 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12079
12080 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12081 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12082
12083 /* Create and register base metrics */
12084 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12085 machineNetRx, machineNetTx);
12086 aCollector->registerBaseMetric(machineNetRate);
12087
12088 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12089 guestLoadUser, guestLoadKernel, guestLoadIdle);
12090 aCollector->registerBaseMetric(guestCpuLoad);
12091
12092 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12093 guestMemTotal, guestMemFree,
12094 guestMemBalloon, guestMemShared,
12095 guestMemCache, guestPagedTotal);
12096 aCollector->registerBaseMetric(guestCpuMem);
12097
12098 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12099 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12100 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12101 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12102
12103 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12104 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12105 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12106 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12107
12108 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12109 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12110 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12111 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12112
12113 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12114 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12115 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12116 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12117
12118 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12119 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12120 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12121 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12122
12123 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12124 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12125 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12126 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12127
12128 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12129 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12130 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12131 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12132
12133 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12134 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12135 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12136 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12137
12138 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12139 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12140 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12141 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12142
12143 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12144 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12145 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12146 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12147
12148 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12149 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12150 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12151 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12152}
12153
12154void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12155{
12156 AssertReturnVoid(isWriteLockOnCurrentThread());
12157
12158 if (aCollector)
12159 {
12160 aCollector->unregisterMetricsFor(aMachine);
12161 aCollector->unregisterBaseMetricsFor(aMachine);
12162 }
12163}
12164
12165#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12166
12167
12168////////////////////////////////////////////////////////////////////////////////
12169
12170DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12171
12172HRESULT SessionMachine::FinalConstruct()
12173{
12174 LogFlowThisFunc(("\n"));
12175
12176 mClientToken = NULL;
12177
12178 return BaseFinalConstruct();
12179}
12180
12181void SessionMachine::FinalRelease()
12182{
12183 LogFlowThisFunc(("\n"));
12184
12185 Assert(!mClientToken);
12186 /* paranoia, should not hang around any more */
12187 if (mClientToken)
12188 {
12189 delete mClientToken;
12190 mClientToken = NULL;
12191 }
12192
12193 uninit(Uninit::Unexpected);
12194
12195 BaseFinalRelease();
12196}
12197
12198/**
12199 * @note Must be called only by Machine::LockMachine() from its own write lock.
12200 */
12201HRESULT SessionMachine::init(Machine *aMachine)
12202{
12203 LogFlowThisFuncEnter();
12204 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12205
12206 AssertReturn(aMachine, E_INVALIDARG);
12207
12208 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12209
12210 /* Enclose the state transition NotReady->InInit->Ready */
12211 AutoInitSpan autoInitSpan(this);
12212 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12213
12214 HRESULT rc = S_OK;
12215
12216 /* create the machine client token */
12217 try
12218 {
12219 mClientToken = new ClientToken(aMachine, this);
12220 if (!mClientToken->isReady())
12221 {
12222 delete mClientToken;
12223 mClientToken = NULL;
12224 rc = E_FAIL;
12225 }
12226 }
12227 catch (std::bad_alloc &)
12228 {
12229 rc = E_OUTOFMEMORY;
12230 }
12231 if (FAILED(rc))
12232 return rc;
12233
12234 /* memorize the peer Machine */
12235 unconst(mPeer) = aMachine;
12236 /* share the parent pointer */
12237 unconst(mParent) = aMachine->mParent;
12238
12239 /* take the pointers to data to share */
12240 mData.share(aMachine->mData);
12241 mSSData.share(aMachine->mSSData);
12242
12243 mUserData.share(aMachine->mUserData);
12244 mHWData.share(aMachine->mHWData);
12245 mMediaData.share(aMachine->mMediaData);
12246
12247 mStorageControllers.allocate();
12248 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12249 it != aMachine->mStorageControllers->end();
12250 ++it)
12251 {
12252 ComObjPtr<StorageController> ctl;
12253 ctl.createObject();
12254 ctl->init(this, *it);
12255 mStorageControllers->push_back(ctl);
12256 }
12257
12258 mUSBControllers.allocate();
12259 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12260 it != aMachine->mUSBControllers->end();
12261 ++it)
12262 {
12263 ComObjPtr<USBController> ctl;
12264 ctl.createObject();
12265 ctl->init(this, *it);
12266 mUSBControllers->push_back(ctl);
12267 }
12268
12269 unconst(mBIOSSettings).createObject();
12270 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12271 /* create another VRDEServer object that will be mutable */
12272 unconst(mVRDEServer).createObject();
12273 mVRDEServer->init(this, aMachine->mVRDEServer);
12274 /* create another audio adapter object that will be mutable */
12275 unconst(mAudioAdapter).createObject();
12276 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12277 /* create a list of serial ports that will be mutable */
12278 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12279 {
12280 unconst(mSerialPorts[slot]).createObject();
12281 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12282 }
12283 /* create a list of parallel ports that will be mutable */
12284 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12285 {
12286 unconst(mParallelPorts[slot]).createObject();
12287 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12288 }
12289
12290 /* create another USB device filters object that will be mutable */
12291 unconst(mUSBDeviceFilters).createObject();
12292 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12293
12294 /* create a list of network adapters that will be mutable */
12295 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12296 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12297 {
12298 unconst(mNetworkAdapters[slot]).createObject();
12299 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12300 }
12301
12302 /* create another bandwidth control object that will be mutable */
12303 unconst(mBandwidthControl).createObject();
12304 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12305
12306 /* default is to delete saved state on Saved -> PoweredOff transition */
12307 mRemoveSavedState = true;
12308
12309 /* Confirm a successful initialization when it's the case */
12310 autoInitSpan.setSucceeded();
12311
12312 miNATNetworksStarted = 0;
12313
12314 LogFlowThisFuncLeave();
12315 return rc;
12316}
12317
12318/**
12319 * Uninitializes this session object. If the reason is other than
12320 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12321 * or the client watcher code.
12322 *
12323 * @param aReason uninitialization reason
12324 *
12325 * @note Locks mParent + this object for writing.
12326 */
12327void SessionMachine::uninit(Uninit::Reason aReason)
12328{
12329 LogFlowThisFuncEnter();
12330 LogFlowThisFunc(("reason=%d\n", aReason));
12331
12332 /*
12333 * Strongly reference ourselves to prevent this object deletion after
12334 * mData->mSession.mMachine.setNull() below (which can release the last
12335 * reference and call the destructor). Important: this must be done before
12336 * accessing any members (and before AutoUninitSpan that does it as well).
12337 * This self reference will be released as the very last step on return.
12338 */
12339 ComObjPtr<SessionMachine> selfRef = this;
12340
12341 /* Enclose the state transition Ready->InUninit->NotReady */
12342 AutoUninitSpan autoUninitSpan(this);
12343 if (autoUninitSpan.uninitDone())
12344 {
12345 LogFlowThisFunc(("Already uninitialized\n"));
12346 LogFlowThisFuncLeave();
12347 return;
12348 }
12349
12350 if (autoUninitSpan.initFailed())
12351 {
12352 /* We've been called by init() because it's failed. It's not really
12353 * necessary (nor it's safe) to perform the regular uninit sequence
12354 * below, the following is enough.
12355 */
12356 LogFlowThisFunc(("Initialization failed.\n"));
12357 /* destroy the machine client token */
12358 if (mClientToken)
12359 {
12360 delete mClientToken;
12361 mClientToken = NULL;
12362 }
12363 uninitDataAndChildObjects();
12364 mData.free();
12365 unconst(mParent) = NULL;
12366 unconst(mPeer) = NULL;
12367 LogFlowThisFuncLeave();
12368 return;
12369 }
12370
12371 MachineState_T lastState;
12372 {
12373 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12374 lastState = mData->mMachineState;
12375 }
12376 NOREF(lastState);
12377
12378#ifdef VBOX_WITH_USB
12379 // release all captured USB devices, but do this before requesting the locks below
12380 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12381 {
12382 /* Console::captureUSBDevices() is called in the VM process only after
12383 * setting the machine state to Starting or Restoring.
12384 * Console::detachAllUSBDevices() will be called upon successful
12385 * termination. So, we need to release USB devices only if there was
12386 * an abnormal termination of a running VM.
12387 *
12388 * This is identical to SessionMachine::DetachAllUSBDevices except
12389 * for the aAbnormal argument. */
12390 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12391 AssertComRC(rc);
12392 NOREF(rc);
12393
12394 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12395 if (service)
12396 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12397 }
12398#endif /* VBOX_WITH_USB */
12399
12400 // we need to lock this object in uninit() because the lock is shared
12401 // with mPeer (as well as data we modify below). mParent lock is needed
12402 // by several calls to it, and USB needs host lock.
12403 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12404
12405#ifdef VBOX_WITH_RESOURCE_USAGE_API
12406 /*
12407 * It is safe to call Machine::i_unregisterMetrics() here because
12408 * PerformanceCollector::samplerCallback no longer accesses guest methods
12409 * holding the lock.
12410 */
12411 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12412 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12413 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12414 this, __PRETTY_FUNCTION__, mCollectorGuest));
12415 if (mCollectorGuest)
12416 {
12417 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12418 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12419 mCollectorGuest = NULL;
12420 }
12421#endif
12422
12423 if (aReason == Uninit::Abnormal)
12424 {
12425 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12426 Global::IsOnlineOrTransient(lastState)));
12427
12428 /* reset the state to Aborted */
12429 if (mData->mMachineState != MachineState_Aborted)
12430 i_setMachineState(MachineState_Aborted);
12431 }
12432
12433 // any machine settings modified?
12434 if (mData->flModifications)
12435 {
12436 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12437 i_rollback(false /* aNotify */);
12438 }
12439
12440 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12441 || !mConsoleTaskData.mSnapshot);
12442 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12443 {
12444 LogWarningThisFunc(("canceling failed save state request!\n"));
12445 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12446 }
12447 else if (!mConsoleTaskData.mSnapshot.isNull())
12448 {
12449 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12450
12451 /* delete all differencing hard disks created (this will also attach
12452 * their parents back by rolling back mMediaData) */
12453 i_rollbackMedia();
12454
12455 // delete the saved state file (it might have been already created)
12456 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12457 // think it's still in use
12458 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
12459 mConsoleTaskData.mSnapshot->uninit();
12460 i_releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12461 }
12462
12463 mData->mSession.mPID = NIL_RTPROCESS;
12464
12465 if (aReason == Uninit::Unexpected)
12466 {
12467 /* Uninitialization didn't come from #checkForDeath(), so tell the
12468 * client watcher thread to update the set of machines that have open
12469 * sessions. */
12470 mParent->i_updateClientWatcher();
12471 }
12472
12473 /* uninitialize all remote controls */
12474 if (mData->mSession.mRemoteControls.size())
12475 {
12476 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12477 mData->mSession.mRemoteControls.size()));
12478
12479 Data::Session::RemoteControlList::iterator it =
12480 mData->mSession.mRemoteControls.begin();
12481 while (it != mData->mSession.mRemoteControls.end())
12482 {
12483 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12484 HRESULT rc = (*it)->Uninitialize();
12485 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12486 if (FAILED(rc))
12487 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12488 ++it;
12489 }
12490 mData->mSession.mRemoteControls.clear();
12491 }
12492
12493 /* Remove all references to the NAT network service. The service will stop
12494 * if all references (also from other VMs) are removed. */
12495 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12496 {
12497 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12498 {
12499 NetworkAttachmentType_T type;
12500 HRESULT hrc;
12501
12502 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12503 if ( SUCCEEDED(hrc)
12504 && type == NetworkAttachmentType_NATNetwork)
12505 {
12506 Bstr name;
12507 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12508 if (SUCCEEDED(hrc))
12509 {
12510 multilock.release();
12511 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12512 mUserData->s.strName.c_str(), name.raw()));
12513 mParent->i_natNetworkRefDec(name.raw());
12514 multilock.acquire();
12515 }
12516 }
12517 }
12518 }
12519
12520 /*
12521 * An expected uninitialization can come only from #checkForDeath().
12522 * Otherwise it means that something's gone really wrong (for example,
12523 * the Session implementation has released the VirtualBox reference
12524 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12525 * etc). However, it's also possible, that the client releases the IPC
12526 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12527 * but the VirtualBox release event comes first to the server process.
12528 * This case is practically possible, so we should not assert on an
12529 * unexpected uninit, just log a warning.
12530 */
12531
12532 if ((aReason == Uninit::Unexpected))
12533 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12534
12535 if (aReason != Uninit::Normal)
12536 {
12537 mData->mSession.mDirectControl.setNull();
12538 }
12539 else
12540 {
12541 /* this must be null here (see #OnSessionEnd()) */
12542 Assert(mData->mSession.mDirectControl.isNull());
12543 Assert(mData->mSession.mState == SessionState_Unlocking);
12544 Assert(!mData->mSession.mProgress.isNull());
12545 }
12546 if (mData->mSession.mProgress)
12547 {
12548 if (aReason == Uninit::Normal)
12549 mData->mSession.mProgress->i_notifyComplete(S_OK);
12550 else
12551 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12552 COM_IIDOF(ISession),
12553 getComponentName(),
12554 tr("The VM session was aborted"));
12555 mData->mSession.mProgress.setNull();
12556 }
12557
12558 /* remove the association between the peer machine and this session machine */
12559 Assert( (SessionMachine*)mData->mSession.mMachine == this
12560 || aReason == Uninit::Unexpected);
12561
12562 /* reset the rest of session data */
12563 mData->mSession.mMachine.setNull();
12564 mData->mSession.mState = SessionState_Unlocked;
12565 mData->mSession.mType.setNull();
12566
12567 /* destroy the machine client token before leaving the exclusive lock */
12568 if (mClientToken)
12569 {
12570 delete mClientToken;
12571 mClientToken = NULL;
12572 }
12573
12574 /* fire an event */
12575 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12576
12577 uninitDataAndChildObjects();
12578
12579 /* free the essential data structure last */
12580 mData.free();
12581
12582 /* release the exclusive lock before setting the below two to NULL */
12583 multilock.release();
12584
12585 unconst(mParent) = NULL;
12586 unconst(mPeer) = NULL;
12587
12588 LogFlowThisFuncLeave();
12589}
12590
12591// util::Lockable interface
12592////////////////////////////////////////////////////////////////////////////////
12593
12594/**
12595 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12596 * with the primary Machine instance (mPeer).
12597 */
12598RWLockHandle *SessionMachine::lockHandle() const
12599{
12600 AssertReturn(mPeer != NULL, NULL);
12601 return mPeer->lockHandle();
12602}
12603
12604// IInternalMachineControl methods
12605////////////////////////////////////////////////////////////////////////////////
12606
12607/**
12608 * Passes collected guest statistics to performance collector object
12609 */
12610HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12611 ULONG aCpuKernel, ULONG aCpuIdle,
12612 ULONG aMemTotal, ULONG aMemFree,
12613 ULONG aMemBalloon, ULONG aMemShared,
12614 ULONG aMemCache, ULONG aPageTotal,
12615 ULONG aAllocVMM, ULONG aFreeVMM,
12616 ULONG aBalloonedVMM, ULONG aSharedVMM,
12617 ULONG aVmNetRx, ULONG aVmNetTx)
12618{
12619#ifdef VBOX_WITH_RESOURCE_USAGE_API
12620 if (mCollectorGuest)
12621 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12622 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12623 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12624 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12625
12626 return S_OK;
12627#else
12628 NOREF(aValidStats);
12629 NOREF(aCpuUser);
12630 NOREF(aCpuKernel);
12631 NOREF(aCpuIdle);
12632 NOREF(aMemTotal);
12633 NOREF(aMemFree);
12634 NOREF(aMemBalloon);
12635 NOREF(aMemShared);
12636 NOREF(aMemCache);
12637 NOREF(aPageTotal);
12638 NOREF(aAllocVMM);
12639 NOREF(aFreeVMM);
12640 NOREF(aBalloonedVMM);
12641 NOREF(aSharedVMM);
12642 NOREF(aVmNetRx);
12643 NOREF(aVmNetTx);
12644 return E_NOTIMPL;
12645#endif
12646}
12647
12648/**
12649 * @note Locks this object for writing.
12650 */
12651HRESULT SessionMachine::setRemoveSavedStateFile(BOOL aRemove)
12652{
12653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12654
12655 mRemoveSavedState = RT_BOOL(aRemove);
12656
12657 return S_OK;
12658}
12659
12660/**
12661 * @note Locks the same as #i_setMachineState() does.
12662 */
12663HRESULT SessionMachine::updateState(MachineState_T aState)
12664{
12665 return i_setMachineState(aState);
12666}
12667
12668/**
12669 * @note Locks this object for writing.
12670 */
12671HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12672{
12673 IProgress* pProgress(aProgress);
12674
12675 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12676
12677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12678
12679 if (mData->mSession.mState != SessionState_Locked)
12680 return VBOX_E_INVALID_OBJECT_STATE;
12681
12682 if (!mData->mSession.mProgress.isNull())
12683 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12684
12685 /* If we didn't reference the NAT network service yet, add a reference to
12686 * force a start */
12687 if (miNATNetworksStarted < 1)
12688 {
12689 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12690 {
12691 NetworkAttachmentType_T type;
12692 HRESULT hrc;
12693 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12694 if ( SUCCEEDED(hrc)
12695 && type == NetworkAttachmentType_NATNetwork)
12696 {
12697 Bstr name;
12698 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12699 if (SUCCEEDED(hrc))
12700 {
12701 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12702 mUserData->s.strName.c_str(), name.raw()));
12703 mPeer->lockHandle()->unlockWrite();
12704 mParent->i_natNetworkRefInc(name.raw());
12705#ifdef RT_LOCK_STRICT
12706 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12707#else
12708 mPeer->lockHandle()->lockWrite();
12709#endif
12710 }
12711 }
12712 }
12713 miNATNetworksStarted++;
12714 }
12715
12716 LogFlowThisFunc(("returns S_OK.\n"));
12717 return S_OK;
12718}
12719
12720/**
12721 * @note Locks this object for writing.
12722 */
12723HRESULT SessionMachine::endPowerUp(LONG aResult)
12724{
12725 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12726
12727 if (mData->mSession.mState != SessionState_Locked)
12728 return VBOX_E_INVALID_OBJECT_STATE;
12729
12730 /* Finalize the LaunchVMProcess progress object. */
12731 if (mData->mSession.mProgress)
12732 {
12733 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
12734 mData->mSession.mProgress.setNull();
12735 }
12736
12737 if (SUCCEEDED((HRESULT)aResult))
12738 {
12739#ifdef VBOX_WITH_RESOURCE_USAGE_API
12740 /* The VM has been powered up successfully, so it makes sense
12741 * now to offer the performance metrics for a running machine
12742 * object. Doing it earlier wouldn't be safe. */
12743 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
12744 mData->mSession.mPID);
12745#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12746 }
12747
12748 return S_OK;
12749}
12750
12751/**
12752 * @note Locks this object for writing.
12753 */
12754HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
12755{
12756 LogFlowThisFuncEnter();
12757
12758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12759
12760 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12761 E_FAIL);
12762
12763 /* create a progress object to track operation completion */
12764 ComObjPtr<Progress> pProgress;
12765 pProgress.createObject();
12766 pProgress->init(i_getVirtualBox(),
12767 static_cast<IMachine *>(this) /* aInitiator */,
12768 Bstr(tr("Stopping the virtual machine")).raw(),
12769 FALSE /* aCancelable */);
12770
12771 /* fill in the console task data */
12772 mConsoleTaskData.mLastState = mData->mMachineState;
12773 mConsoleTaskData.mProgress = pProgress;
12774
12775 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12776 i_setMachineState(MachineState_Stopping);
12777
12778 pProgress.queryInterfaceTo(aProgress.asOutParam());
12779
12780 return S_OK;
12781}
12782
12783/**
12784 * @note Locks this object for writing.
12785 */
12786HRESULT SessionMachine::endPoweringDown(LONG aResult,
12787 const com::Utf8Str &aErrMsg)
12788{
12789 LogFlowThisFuncEnter();
12790
12791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12792
12793 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
12794 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
12795 && mConsoleTaskData.mLastState != MachineState_Null,
12796 E_FAIL);
12797
12798 /*
12799 * On failure, set the state to the state we had when BeginPoweringDown()
12800 * was called (this is expected by Console::PowerDown() and the associated
12801 * task). On success the VM process already changed the state to
12802 * MachineState_PoweredOff, so no need to do anything.
12803 */
12804 if (FAILED(aResult))
12805 i_setMachineState(mConsoleTaskData.mLastState);
12806
12807 /* notify the progress object about operation completion */
12808 Assert(mConsoleTaskData.mProgress);
12809 if (SUCCEEDED(aResult))
12810 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
12811 else
12812 {
12813 if (aErrMsg.length())
12814 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
12815 COM_IIDOF(ISession),
12816 getComponentName(),
12817 aErrMsg.c_str());
12818 else
12819 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
12820 }
12821
12822 /* clear out the temporary saved state data */
12823 mConsoleTaskData.mLastState = MachineState_Null;
12824 mConsoleTaskData.mProgress.setNull();
12825
12826 LogFlowThisFuncLeave();
12827 return S_OK;
12828}
12829
12830
12831/**
12832 * Goes through the USB filters of the given machine to see if the given
12833 * device matches any filter or not.
12834 *
12835 * @note Locks the same as USBController::hasMatchingFilter() does.
12836 */
12837HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
12838 BOOL *aMatched,
12839 ULONG *aMaskedInterfaces)
12840{
12841 LogFlowThisFunc(("\n"));
12842
12843#ifdef VBOX_WITH_USB
12844 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
12845#else
12846 NOREF(aDevice);
12847 NOREF(aMaskedInterfaces);
12848 *aMatched = FALSE;
12849#endif
12850
12851 return S_OK;
12852}
12853
12854/**
12855 * @note Locks the same as Host::captureUSBDevice() does.
12856 */
12857HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
12858{
12859 LogFlowThisFunc(("\n"));
12860
12861#ifdef VBOX_WITH_USB
12862 /* if captureDeviceForVM() fails, it must have set extended error info */
12863 clearError();
12864 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
12865 if (FAILED(rc)) return rc;
12866
12867 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12868 AssertReturn(service, E_FAIL);
12869 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
12870#else
12871 NOREF(aId);
12872 return E_NOTIMPL;
12873#endif
12874}
12875
12876/**
12877 * @note Locks the same as Host::detachUSBDevice() does.
12878 */
12879HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
12880 BOOL aDone)
12881{
12882 LogFlowThisFunc(("\n"));
12883
12884#ifdef VBOX_WITH_USB
12885 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12886 AssertReturn(service, E_FAIL);
12887 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
12888#else
12889 NOREF(aId);
12890 NOREF(aDone);
12891 return E_NOTIMPL;
12892#endif
12893}
12894
12895/**
12896 * Inserts all machine filters to the USB proxy service and then calls
12897 * Host::autoCaptureUSBDevices().
12898 *
12899 * Called by Console from the VM process upon VM startup.
12900 *
12901 * @note Locks what called methods lock.
12902 */
12903HRESULT SessionMachine::autoCaptureUSBDevices()
12904{
12905 LogFlowThisFunc(("\n"));
12906
12907#ifdef VBOX_WITH_USB
12908 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
12909 AssertComRC(rc);
12910 NOREF(rc);
12911
12912 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12913 AssertReturn(service, E_FAIL);
12914 return service->autoCaptureDevicesForVM(this);
12915#else
12916 return S_OK;
12917#endif
12918}
12919
12920/**
12921 * Removes all machine filters from the USB proxy service and then calls
12922 * Host::detachAllUSBDevices().
12923 *
12924 * Called by Console from the VM process upon normal VM termination or by
12925 * SessionMachine::uninit() upon abnormal VM termination (from under the
12926 * Machine/SessionMachine lock).
12927 *
12928 * @note Locks what called methods lock.
12929 */
12930HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
12931{
12932 LogFlowThisFunc(("\n"));
12933
12934#ifdef VBOX_WITH_USB
12935 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12936 AssertComRC(rc);
12937 NOREF(rc);
12938
12939 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12940 AssertReturn(service, E_FAIL);
12941 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12942#else
12943 NOREF(aDone);
12944 return S_OK;
12945#endif
12946}
12947
12948/**
12949 * @note Locks this object for writing.
12950 */
12951HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
12952 ComPtr<IProgress> &aProgress)
12953{
12954 LogFlowThisFuncEnter();
12955
12956 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
12957 /*
12958 * We don't assert below because it might happen that a non-direct session
12959 * informs us it is closed right after we've been uninitialized -- it's ok.
12960 */
12961
12962 /* get IInternalSessionControl interface */
12963 ComPtr<IInternalSessionControl> control(aSession);
12964
12965 ComAssertRet(!control.isNull(), E_INVALIDARG);
12966
12967 /* Creating a Progress object requires the VirtualBox lock, and
12968 * thus locking it here is required by the lock order rules. */
12969 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12970
12971 if (control == mData->mSession.mDirectControl)
12972 {
12973 /* The direct session is being normally closed by the client process
12974 * ----------------------------------------------------------------- */
12975
12976 /* go to the closing state (essential for all open*Session() calls and
12977 * for #checkForDeath()) */
12978 Assert(mData->mSession.mState == SessionState_Locked);
12979 mData->mSession.mState = SessionState_Unlocking;
12980
12981 /* set direct control to NULL to release the remote instance */
12982 mData->mSession.mDirectControl.setNull();
12983 LogFlowThisFunc(("Direct control is set to NULL\n"));
12984
12985 if (mData->mSession.mProgress)
12986 {
12987 /* finalize the progress, someone might wait if a frontend
12988 * closes the session before powering on the VM. */
12989 mData->mSession.mProgress->notifyComplete(E_FAIL,
12990 COM_IIDOF(ISession),
12991 getComponentName(),
12992 tr("The VM session was closed before any attempt to power it on"));
12993 mData->mSession.mProgress.setNull();
12994 }
12995
12996 /* Create the progress object the client will use to wait until
12997 * #checkForDeath() is called to uninitialize this session object after
12998 * it releases the IPC semaphore.
12999 * Note! Because we're "reusing" mProgress here, this must be a proxy
13000 * object just like for LaunchVMProcess. */
13001 Assert(mData->mSession.mProgress.isNull());
13002 ComObjPtr<ProgressProxy> progress;
13003 progress.createObject();
13004 ComPtr<IUnknown> pPeer(mPeer);
13005 progress->init(mParent, pPeer,
13006 Bstr(tr("Closing session")).raw(),
13007 FALSE /* aCancelable */);
13008 progress.queryInterfaceTo(aProgress.asOutParam());
13009 mData->mSession.mProgress = progress;
13010 }
13011 else
13012 {
13013 /* the remote session is being normally closed */
13014 Data::Session::RemoteControlList::iterator it =
13015 mData->mSession.mRemoteControls.begin();
13016 while (it != mData->mSession.mRemoteControls.end())
13017 {
13018 if (control == *it)
13019 break;
13020 ++it;
13021 }
13022 BOOL found = it != mData->mSession.mRemoteControls.end();
13023 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13024 E_INVALIDARG);
13025 // This MUST be erase(it), not remove(*it) as the latter triggers a
13026 // very nasty use after free due to the place where the value "lives".
13027 mData->mSession.mRemoteControls.erase(it);
13028 }
13029
13030 /* signal the client watcher thread, because the client is going away */
13031 mParent->i_updateClientWatcher();
13032
13033 LogFlowThisFuncLeave();
13034 return S_OK;
13035}
13036
13037/**
13038 * @note Locks this object for writing.
13039 */
13040HRESULT SessionMachine::beginSavingState(ComPtr<IProgress> &aProgress,
13041 com::Utf8Str &aStateFilePath)
13042{
13043 LogFlowThisFuncEnter();
13044
13045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13046
13047 AssertReturn( mData->mMachineState == MachineState_Paused
13048 && mConsoleTaskData.mLastState == MachineState_Null
13049 && mConsoleTaskData.strStateFilePath.isEmpty(),
13050 E_FAIL);
13051
13052 /* create a progress object to track operation completion */
13053 ComObjPtr<Progress> pProgress;
13054 pProgress.createObject();
13055 pProgress->init(i_getVirtualBox(),
13056 static_cast<IMachine *>(this) /* aInitiator */,
13057 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13058 FALSE /* aCancelable */);
13059
13060 /* stateFilePath is null when the machine is not running */
13061 if (mData->mMachineState == MachineState_Paused)
13062 i_composeSavedStateFilename(aStateFilePath);
13063
13064 /* fill in the console task data */
13065 mConsoleTaskData.mLastState = mData->mMachineState;
13066 mConsoleTaskData.strStateFilePath = aStateFilePath;
13067 mConsoleTaskData.mProgress = pProgress;
13068
13069 /* set the state to Saving (this is expected by Console::SaveState()) */
13070 i_setMachineState(MachineState_Saving);
13071
13072 pProgress.queryInterfaceTo(aProgress.asOutParam());
13073
13074 return S_OK;
13075}
13076
13077/**
13078 * @note Locks mParent + this object for writing.
13079 */
13080HRESULT SessionMachine::endSavingState(LONG aResult,
13081 const com::Utf8Str &aErrMsg)
13082{
13083 LogFlowThisFunc(("\n"));
13084
13085 /* endSavingState() need mParent lock */
13086 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13087
13088 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_Saved)
13089 || (FAILED(aResult) && mData->mMachineState == MachineState_Saving))
13090 && mConsoleTaskData.mLastState != MachineState_Null
13091 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13092 E_FAIL);
13093
13094 /*
13095 * On failure, set the state to the state we had when BeginSavingState()
13096 * was called (this is expected by Console::SaveState() and the associated
13097 * task). On success the VM process already changed the state to
13098 * MachineState_Saved, so no need to do anything.
13099 */
13100 if (FAILED(aResult))
13101 i_setMachineState(mConsoleTaskData.mLastState);
13102
13103 return i_endSavingState(aResult, aErrMsg);
13104}
13105
13106/**
13107 * @note Locks this object for writing.
13108 */
13109HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13110{
13111 LogFlowThisFunc(("\n"));
13112
13113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13114
13115 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13116 || mData->mMachineState == MachineState_Teleported
13117 || mData->mMachineState == MachineState_Aborted
13118 , E_FAIL); /** @todo setError. */
13119
13120 com::Utf8Str stateFilePathFull;
13121 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13122 if (RT_FAILURE(vrc))
13123 return setError(VBOX_E_FILE_ERROR,
13124 tr("Invalid saved state file path '%s' (%Rrc)"),
13125 aSavedStateFile.c_str(),
13126 vrc);
13127
13128 mSSData->strStateFilePath = stateFilePathFull;
13129
13130 /* The below i_setMachineState() will detect the state transition and will
13131 * update the settings file */
13132
13133 return i_setMachineState(MachineState_Saved);
13134}
13135
13136HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13137 std::vector<com::Utf8Str> &aValues,
13138 std::vector<LONG64> &aTimestamps,
13139 std::vector<com::Utf8Str> &aFlags)
13140{
13141 LogFlowThisFunc(("\n"));
13142
13143#ifdef VBOX_WITH_GUEST_PROPS
13144 using namespace guestProp;
13145
13146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13147
13148 size_t cEntries = mHWData->mGuestProperties.size();
13149 aNames.resize(cEntries);
13150 aValues.resize(cEntries);
13151 aTimestamps.resize(cEntries);
13152 aFlags.resize(cEntries);
13153
13154 size_t i = 0;
13155 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13156 it != mHWData->mGuestProperties.end();
13157 ++it, ++i)
13158 {
13159 char szFlags[MAX_FLAGS_LEN + 1];
13160 aNames[i] = it->first;
13161 aValues[i] = it->second.strValue;
13162 aTimestamps[i] = it->second.mTimestamp;
13163
13164 /* If it is NULL, keep it NULL. */
13165 if (it->second.mFlags)
13166 {
13167 writeFlags(it->second.mFlags, szFlags);
13168 aFlags[i] = szFlags;
13169 }
13170 else
13171 aFlags[i] = "";
13172 }
13173 return S_OK;
13174#else
13175 ReturnComNotImplemented();
13176#endif
13177}
13178
13179HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13180 const com::Utf8Str &aValue,
13181 LONG64 aTimestamp,
13182 const com::Utf8Str &aFlags)
13183{
13184 LogFlowThisFunc(("\n"));
13185
13186#ifdef VBOX_WITH_GUEST_PROPS
13187 using namespace guestProp;
13188
13189 try
13190 {
13191 /*
13192 * Convert input up front.
13193 */
13194 uint32_t fFlags = NILFLAG;
13195 if (aFlags.length())
13196 {
13197 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13198 AssertRCReturn(vrc, E_INVALIDARG);
13199 }
13200
13201 /*
13202 * Now grab the object lock, validate the state and do the update.
13203 */
13204
13205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13206
13207 switch (mData->mMachineState)
13208 {
13209 case MachineState_Paused:
13210 case MachineState_Running:
13211 case MachineState_Teleporting:
13212 case MachineState_TeleportingPausedVM:
13213 case MachineState_LiveSnapshotting:
13214 case MachineState_DeletingSnapshotOnline:
13215 case MachineState_DeletingSnapshotPaused:
13216 case MachineState_Saving:
13217 case MachineState_Stopping:
13218 break;
13219
13220 default:
13221 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13222 VBOX_E_INVALID_VM_STATE);
13223 }
13224
13225 i_setModified(IsModified_MachineData);
13226 mHWData.backup();
13227
13228 bool fDelete = !aValue.length();
13229 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13230 if (it != mHWData->mGuestProperties.end())
13231 {
13232 if (!fDelete)
13233 {
13234 it->second.strValue = aValue;
13235 it->second.mTimestamp = aTimestamp;
13236 it->second.mFlags = fFlags;
13237 }
13238 else
13239 mHWData->mGuestProperties.erase(it);
13240
13241 mData->mGuestPropertiesModified = TRUE;
13242 }
13243 else if (!fDelete)
13244 {
13245 HWData::GuestProperty prop;
13246 prop.strValue = aValue;
13247 prop.mTimestamp = aTimestamp;
13248 prop.mFlags = fFlags;
13249
13250 mHWData->mGuestProperties[aName] = prop;
13251 mData->mGuestPropertiesModified = TRUE;
13252 }
13253
13254 /*
13255 * Send a callback notification if appropriate
13256 */
13257 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13258 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13259 RTSTR_MAX,
13260 aName.c_str(),
13261 RTSTR_MAX, NULL)
13262 )
13263 {
13264 alock.release();
13265
13266 mParent->i_onGuestPropertyChange(mData->mUuid,
13267 Bstr(aName).raw(),
13268 Bstr(aValue).raw(),
13269 Bstr(aFlags).raw());
13270 }
13271 }
13272 catch (...)
13273 {
13274 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13275 }
13276 return S_OK;
13277#else
13278 ReturnComNotImplemented();
13279#endif
13280}
13281
13282
13283HRESULT SessionMachine::lockMedia()
13284{
13285 AutoMultiWriteLock2 alock(this->lockHandle(),
13286 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13287
13288 AssertReturn( mData->mMachineState == MachineState_Starting
13289 || mData->mMachineState == MachineState_Restoring
13290 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13291
13292 clearError();
13293 alock.release();
13294 return i_lockMedia();
13295}
13296
13297HRESULT SessionMachine::unlockMedia()
13298{
13299 HRESULT hrc = i_unlockMedia();
13300 return hrc;
13301}
13302
13303HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13304 ComPtr<IMediumAttachment> &aNewAttachment)
13305{
13306 // request the host lock first, since might be calling Host methods for getting host drives;
13307 // next, protect the media tree all the while we're in here, as well as our member variables
13308 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13309 this->lockHandle(),
13310 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13311
13312 IMediumAttachment *iAttach = aAttachment;
13313 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13314
13315 Bstr ctrlName;
13316 LONG lPort;
13317 LONG lDevice;
13318 bool fTempEject;
13319 {
13320 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13321
13322 /* Need to query the details first, as the IMediumAttachment reference
13323 * might be to the original settings, which we are going to change. */
13324 ctrlName = pAttach->i_getControllerName();
13325 lPort = pAttach->i_getPort();
13326 lDevice = pAttach->i_getDevice();
13327 fTempEject = pAttach->i_getTempEject();
13328 }
13329
13330 if (!fTempEject)
13331 {
13332 /* Remember previously mounted medium. The medium before taking the
13333 * backup is not necessarily the same thing. */
13334 ComObjPtr<Medium> oldmedium;
13335 oldmedium = pAttach->i_getMedium();
13336
13337 i_setModified(IsModified_Storage);
13338 mMediaData.backup();
13339
13340 // The backup operation makes the pAttach reference point to the
13341 // old settings. Re-get the correct reference.
13342 pAttach = i_findAttachment(mMediaData->mAttachments,
13343 ctrlName.raw(),
13344 lPort,
13345 lDevice);
13346
13347 {
13348 AutoCaller autoAttachCaller(this);
13349 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13350
13351 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13352 if (!oldmedium.isNull())
13353 oldmedium->i_removeBackReference(mData->mUuid);
13354
13355 pAttach->i_updateMedium(NULL);
13356 pAttach->i_updateEjected();
13357 }
13358
13359 i_setModified(IsModified_Storage);
13360 }
13361 else
13362 {
13363 {
13364 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13365 pAttach->i_updateEjected();
13366 }
13367 }
13368
13369 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13370
13371 return S_OK;
13372}
13373
13374// public methods only for internal purposes
13375/////////////////////////////////////////////////////////////////////////////
13376
13377#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13378/**
13379 * Called from the client watcher thread to check for expected or unexpected
13380 * death of the client process that has a direct session to this machine.
13381 *
13382 * On Win32 and on OS/2, this method is called only when we've got the
13383 * mutex (i.e. the client has either died or terminated normally) so it always
13384 * returns @c true (the client is terminated, the session machine is
13385 * uninitialized).
13386 *
13387 * On other platforms, the method returns @c true if the client process has
13388 * terminated normally or abnormally and the session machine was uninitialized,
13389 * and @c false if the client process is still alive.
13390 *
13391 * @note Locks this object for writing.
13392 */
13393bool SessionMachine::i_checkForDeath()
13394{
13395 Uninit::Reason reason;
13396 bool terminated = false;
13397
13398 /* Enclose autoCaller with a block because calling uninit() from under it
13399 * will deadlock. */
13400 {
13401 AutoCaller autoCaller(this);
13402 if (!autoCaller.isOk())
13403 {
13404 /* return true if not ready, to cause the client watcher to exclude
13405 * the corresponding session from watching */
13406 LogFlowThisFunc(("Already uninitialized!\n"));
13407 return true;
13408 }
13409
13410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13411
13412 /* Determine the reason of death: if the session state is Closing here,
13413 * everything is fine. Otherwise it means that the client did not call
13414 * OnSessionEnd() before it released the IPC semaphore. This may happen
13415 * either because the client process has abnormally terminated, or
13416 * because it simply forgot to call ISession::Close() before exiting. We
13417 * threat the latter also as an abnormal termination (see
13418 * Session::uninit() for details). */
13419 reason = mData->mSession.mState == SessionState_Unlocking ?
13420 Uninit::Normal :
13421 Uninit::Abnormal;
13422
13423 if (mClientToken)
13424 terminated = mClientToken->release();
13425 } /* AutoCaller block */
13426
13427 if (terminated)
13428 uninit(reason);
13429
13430 return terminated;
13431}
13432
13433void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13434{
13435 LogFlowThisFunc(("\n"));
13436
13437 strTokenId.setNull();
13438
13439 AutoCaller autoCaller(this);
13440 AssertComRCReturnVoid(autoCaller.rc());
13441
13442 Assert(mClientToken);
13443 if (mClientToken)
13444 mClientToken->getId(strTokenId);
13445}
13446#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13447IToken *SessionMachine::i_getToken()
13448{
13449 LogFlowThisFunc(("\n"));
13450
13451 AutoCaller autoCaller(this);
13452 AssertComRCReturn(autoCaller.rc(), NULL);
13453
13454 Assert(mClientToken);
13455 if (mClientToken)
13456 return mClientToken->getToken();
13457 else
13458 return NULL;
13459}
13460#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13461
13462Machine::ClientToken *SessionMachine::i_getClientToken()
13463{
13464 LogFlowThisFunc(("\n"));
13465
13466 AutoCaller autoCaller(this);
13467 AssertComRCReturn(autoCaller.rc(), NULL);
13468
13469 return mClientToken;
13470}
13471
13472
13473/**
13474 * @note Locks this object for reading.
13475 */
13476HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13477{
13478 LogFlowThisFunc(("\n"));
13479
13480 AutoCaller autoCaller(this);
13481 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13482
13483 ComPtr<IInternalSessionControl> directControl;
13484 {
13485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13486 directControl = mData->mSession.mDirectControl;
13487 }
13488
13489 /* ignore notifications sent after #OnSessionEnd() is called */
13490 if (!directControl)
13491 return S_OK;
13492
13493 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13494}
13495
13496/**
13497 * @note Locks this object for reading.
13498 */
13499HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13500 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13501 IN_BSTR aGuestIp, LONG aGuestPort)
13502{
13503 LogFlowThisFunc(("\n"));
13504
13505 AutoCaller autoCaller(this);
13506 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13507
13508 ComPtr<IInternalSessionControl> directControl;
13509 {
13510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13511 directControl = mData->mSession.mDirectControl;
13512 }
13513
13514 /* ignore notifications sent after #OnSessionEnd() is called */
13515 if (!directControl)
13516 return S_OK;
13517 /*
13518 * instead acting like callback we ask IVirtualBox deliver corresponding event
13519 */
13520
13521 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13522 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13523 return S_OK;
13524}
13525
13526/**
13527 * @note Locks this object for reading.
13528 */
13529HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13530{
13531 LogFlowThisFunc(("\n"));
13532
13533 AutoCaller autoCaller(this);
13534 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13535
13536 ComPtr<IInternalSessionControl> directControl;
13537 {
13538 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13539 directControl = mData->mSession.mDirectControl;
13540 }
13541
13542 /* ignore notifications sent after #OnSessionEnd() is called */
13543 if (!directControl)
13544 return S_OK;
13545
13546 return directControl->OnSerialPortChange(serialPort);
13547}
13548
13549/**
13550 * @note Locks this object for reading.
13551 */
13552HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13553{
13554 LogFlowThisFunc(("\n"));
13555
13556 AutoCaller autoCaller(this);
13557 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13558
13559 ComPtr<IInternalSessionControl> directControl;
13560 {
13561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13562 directControl = mData->mSession.mDirectControl;
13563 }
13564
13565 /* ignore notifications sent after #OnSessionEnd() is called */
13566 if (!directControl)
13567 return S_OK;
13568
13569 return directControl->OnParallelPortChange(parallelPort);
13570}
13571
13572/**
13573 * @note Locks this object for reading.
13574 */
13575HRESULT SessionMachine::i_onStorageControllerChange()
13576{
13577 LogFlowThisFunc(("\n"));
13578
13579 AutoCaller autoCaller(this);
13580 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13581
13582 ComPtr<IInternalSessionControl> directControl;
13583 {
13584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13585 directControl = mData->mSession.mDirectControl;
13586 }
13587
13588 /* ignore notifications sent after #OnSessionEnd() is called */
13589 if (!directControl)
13590 return S_OK;
13591
13592 return directControl->OnStorageControllerChange();
13593}
13594
13595/**
13596 * @note Locks this object for reading.
13597 */
13598HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13599{
13600 LogFlowThisFunc(("\n"));
13601
13602 AutoCaller autoCaller(this);
13603 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13604
13605 ComPtr<IInternalSessionControl> directControl;
13606 {
13607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13608 directControl = mData->mSession.mDirectControl;
13609 }
13610
13611 /* ignore notifications sent after #OnSessionEnd() is called */
13612 if (!directControl)
13613 return S_OK;
13614
13615 return directControl->OnMediumChange(aAttachment, aForce);
13616}
13617
13618/**
13619 * @note Locks this object for reading.
13620 */
13621HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13622{
13623 LogFlowThisFunc(("\n"));
13624
13625 AutoCaller autoCaller(this);
13626 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13627
13628 ComPtr<IInternalSessionControl> directControl;
13629 {
13630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13631 directControl = mData->mSession.mDirectControl;
13632 }
13633
13634 /* ignore notifications sent after #OnSessionEnd() is called */
13635 if (!directControl)
13636 return S_OK;
13637
13638 return directControl->OnCPUChange(aCPU, aRemove);
13639}
13640
13641HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13642{
13643 LogFlowThisFunc(("\n"));
13644
13645 AutoCaller autoCaller(this);
13646 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13647
13648 ComPtr<IInternalSessionControl> directControl;
13649 {
13650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13651 directControl = mData->mSession.mDirectControl;
13652 }
13653
13654 /* ignore notifications sent after #OnSessionEnd() is called */
13655 if (!directControl)
13656 return S_OK;
13657
13658 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13659}
13660
13661/**
13662 * @note Locks this object for reading.
13663 */
13664HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13665{
13666 LogFlowThisFunc(("\n"));
13667
13668 AutoCaller autoCaller(this);
13669 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13670
13671 ComPtr<IInternalSessionControl> directControl;
13672 {
13673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13674 directControl = mData->mSession.mDirectControl;
13675 }
13676
13677 /* ignore notifications sent after #OnSessionEnd() is called */
13678 if (!directControl)
13679 return S_OK;
13680
13681 return directControl->OnVRDEServerChange(aRestart);
13682}
13683
13684/**
13685 * @note Locks this object for reading.
13686 */
13687HRESULT SessionMachine::i_onVideoCaptureChange()
13688{
13689 LogFlowThisFunc(("\n"));
13690
13691 AutoCaller autoCaller(this);
13692 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13693
13694 ComPtr<IInternalSessionControl> directControl;
13695 {
13696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13697 directControl = mData->mSession.mDirectControl;
13698 }
13699
13700 /* ignore notifications sent after #OnSessionEnd() is called */
13701 if (!directControl)
13702 return S_OK;
13703
13704 return directControl->OnVideoCaptureChange();
13705}
13706
13707/**
13708 * @note Locks this object for reading.
13709 */
13710HRESULT SessionMachine::i_onUSBControllerChange()
13711{
13712 LogFlowThisFunc(("\n"));
13713
13714 AutoCaller autoCaller(this);
13715 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13716
13717 ComPtr<IInternalSessionControl> directControl;
13718 {
13719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13720 directControl = mData->mSession.mDirectControl;
13721 }
13722
13723 /* ignore notifications sent after #OnSessionEnd() is called */
13724 if (!directControl)
13725 return S_OK;
13726
13727 return directControl->OnUSBControllerChange();
13728}
13729
13730/**
13731 * @note Locks this object for reading.
13732 */
13733HRESULT SessionMachine::i_onSharedFolderChange()
13734{
13735 LogFlowThisFunc(("\n"));
13736
13737 AutoCaller autoCaller(this);
13738 AssertComRCReturnRC(autoCaller.rc());
13739
13740 ComPtr<IInternalSessionControl> directControl;
13741 {
13742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13743 directControl = mData->mSession.mDirectControl;
13744 }
13745
13746 /* ignore notifications sent after #OnSessionEnd() is called */
13747 if (!directControl)
13748 return S_OK;
13749
13750 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13751}
13752
13753/**
13754 * @note Locks this object for reading.
13755 */
13756HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13757{
13758 LogFlowThisFunc(("\n"));
13759
13760 AutoCaller autoCaller(this);
13761 AssertComRCReturnRC(autoCaller.rc());
13762
13763 ComPtr<IInternalSessionControl> directControl;
13764 {
13765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13766 directControl = mData->mSession.mDirectControl;
13767 }
13768
13769 /* ignore notifications sent after #OnSessionEnd() is called */
13770 if (!directControl)
13771 return S_OK;
13772
13773 return directControl->OnClipboardModeChange(aClipboardMode);
13774}
13775
13776/**
13777 * @note Locks this object for reading.
13778 */
13779HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13780{
13781 LogFlowThisFunc(("\n"));
13782
13783 AutoCaller autoCaller(this);
13784 AssertComRCReturnRC(autoCaller.rc());
13785
13786 ComPtr<IInternalSessionControl> directControl;
13787 {
13788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13789 directControl = mData->mSession.mDirectControl;
13790 }
13791
13792 /* ignore notifications sent after #OnSessionEnd() is called */
13793 if (!directControl)
13794 return S_OK;
13795
13796 return directControl->OnDnDModeChange(aDnDMode);
13797}
13798
13799/**
13800 * @note Locks this object for reading.
13801 */
13802HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13803{
13804 LogFlowThisFunc(("\n"));
13805
13806 AutoCaller autoCaller(this);
13807 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13808
13809 ComPtr<IInternalSessionControl> directControl;
13810 {
13811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13812 directControl = mData->mSession.mDirectControl;
13813 }
13814
13815 /* ignore notifications sent after #OnSessionEnd() is called */
13816 if (!directControl)
13817 return S_OK;
13818
13819 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13820}
13821
13822/**
13823 * @note Locks this object for reading.
13824 */
13825HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13826{
13827 LogFlowThisFunc(("\n"));
13828
13829 AutoCaller autoCaller(this);
13830 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13831
13832 ComPtr<IInternalSessionControl> directControl;
13833 {
13834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13835 directControl = mData->mSession.mDirectControl;
13836 }
13837
13838 /* ignore notifications sent after #OnSessionEnd() is called */
13839 if (!directControl)
13840 return S_OK;
13841
13842 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13843}
13844
13845/**
13846 * Returns @c true if this machine's USB controller reports it has a matching
13847 * filter for the given USB device and @c false otherwise.
13848 *
13849 * @note locks this object for reading.
13850 */
13851bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13852{
13853 AutoCaller autoCaller(this);
13854 /* silently return if not ready -- this method may be called after the
13855 * direct machine session has been called */
13856 if (!autoCaller.isOk())
13857 return false;
13858
13859#ifdef VBOX_WITH_USB
13860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13861
13862 switch (mData->mMachineState)
13863 {
13864 case MachineState_Starting:
13865 case MachineState_Restoring:
13866 case MachineState_TeleportingIn:
13867 case MachineState_Paused:
13868 case MachineState_Running:
13869 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13870 * elsewhere... */
13871 alock.release();
13872 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
13873 default: break;
13874 }
13875#else
13876 NOREF(aDevice);
13877 NOREF(aMaskedIfs);
13878#endif
13879 return false;
13880}
13881
13882/**
13883 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13884 */
13885HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
13886 IVirtualBoxErrorInfo *aError,
13887 ULONG aMaskedIfs,
13888 const com::Utf8Str &aCaptureFilename)
13889{
13890 LogFlowThisFunc(("\n"));
13891
13892 AutoCaller autoCaller(this);
13893
13894 /* This notification may happen after the machine object has been
13895 * uninitialized (the session was closed), so don't assert. */
13896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13897
13898 ComPtr<IInternalSessionControl> directControl;
13899 {
13900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13901 directControl = mData->mSession.mDirectControl;
13902 }
13903
13904 /* fail on notifications sent after #OnSessionEnd() is called, it is
13905 * expected by the caller */
13906 if (!directControl)
13907 return E_FAIL;
13908
13909 /* No locks should be held at this point. */
13910 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13911 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13912
13913 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
13914}
13915
13916/**
13917 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13918 */
13919HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
13920 IVirtualBoxErrorInfo *aError)
13921{
13922 LogFlowThisFunc(("\n"));
13923
13924 AutoCaller autoCaller(this);
13925
13926 /* This notification may happen after the machine object has been
13927 * uninitialized (the session was closed), so don't assert. */
13928 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13929
13930 ComPtr<IInternalSessionControl> directControl;
13931 {
13932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13933 directControl = mData->mSession.mDirectControl;
13934 }
13935
13936 /* fail on notifications sent after #OnSessionEnd() is called, it is
13937 * expected by the caller */
13938 if (!directControl)
13939 return E_FAIL;
13940
13941 /* No locks should be held at this point. */
13942 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13943 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13944
13945 return directControl->OnUSBDeviceDetach(aId, aError);
13946}
13947
13948// protected methods
13949/////////////////////////////////////////////////////////////////////////////
13950
13951/**
13952 * Helper method to finalize saving the state.
13953 *
13954 * @note Must be called from under this object's lock.
13955 *
13956 * @param aRc S_OK if the snapshot has been taken successfully
13957 * @param aErrMsg human readable error message for failure
13958 *
13959 * @note Locks mParent + this objects for writing.
13960 */
13961HRESULT SessionMachine::i_endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13962{
13963 LogFlowThisFuncEnter();
13964
13965 AutoCaller autoCaller(this);
13966 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13967
13968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13969
13970 HRESULT rc = S_OK;
13971
13972 if (SUCCEEDED(aRc))
13973 {
13974 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13975
13976 /* save all VM settings */
13977 rc = i_saveSettings(NULL);
13978 // no need to check whether VirtualBox.xml needs saving also since
13979 // we can't have a name change pending at this point
13980 }
13981 else
13982 {
13983 // delete the saved state file (it might have been already created);
13984 // we need not check whether this is shared with a snapshot here because
13985 // we certainly created this saved state file here anew
13986 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13987 }
13988
13989 /* notify the progress object about operation completion */
13990 Assert(mConsoleTaskData.mProgress);
13991 if (SUCCEEDED(aRc))
13992 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13993 else
13994 {
13995 if (aErrMsg.length())
13996 mConsoleTaskData.mProgress->i_notifyComplete(aRc,
13997 COM_IIDOF(ISession),
13998 getComponentName(),
13999 aErrMsg.c_str());
14000 else
14001 mConsoleTaskData.mProgress->i_notifyComplete(aRc);
14002 }
14003
14004 /* clear out the temporary saved state data */
14005 mConsoleTaskData.mLastState = MachineState_Null;
14006 mConsoleTaskData.strStateFilePath.setNull();
14007 mConsoleTaskData.mProgress.setNull();
14008
14009 LogFlowThisFuncLeave();
14010 return rc;
14011}
14012
14013/**
14014 * Deletes the given file if it is no longer in use by either the current machine state
14015 * (if the machine is "saved") or any of the machine's snapshots.
14016 *
14017 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14018 * but is different for each SnapshotMachine. When calling this, the order of calling this
14019 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14020 * is therefore critical. I know, it's all rather messy.
14021 *
14022 * @param strStateFile
14023 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14024 * the test for whether the saved state file is in use.
14025 */
14026void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14027 Snapshot *pSnapshotToIgnore)
14028{
14029 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14030 if ( (strStateFile.isNotEmpty())
14031 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14032 )
14033 // ... and it must also not be shared with other snapshots
14034 if ( !mData->mFirstSnapshot
14035 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14036 // this checks the SnapshotMachine's state file paths
14037 )
14038 RTFileDelete(strStateFile.c_str());
14039}
14040
14041/**
14042 * Locks the attached media.
14043 *
14044 * All attached hard disks are locked for writing and DVD/floppy are locked for
14045 * reading. Parents of attached hard disks (if any) are locked for reading.
14046 *
14047 * This method also performs accessibility check of all media it locks: if some
14048 * media is inaccessible, the method will return a failure and a bunch of
14049 * extended error info objects per each inaccessible medium.
14050 *
14051 * Note that this method is atomic: if it returns a success, all media are
14052 * locked as described above; on failure no media is locked at all (all
14053 * succeeded individual locks will be undone).
14054 *
14055 * The caller is responsible for doing the necessary state sanity checks.
14056 *
14057 * The locks made by this method must be undone by calling #unlockMedia() when
14058 * no more needed.
14059 */
14060HRESULT SessionMachine::i_lockMedia()
14061{
14062 AutoCaller autoCaller(this);
14063 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14064
14065 AutoMultiWriteLock2 alock(this->lockHandle(),
14066 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14067
14068 /* bail out if trying to lock things with already set up locking */
14069 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14070
14071 MultiResult mrc(S_OK);
14072
14073 /* Collect locking information for all medium objects attached to the VM. */
14074 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14075 it != mMediaData->mAttachments.end();
14076 ++it)
14077 {
14078 MediumAttachment* pAtt = *it;
14079 DeviceType_T devType = pAtt->i_getType();
14080 Medium *pMedium = pAtt->i_getMedium();
14081
14082 MediumLockList *pMediumLockList(new MediumLockList());
14083 // There can be attachments without a medium (floppy/dvd), and thus
14084 // it's impossible to create a medium lock list. It still makes sense
14085 // to have the empty medium lock list in the map in case a medium is
14086 // attached later.
14087 if (pMedium != NULL)
14088 {
14089 MediumType_T mediumType = pMedium->i_getType();
14090 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14091 || mediumType == MediumType_Shareable;
14092 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14093
14094 alock.release();
14095 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14096 !fIsReadOnlyLock /* fMediumLockWrite */,
14097 false /* fMediumLockWriteAll */,
14098 NULL,
14099 *pMediumLockList);
14100 alock.acquire();
14101 if (FAILED(mrc))
14102 {
14103 delete pMediumLockList;
14104 mData->mSession.mLockedMedia.Clear();
14105 break;
14106 }
14107 }
14108
14109 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14110 if (FAILED(rc))
14111 {
14112 mData->mSession.mLockedMedia.Clear();
14113 mrc = setError(rc,
14114 tr("Collecting locking information for all attached media failed"));
14115 break;
14116 }
14117 }
14118
14119 if (SUCCEEDED(mrc))
14120 {
14121 /* Now lock all media. If this fails, nothing is locked. */
14122 alock.release();
14123 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14124 alock.acquire();
14125 if (FAILED(rc))
14126 {
14127 mrc = setError(rc,
14128 tr("Locking of attached media failed"));
14129 }
14130 }
14131
14132 return mrc;
14133}
14134
14135/**
14136 * Undoes the locks made by by #lockMedia().
14137 */
14138HRESULT SessionMachine::i_unlockMedia()
14139{
14140 AutoCaller autoCaller(this);
14141 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14142
14143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14144
14145 /* we may be holding important error info on the current thread;
14146 * preserve it */
14147 ErrorInfoKeeper eik;
14148
14149 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14150 AssertComRC(rc);
14151 return rc;
14152}
14153
14154/**
14155 * Helper to change the machine state (reimplementation).
14156 *
14157 * @note Locks this object for writing.
14158 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14159 * it can cause crashes in random places due to unexpectedly committing
14160 * the current settings. The caller is responsible for that. The call
14161 * to saveStateSettings is fine, because this method does not commit.
14162 */
14163HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14164{
14165 LogFlowThisFuncEnter();
14166 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14167
14168 AutoCaller autoCaller(this);
14169 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14170
14171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14172
14173 MachineState_T oldMachineState = mData->mMachineState;
14174
14175 AssertMsgReturn(oldMachineState != aMachineState,
14176 ("oldMachineState=%s, aMachineState=%s\n",
14177 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14178 E_FAIL);
14179
14180 HRESULT rc = S_OK;
14181
14182 int stsFlags = 0;
14183 bool deleteSavedState = false;
14184
14185 /* detect some state transitions */
14186
14187 if ( ( oldMachineState == MachineState_Saved
14188 && aMachineState == MachineState_Restoring)
14189 || ( ( oldMachineState == MachineState_PoweredOff
14190 || oldMachineState == MachineState_Teleported
14191 || oldMachineState == MachineState_Aborted
14192 )
14193 && ( aMachineState == MachineState_TeleportingIn
14194 || aMachineState == MachineState_Starting
14195 )
14196 )
14197 )
14198 {
14199 /* The EMT thread is about to start */
14200
14201 /* Nothing to do here for now... */
14202
14203 /// @todo NEWMEDIA don't let mDVDDrive and other children
14204 /// change anything when in the Starting/Restoring state
14205 }
14206 else if ( ( oldMachineState == MachineState_Running
14207 || oldMachineState == MachineState_Paused
14208 || oldMachineState == MachineState_Teleporting
14209 || oldMachineState == MachineState_LiveSnapshotting
14210 || oldMachineState == MachineState_Stuck
14211 || oldMachineState == MachineState_Starting
14212 || oldMachineState == MachineState_Stopping
14213 || oldMachineState == MachineState_Saving
14214 || oldMachineState == MachineState_Restoring
14215 || oldMachineState == MachineState_TeleportingPausedVM
14216 || oldMachineState == MachineState_TeleportingIn
14217 )
14218 && ( aMachineState == MachineState_PoweredOff
14219 || aMachineState == MachineState_Saved
14220 || aMachineState == MachineState_Teleported
14221 || aMachineState == MachineState_Aborted
14222 )
14223 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14224 * snapshot */
14225 && ( mConsoleTaskData.mSnapshot.isNull()
14226 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14227 )
14228 )
14229 {
14230 /* The EMT thread has just stopped, unlock attached media. Note that as
14231 * opposed to locking that is done from Console, we do unlocking here
14232 * because the VM process may have aborted before having a chance to
14233 * properly unlock all media it locked. */
14234
14235 unlockMedia();
14236 }
14237
14238 if (oldMachineState == MachineState_Restoring)
14239 {
14240 if (aMachineState != MachineState_Saved)
14241 {
14242 /*
14243 * delete the saved state file once the machine has finished
14244 * restoring from it (note that Console sets the state from
14245 * Restoring to Saved if the VM couldn't restore successfully,
14246 * to give the user an ability to fix an error and retry --
14247 * we keep the saved state file in this case)
14248 */
14249 deleteSavedState = true;
14250 }
14251 }
14252 else if ( oldMachineState == MachineState_Saved
14253 && ( aMachineState == MachineState_PoweredOff
14254 || aMachineState == MachineState_Aborted
14255 || aMachineState == MachineState_Teleported
14256 )
14257 )
14258 {
14259 /*
14260 * delete the saved state after Console::ForgetSavedState() is called
14261 * or if the VM process (owning a direct VM session) crashed while the
14262 * VM was Saved
14263 */
14264
14265 /// @todo (dmik)
14266 // Not sure that deleting the saved state file just because of the
14267 // client death before it attempted to restore the VM is a good
14268 // thing. But when it crashes we need to go to the Aborted state
14269 // which cannot have the saved state file associated... The only
14270 // way to fix this is to make the Aborted condition not a VM state
14271 // but a bool flag: i.e., when a crash occurs, set it to true and
14272 // change the state to PoweredOff or Saved depending on the
14273 // saved state presence.
14274
14275 deleteSavedState = true;
14276 mData->mCurrentStateModified = TRUE;
14277 stsFlags |= SaveSTS_CurStateModified;
14278 }
14279
14280 if ( aMachineState == MachineState_Starting
14281 || aMachineState == MachineState_Restoring
14282 || aMachineState == MachineState_TeleportingIn
14283 )
14284 {
14285 /* set the current state modified flag to indicate that the current
14286 * state is no more identical to the state in the
14287 * current snapshot */
14288 if (!mData->mCurrentSnapshot.isNull())
14289 {
14290 mData->mCurrentStateModified = TRUE;
14291 stsFlags |= SaveSTS_CurStateModified;
14292 }
14293 }
14294
14295 if (deleteSavedState)
14296 {
14297 if (mRemoveSavedState)
14298 {
14299 Assert(!mSSData->strStateFilePath.isEmpty());
14300
14301 // it is safe to delete the saved state file if ...
14302 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14303 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14304 // ... none of the snapshots share the saved state file
14305 )
14306 RTFileDelete(mSSData->strStateFilePath.c_str());
14307 }
14308
14309 mSSData->strStateFilePath.setNull();
14310 stsFlags |= SaveSTS_StateFilePath;
14311 }
14312
14313 /* redirect to the underlying peer machine */
14314 mPeer->i_setMachineState(aMachineState);
14315
14316 if ( aMachineState == MachineState_PoweredOff
14317 || aMachineState == MachineState_Teleported
14318 || aMachineState == MachineState_Aborted
14319 || aMachineState == MachineState_Saved)
14320 {
14321 /* the machine has stopped execution
14322 * (or the saved state file was adopted) */
14323 stsFlags |= SaveSTS_StateTimeStamp;
14324 }
14325
14326 if ( ( oldMachineState == MachineState_PoweredOff
14327 || oldMachineState == MachineState_Aborted
14328 || oldMachineState == MachineState_Teleported
14329 )
14330 && aMachineState == MachineState_Saved)
14331 {
14332 /* the saved state file was adopted */
14333 Assert(!mSSData->strStateFilePath.isEmpty());
14334 stsFlags |= SaveSTS_StateFilePath;
14335 }
14336
14337#ifdef VBOX_WITH_GUEST_PROPS
14338 if ( aMachineState == MachineState_PoweredOff
14339 || aMachineState == MachineState_Aborted
14340 || aMachineState == MachineState_Teleported)
14341 {
14342 /* Make sure any transient guest properties get removed from the
14343 * property store on shutdown. */
14344 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14345
14346 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14347 settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14348 while (it != llGuestProperties.end())
14349 {
14350 const settings::GuestProperty &prop = *it;
14351 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14352 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14353 {
14354 it = llGuestProperties.erase(it);
14355 fNeedsSaving = true;
14356 }
14357 else
14358 {
14359 ++it;
14360 }
14361 }
14362
14363 if (fNeedsSaving)
14364 {
14365 mData->mCurrentStateModified = TRUE;
14366 stsFlags |= SaveSTS_CurStateModified;
14367 }
14368 }
14369#endif /* VBOX_WITH_GUEST_PROPS */
14370
14371 rc = i_saveStateSettings(stsFlags);
14372
14373 if ( ( oldMachineState != MachineState_PoweredOff
14374 && oldMachineState != MachineState_Aborted
14375 && oldMachineState != MachineState_Teleported
14376 )
14377 && ( aMachineState == MachineState_PoweredOff
14378 || aMachineState == MachineState_Aborted
14379 || aMachineState == MachineState_Teleported
14380 )
14381 )
14382 {
14383 /* we've been shut down for any reason */
14384 /* no special action so far */
14385 }
14386
14387 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14388 LogFlowThisFuncLeave();
14389 return rc;
14390}
14391
14392/**
14393 * Sends the current machine state value to the VM process.
14394 *
14395 * @note Locks this object for reading, then calls a client process.
14396 */
14397HRESULT SessionMachine::i_updateMachineStateOnClient()
14398{
14399 AutoCaller autoCaller(this);
14400 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14401
14402 ComPtr<IInternalSessionControl> directControl;
14403 {
14404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14405 AssertReturn(!!mData, E_FAIL);
14406 directControl = mData->mSession.mDirectControl;
14407
14408 /* directControl may be already set to NULL here in #OnSessionEnd()
14409 * called too early by the direct session process while there is still
14410 * some operation (like deleting the snapshot) in progress. The client
14411 * process in this case is waiting inside Session::close() for the
14412 * "end session" process object to complete, while #uninit() called by
14413 * #checkForDeath() on the Watcher thread is waiting for the pending
14414 * operation to complete. For now, we accept this inconsistent behavior
14415 * and simply do nothing here. */
14416
14417 if (mData->mSession.mState == SessionState_Unlocking)
14418 return S_OK;
14419
14420 AssertReturn(!directControl.isNull(), E_FAIL);
14421 }
14422
14423 return directControl->UpdateMachineState(mData->mMachineState);
14424}
14425
14426HRESULT Machine::setRemoveSavedStateFile(BOOL aRemove)
14427{
14428 NOREF(aRemove);
14429 ReturnComNotImplemented();
14430}
14431
14432HRESULT Machine::updateState(MachineState_T aState)
14433{
14434 NOREF(aState);
14435 ReturnComNotImplemented();
14436}
14437
14438HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14439{
14440 NOREF(aProgress);
14441 ReturnComNotImplemented();
14442}
14443
14444HRESULT Machine::endPowerUp(LONG aResult)
14445{
14446 NOREF(aResult);
14447 ReturnComNotImplemented();
14448}
14449
14450HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14451{
14452 NOREF(aProgress);
14453 ReturnComNotImplemented();
14454}
14455
14456HRESULT Machine::endPoweringDown(LONG aResult,
14457 const com::Utf8Str &aErrMsg)
14458{
14459 NOREF(aResult);
14460 NOREF(aErrMsg);
14461 ReturnComNotImplemented();
14462}
14463
14464HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14465 BOOL *aMatched,
14466 ULONG *aMaskedInterfaces)
14467{
14468 NOREF(aDevice);
14469 NOREF(aMatched);
14470 NOREF(aMaskedInterfaces);
14471 ReturnComNotImplemented();
14472
14473}
14474
14475HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14476{
14477 NOREF(aId); NOREF(aCaptureFilename);
14478 ReturnComNotImplemented();
14479}
14480
14481HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14482 BOOL aDone)
14483{
14484 NOREF(aId);
14485 NOREF(aDone);
14486 ReturnComNotImplemented();
14487}
14488
14489HRESULT Machine::autoCaptureUSBDevices()
14490{
14491 ReturnComNotImplemented();
14492}
14493
14494HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14495{
14496 NOREF(aDone);
14497 ReturnComNotImplemented();
14498}
14499
14500HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14501 ComPtr<IProgress> &aProgress)
14502{
14503 NOREF(aSession);
14504 NOREF(aProgress);
14505 ReturnComNotImplemented();
14506}
14507
14508HRESULT Machine::beginSavingState(ComPtr<IProgress> &aProgress,
14509 com::Utf8Str &aStateFilePath)
14510{
14511 NOREF(aProgress);
14512 NOREF(aStateFilePath);
14513 ReturnComNotImplemented();
14514}
14515
14516HRESULT Machine::endSavingState(LONG aResult,
14517 const com::Utf8Str &aErrMsg)
14518{
14519 NOREF(aResult);
14520 NOREF(aErrMsg);
14521 ReturnComNotImplemented();
14522}
14523
14524HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
14525{
14526 NOREF(aSavedStateFile);
14527 ReturnComNotImplemented();
14528}
14529
14530HRESULT Machine::beginTakingSnapshot(const ComPtr<IConsole> &aInitiator,
14531 const com::Utf8Str &aName,
14532 const com::Utf8Str &aDescription,
14533 const ComPtr<IProgress> &aConsoleProgress,
14534 BOOL aFTakingSnapshotOnline,
14535 com::Utf8Str &aStateFilePath)
14536{
14537 NOREF(aInitiator);
14538 NOREF(aName);
14539 NOREF(aDescription);
14540 NOREF(aConsoleProgress);
14541 NOREF(aFTakingSnapshotOnline);
14542 NOREF(aStateFilePath);
14543 ReturnComNotImplemented();
14544}
14545
14546HRESULT Machine::endTakingSnapshot(BOOL aSuccess)
14547{
14548 NOREF(aSuccess);
14549 ReturnComNotImplemented();
14550}
14551
14552HRESULT Machine::deleteSnapshot(const ComPtr<IConsole> &aInitiator,
14553 const com::Guid &aStartId,
14554 const com::Guid &aEndId,
14555 BOOL aDeleteAllChildren,
14556 MachineState_T *aMachineState,
14557 ComPtr<IProgress> &aProgress)
14558{
14559 NOREF(aInitiator);
14560 NOREF(aStartId);
14561 NOREF(aEndId);
14562 NOREF(aDeleteAllChildren);
14563 NOREF(aMachineState);
14564 NOREF(aProgress);
14565 ReturnComNotImplemented();
14566}
14567
14568HRESULT Machine::finishOnlineMergeMedium()
14569{
14570 ReturnComNotImplemented();
14571}
14572
14573HRESULT Machine::restoreSnapshot(const ComPtr<IConsole> &aInitiator,
14574 const ComPtr<ISnapshot> &aSnapshot,
14575 MachineState_T *aMachineState,
14576 ComPtr<IProgress> &aProgress)
14577{
14578 NOREF(aInitiator);
14579 NOREF(aSnapshot);
14580 NOREF(aMachineState);
14581 NOREF(aProgress);
14582 ReturnComNotImplemented();
14583}
14584
14585HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14586 std::vector<com::Utf8Str> &aValues,
14587 std::vector<LONG64> &aTimestamps,
14588 std::vector<com::Utf8Str> &aFlags)
14589{
14590 NOREF(aNames);
14591 NOREF(aValues);
14592 NOREF(aTimestamps);
14593 NOREF(aFlags);
14594 ReturnComNotImplemented();
14595}
14596
14597HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14598 const com::Utf8Str &aValue,
14599 LONG64 aTimestamp,
14600 const com::Utf8Str &aFlags)
14601{
14602 NOREF(aName);
14603 NOREF(aValue);
14604 NOREF(aTimestamp);
14605 NOREF(aFlags);
14606 ReturnComNotImplemented();
14607}
14608
14609HRESULT Machine::lockMedia()
14610{
14611 ReturnComNotImplemented();
14612}
14613
14614HRESULT Machine::unlockMedia()
14615{
14616 ReturnComNotImplemented();
14617}
14618
14619HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14620 ComPtr<IMediumAttachment> &aNewAttachment)
14621{
14622 NOREF(aAttachment);
14623 NOREF(aNewAttachment);
14624 ReturnComNotImplemented();
14625}
14626
14627HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14628 ULONG aCpuUser,
14629 ULONG aCpuKernel,
14630 ULONG aCpuIdle,
14631 ULONG aMemTotal,
14632 ULONG aMemFree,
14633 ULONG aMemBalloon,
14634 ULONG aMemShared,
14635 ULONG aMemCache,
14636 ULONG aPagedTotal,
14637 ULONG aMemAllocTotal,
14638 ULONG aMemFreeTotal,
14639 ULONG aMemBalloonTotal,
14640 ULONG aMemSharedTotal,
14641 ULONG aVmNetRx,
14642 ULONG aVmNetTx)
14643{
14644 NOREF(aValidStats);
14645 NOREF(aCpuUser);
14646 NOREF(aCpuKernel);
14647 NOREF(aCpuIdle);
14648 NOREF(aMemTotal);
14649 NOREF(aMemFree);
14650 NOREF(aMemBalloon);
14651 NOREF(aMemShared);
14652 NOREF(aMemCache);
14653 NOREF(aPagedTotal);
14654 NOREF(aMemAllocTotal);
14655 NOREF(aMemFreeTotal);
14656 NOREF(aMemBalloonTotal);
14657 NOREF(aMemSharedTotal);
14658 NOREF(aVmNetRx);
14659 NOREF(aVmNetTx);
14660 ReturnComNotImplemented();
14661}
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