VirtualBox

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

Last change on this file since 50403 was 50355, checked in by vboxsync, 11 years ago

6813 stage 7 VirtualBoxImpl.cpp etc

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 507.3 KB
Line 
1/* $Id: MachineImpl.cpp 50355 2014-02-06 17:55:07Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "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#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureEnabled = false;
169 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
170 maVideoCaptureScreens[i] = true;
171
172 mHWVirtExEnabled = true;
173 mHWVirtExNestedPagingEnabled = true;
174#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
175 mHWVirtExLargePagesEnabled = true;
176#else
177 /* Not supported on 32 bits hosts. */
178 mHWVirtExLargePagesEnabled = false;
179#endif
180 mHWVirtExVPIDEnabled = true;
181 mHWVirtExUXEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
189 mSyntheticCpu = false;
190 mTripleFaultReset = false;
191 mHPETEnabled = false;
192
193 /* default boot order: floppy - DVD - HDD */
194 mBootOrder[0] = DeviceType_Floppy;
195 mBootOrder[1] = DeviceType_DVD;
196 mBootOrder[2] = DeviceType_HardDisk;
197 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
198 mBootOrder[i] = DeviceType_Null;
199
200 mClipboardMode = ClipboardMode_Disabled;
201 mDragAndDropMode = DragAndDropMode_Disabled;
202 mGuestPropertyNotificationPatterns = "";
203
204 mFirmwareType = FirmwareType_BIOS;
205 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
206 mPointingHIDType = PointingHIDType_PS2Mouse;
207 mChipsetType = ChipsetType_PIIX3;
208 mEmulatedUSBCardReaderEnabled = FALSE;
209
210 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
211 mCPUAttached[i] = false;
212
213 mIOCacheEnabled = true;
214 mIOCacheSize = 5; /* 5MB */
215
216 /* Maximum CPU execution cap by default. */
217 mCpuExecutionCap = 100;
218}
219
220Machine::HWData::~HWData()
221{
222}
223
224/////////////////////////////////////////////////////////////////////////////
225// Machine::HDData structure
226/////////////////////////////////////////////////////////////////////////////
227
228Machine::MediaData::MediaData()
229{
230}
231
232Machine::MediaData::~MediaData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine class
238/////////////////////////////////////////////////////////////////////////////
239
240// constructor / destructor
241/////////////////////////////////////////////////////////////////////////////
242
243Machine::Machine() :
244#ifdef VBOX_WITH_RESOURCE_USAGE_API
245 mCollectorGuest(NULL),
246#endif
247 mPeer(NULL),
248 mParent(NULL),
249 mSerialPorts(),
250 mParallelPorts(),
251 uRegistryNeedsSaving(0)
252{}
253
254Machine::~Machine()
255{}
256
257HRESULT Machine::FinalConstruct()
258{
259 LogFlowThisFunc(("\n"));
260 return BaseFinalConstruct();
261}
262
263void Machine::FinalRelease()
264{
265 LogFlowThisFunc(("\n"));
266 uninit();
267 BaseFinalRelease();
268}
269
270/**
271 * Initializes a new machine instance; this init() variant creates a new, empty machine.
272 * This gets called from VirtualBox::CreateMachine().
273 *
274 * @param aParent Associated parent object
275 * @param strConfigFile Local file system path to the VM settings file (can
276 * be relative to the VirtualBox config directory).
277 * @param strName name for the machine
278 * @param llGroups list of groups for the machine
279 * @param aOsType OS Type of this machine or NULL.
280 * @param aId UUID for the new machine.
281 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
282 *
283 * @return Success indicator. if not S_OK, the machine object is invalid
284 */
285HRESULT Machine::init(VirtualBox *aParent,
286 const Utf8Str &strConfigFile,
287 const Utf8Str &strName,
288 const StringsList &llGroups,
289 GuestOSType *aOsType,
290 const Guid &aId,
291 bool fForceOverwrite,
292 bool fDirectoryIncludesUUID)
293{
294 LogFlowThisFuncEnter();
295 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
296
297 /* Enclose the state transition NotReady->InInit->Ready */
298 AutoInitSpan autoInitSpan(this);
299 AssertReturn(autoInitSpan.isOk(), E_FAIL);
300
301 HRESULT rc = initImpl(aParent, strConfigFile);
302 if (FAILED(rc)) return rc;
303
304 rc = tryCreateMachineConfigFile(fForceOverwrite);
305 if (FAILED(rc)) return rc;
306
307 if (SUCCEEDED(rc))
308 {
309 // create an empty machine config
310 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
311
312 rc = initDataAndChildObjects();
313 }
314
315 if (SUCCEEDED(rc))
316 {
317 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
318 mData->mAccessible = TRUE;
319
320 unconst(mData->mUuid) = aId;
321
322 mUserData->s.strName = strName;
323
324 mUserData->s.llGroups = llGroups;
325
326 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
327 // the "name sync" flag determines whether the machine directory gets renamed along
328 // with the machine file; say so if the settings file name is the same as the
329 // settings file parent directory (machine directory)
330 mUserData->s.fNameSync = isInOwnDir();
331
332 // initialize the default snapshots folder
333 rc = COMSETTER(SnapshotFolder)(NULL);
334 AssertComRC(rc);
335
336 if (aOsType)
337 {
338 /* Store OS type */
339 mUserData->s.strOsType = aOsType->i_id();
340
341 /* Apply BIOS defaults */
342 mBIOSSettings->i_applyDefaults(aOsType);
343
344 /* Apply network adapters defaults */
345 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
346 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
347
348 /* Apply serial port defaults */
349 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
350 mSerialPorts[slot]->i_applyDefaults(aOsType);
351
352 /* Let the OS type select 64-bit ness. */
353 mHWData->mLongMode = aOsType->i_is64Bit()
354 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
355 }
356
357 /* At this point the changing of the current state modification
358 * flag is allowed. */
359 allowStateModification();
360
361 /* commit all changes made during the initialization */
362 commit();
363 }
364
365 /* Confirm a successful initialization when it's the case */
366 if (SUCCEEDED(rc))
367 {
368 if (mData->mAccessible)
369 autoInitSpan.setSucceeded();
370 else
371 autoInitSpan.setLimited();
372 }
373
374 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
375 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
376 mData->mRegistered,
377 mData->mAccessible,
378 rc));
379
380 LogFlowThisFuncLeave();
381
382 return rc;
383}
384
385/**
386 * Initializes a new instance with data from machine XML (formerly Init_Registered).
387 * Gets called in two modes:
388 *
389 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
390 * UUID is specified and we mark the machine as "registered";
391 *
392 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
393 * and the machine remains unregistered until RegisterMachine() is called.
394 *
395 * @param aParent Associated parent object
396 * @param aConfigFile Local file system path to the VM settings file (can
397 * be relative to the VirtualBox config directory).
398 * @param aId UUID of the machine or NULL (see above).
399 *
400 * @return Success indicator. if not S_OK, the machine object is invalid
401 */
402HRESULT Machine::initFromSettings(VirtualBox *aParent,
403 const Utf8Str &strConfigFile,
404 const Guid *aId)
405{
406 LogFlowThisFuncEnter();
407 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
408
409 /* Enclose the state transition NotReady->InInit->Ready */
410 AutoInitSpan autoInitSpan(this);
411 AssertReturn(autoInitSpan.isOk(), E_FAIL);
412
413 HRESULT rc = initImpl(aParent, strConfigFile);
414 if (FAILED(rc)) return rc;
415
416 if (aId)
417 {
418 // loading a registered VM:
419 unconst(mData->mUuid) = *aId;
420 mData->mRegistered = TRUE;
421 // now load the settings from XML:
422 rc = registeredInit();
423 // this calls initDataAndChildObjects() and loadSettings()
424 }
425 else
426 {
427 // opening an unregistered VM (VirtualBox::OpenMachine()):
428 rc = initDataAndChildObjects();
429
430 if (SUCCEEDED(rc))
431 {
432 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
433 mData->mAccessible = TRUE;
434
435 try
436 {
437 // load and parse machine XML; this will throw on XML or logic errors
438 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
439
440 // reject VM UUID duplicates, they can happen if someone
441 // tries to register an already known VM config again
442 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
443 true /* fPermitInaccessible */,
444 false /* aDoSetError */,
445 NULL) != VBOX_E_OBJECT_NOT_FOUND)
446 {
447 throw setError(E_FAIL,
448 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
449 mData->m_strConfigFile.c_str());
450 }
451
452 // use UUID from machine config
453 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
454
455 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
456 NULL /* puuidRegistry */);
457 if (FAILED(rc)) throw rc;
458
459 /* At this point the changing of the current state modification
460 * flag is allowed. */
461 allowStateModification();
462
463 commit();
464 }
465 catch (HRESULT err)
466 {
467 /* we assume that error info is set by the thrower */
468 rc = err;
469 }
470 catch (...)
471 {
472 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
473 }
474 }
475 }
476
477 /* Confirm a successful initialization when it's the case */
478 if (SUCCEEDED(rc))
479 {
480 if (mData->mAccessible)
481 autoInitSpan.setSucceeded();
482 else
483 {
484 autoInitSpan.setLimited();
485
486 // uninit media from this machine's media registry, or else
487 // reloading the settings will fail
488 mParent->i_unregisterMachineMedia(getId());
489 }
490 }
491
492 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
493 "rc=%08X\n",
494 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
495 mData->mRegistered, mData->mAccessible, rc));
496
497 LogFlowThisFuncLeave();
498
499 return rc;
500}
501
502/**
503 * Initializes a new instance from a machine config that is already in memory
504 * (import OVF case). Since we are importing, the UUID in the machine
505 * config is ignored and we always generate a fresh one.
506 *
507 * @param strName Name for the new machine; this overrides what is specified in config and is used
508 * for the settings file as well.
509 * @param config Machine configuration loaded and parsed from XML.
510 *
511 * @return Success indicator. if not S_OK, the machine object is invalid
512 */
513HRESULT Machine::init(VirtualBox *aParent,
514 const Utf8Str &strName,
515 const settings::MachineConfigFile &config)
516{
517 LogFlowThisFuncEnter();
518
519 /* Enclose the state transition NotReady->InInit->Ready */
520 AutoInitSpan autoInitSpan(this);
521 AssertReturn(autoInitSpan.isOk(), E_FAIL);
522
523 Utf8Str strConfigFile;
524 aParent->i_getDefaultMachineFolder(strConfigFile);
525 strConfigFile.append(RTPATH_DELIMITER);
526 strConfigFile.append(strName);
527 strConfigFile.append(RTPATH_DELIMITER);
528 strConfigFile.append(strName);
529 strConfigFile.append(".vbox");
530
531 HRESULT rc = initImpl(aParent, strConfigFile);
532 if (FAILED(rc)) return rc;
533
534 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
535 if (FAILED(rc)) return rc;
536
537 rc = initDataAndChildObjects();
538
539 if (SUCCEEDED(rc))
540 {
541 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
542 mData->mAccessible = TRUE;
543
544 // create empty machine config for instance data
545 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
546
547 // generate fresh UUID, ignore machine config
548 unconst(mData->mUuid).create();
549
550 rc = loadMachineDataFromSettings(config,
551 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
552
553 // override VM name as well, it may be different
554 mUserData->s.strName = strName;
555
556 if (SUCCEEDED(rc))
557 {
558 /* At this point the changing of the current state modification
559 * flag is allowed. */
560 allowStateModification();
561
562 /* commit all changes made during the initialization */
563 commit();
564 }
565 }
566
567 /* Confirm a successful initialization when it's the case */
568 if (SUCCEEDED(rc))
569 {
570 if (mData->mAccessible)
571 autoInitSpan.setSucceeded();
572 else
573 {
574 autoInitSpan.setLimited();
575
576 // uninit media from this machine's media registry, or else
577 // reloading the settings will fail
578 mParent->i_unregisterMachineMedia(getId());
579 }
580 }
581
582 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
583 "rc=%08X\n",
584 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
585 mData->mRegistered, mData->mAccessible, rc));
586
587 LogFlowThisFuncLeave();
588
589 return rc;
590}
591
592/**
593 * Shared code between the various init() implementations.
594 * @param aParent
595 * @return
596 */
597HRESULT Machine::initImpl(VirtualBox *aParent,
598 const Utf8Str &strConfigFile)
599{
600 LogFlowThisFuncEnter();
601
602 AssertReturn(aParent, E_INVALIDARG);
603 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
604
605 HRESULT rc = S_OK;
606
607 /* share the parent weakly */
608 unconst(mParent) = aParent;
609
610 /* allocate the essential machine data structure (the rest will be
611 * allocated later by initDataAndChildObjects() */
612 mData.allocate();
613
614 /* memorize the config file name (as provided) */
615 mData->m_strConfigFile = strConfigFile;
616
617 /* get the full file name */
618 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
619 if (RT_FAILURE(vrc1))
620 return setError(VBOX_E_FILE_ERROR,
621 tr("Invalid machine settings file name '%s' (%Rrc)"),
622 strConfigFile.c_str(),
623 vrc1);
624
625 LogFlowThisFuncLeave();
626
627 return rc;
628}
629
630/**
631 * Tries to create a machine settings file in the path stored in the machine
632 * instance data. Used when a new machine is created to fail gracefully if
633 * the settings file could not be written (e.g. because machine dir is read-only).
634 * @return
635 */
636HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
637{
638 HRESULT rc = S_OK;
639
640 // when we create a new machine, we must be able to create the settings file
641 RTFILE f = NIL_RTFILE;
642 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
643 if ( RT_SUCCESS(vrc)
644 || vrc == VERR_SHARING_VIOLATION
645 )
646 {
647 if (RT_SUCCESS(vrc))
648 RTFileClose(f);
649 if (!fForceOverwrite)
650 rc = setError(VBOX_E_FILE_ERROR,
651 tr("Machine settings file '%s' already exists"),
652 mData->m_strConfigFileFull.c_str());
653 else
654 {
655 /* try to delete the config file, as otherwise the creation
656 * of a new settings file will fail. */
657 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
658 if (RT_FAILURE(vrc2))
659 rc = setError(VBOX_E_FILE_ERROR,
660 tr("Could not delete the existing settings file '%s' (%Rrc)"),
661 mData->m_strConfigFileFull.c_str(), vrc2);
662 }
663 }
664 else if ( vrc != VERR_FILE_NOT_FOUND
665 && vrc != VERR_PATH_NOT_FOUND
666 )
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Invalid machine settings file name '%s' (%Rrc)"),
669 mData->m_strConfigFileFull.c_str(),
670 vrc);
671 return rc;
672}
673
674/**
675 * Initializes the registered machine by loading the settings file.
676 * This method is separated from #init() in order to make it possible to
677 * retry the operation after VirtualBox startup instead of refusing to
678 * startup the whole VirtualBox server in case if the settings file of some
679 * registered VM is invalid or inaccessible.
680 *
681 * @note Must be always called from this object's write lock
682 * (unless called from #init() that doesn't need any locking).
683 * @note Locks the mUSBController method for writing.
684 * @note Subclasses must not call this method.
685 */
686HRESULT Machine::registeredInit()
687{
688 AssertReturn(!isSessionMachine(), E_FAIL);
689 AssertReturn(!isSnapshotMachine(), E_FAIL);
690 AssertReturn(mData->mUuid.isValid(), E_FAIL);
691 AssertReturn(!mData->mAccessible, E_FAIL);
692
693 HRESULT rc = initDataAndChildObjects();
694
695 if (SUCCEEDED(rc))
696 {
697 /* Temporarily reset the registered flag in order to let setters
698 * potentially called from loadSettings() succeed (isMutable() used in
699 * all setters will return FALSE for a Machine instance if mRegistered
700 * is TRUE). */
701 mData->mRegistered = FALSE;
702
703 try
704 {
705 // load and parse machine XML; this will throw on XML or logic errors
706 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
707
708 if (mData->mUuid != mData->pMachineConfigFile->uuid)
709 throw setError(E_FAIL,
710 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
711 mData->pMachineConfigFile->uuid.raw(),
712 mData->m_strConfigFileFull.c_str(),
713 mData->mUuid.toString().c_str(),
714 mParent->i_settingsFilePath().c_str());
715
716 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
717 NULL /* const Guid *puuidRegistry */);
718 if (FAILED(rc)) throw rc;
719 }
720 catch (HRESULT err)
721 {
722 /* we assume that error info is set by the thrower */
723 rc = err;
724 }
725 catch (...)
726 {
727 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
728 }
729
730 /* Restore the registered flag (even on failure) */
731 mData->mRegistered = TRUE;
732 }
733
734 if (SUCCEEDED(rc))
735 {
736 /* Set mAccessible to TRUE only if we successfully locked and loaded
737 * the settings file */
738 mData->mAccessible = TRUE;
739
740 /* commit all changes made during loading the settings file */
741 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
742 /// @todo r=klaus for some reason the settings loading logic backs up
743 // the settings, and therefore a commit is needed. Should probably be changed.
744 }
745 else
746 {
747 /* If the machine is registered, then, instead of returning a
748 * failure, we mark it as inaccessible and set the result to
749 * success to give it a try later */
750
751 /* fetch the current error info */
752 mData->mAccessError = com::ErrorInfo();
753 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
754 mData->mUuid.raw(),
755 mData->mAccessError.getText().raw()));
756
757 /* rollback all changes */
758 rollback(false /* aNotify */);
759
760 // uninit media from this machine's media registry, or else
761 // reloading the settings will fail
762 mParent->i_unregisterMachineMedia(getId());
763
764 /* uninitialize the common part to make sure all data is reset to
765 * default (null) values */
766 uninitDataAndChildObjects();
767
768 rc = S_OK;
769 }
770
771 return rc;
772}
773
774/**
775 * Uninitializes the instance.
776 * Called either from FinalRelease() or by the parent when it gets destroyed.
777 *
778 * @note The caller of this method must make sure that this object
779 * a) doesn't have active callers on the current thread and b) is not locked
780 * by the current thread; otherwise uninit() will hang either a) due to
781 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
782 * a dead-lock caused by this thread waiting for all callers on the other
783 * threads are done but preventing them from doing so by holding a lock.
784 */
785void Machine::uninit()
786{
787 LogFlowThisFuncEnter();
788
789 Assert(!isWriteLockOnCurrentThread());
790
791 Assert(!uRegistryNeedsSaving);
792 if (uRegistryNeedsSaving)
793 {
794 AutoCaller autoCaller(this);
795 if (SUCCEEDED(autoCaller.rc()))
796 {
797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
798 saveSettings(NULL, Machine::SaveS_Force);
799 }
800 }
801
802 /* Enclose the state transition Ready->InUninit->NotReady */
803 AutoUninitSpan autoUninitSpan(this);
804 if (autoUninitSpan.uninitDone())
805 return;
806
807 Assert(!isSnapshotMachine());
808 Assert(!isSessionMachine());
809 Assert(!!mData);
810
811 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
812 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
813
814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
815
816 if (!mData->mSession.mMachine.isNull())
817 {
818 /* Theoretically, this can only happen if the VirtualBox server has been
819 * terminated while there were clients running that owned open direct
820 * sessions. Since in this case we are definitely called by
821 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
822 * won't happen on the client watcher thread (because it does
823 * VirtualBox::addCaller() for the duration of the
824 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
825 * cannot happen until the VirtualBox caller is released). This is
826 * important, because SessionMachine::uninit() cannot correctly operate
827 * after we return from this method (it expects the Machine instance is
828 * still valid). We'll call it ourselves below.
829 */
830 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
831 (SessionMachine*)mData->mSession.mMachine));
832
833 if (Global::IsOnlineOrTransient(mData->mMachineState))
834 {
835 LogWarningThisFunc(("Setting state to Aborted!\n"));
836 /* set machine state using SessionMachine reimplementation */
837 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
838 }
839
840 /*
841 * Uninitialize SessionMachine using public uninit() to indicate
842 * an unexpected uninitialization.
843 */
844 mData->mSession.mMachine->uninit();
845 /* SessionMachine::uninit() must set mSession.mMachine to null */
846 Assert(mData->mSession.mMachine.isNull());
847 }
848
849 // uninit media from this machine's media registry, if they're still there
850 Guid uuidMachine(getId());
851
852 /* the lock is no more necessary (SessionMachine is uninitialized) */
853 alock.release();
854
855 /* XXX This will fail with
856 * "cannot be closed because it is still attached to 1 virtual machines"
857 * because at this point we did not call uninitDataAndChildObjects() yet
858 * and therefore also removeBackReference() for all these mediums was not called! */
859
860 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
861 mParent->i_unregisterMachineMedia(uuidMachine);
862
863 // has machine been modified?
864 if (mData->flModifications)
865 {
866 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
867 rollback(false /* aNotify */);
868 }
869
870 if (mData->mAccessible)
871 uninitDataAndChildObjects();
872
873 /* free the essential data structure last */
874 mData.free();
875
876 LogFlowThisFuncLeave();
877}
878
879// IMachine properties
880/////////////////////////////////////////////////////////////////////////////
881
882STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
883{
884 CheckComArgOutPointerValid(aParent);
885
886 AutoLimitedCaller autoCaller(this);
887 if (FAILED(autoCaller.rc())) return autoCaller.rc();
888
889 /* mParent is constant during life time, no need to lock */
890 ComObjPtr<VirtualBox> pVirtualBox(mParent);
891 pVirtualBox.queryInterfaceTo(aParent);
892
893 return S_OK;
894}
895
896STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
897{
898 CheckComArgOutPointerValid(aAccessible);
899
900 AutoLimitedCaller autoCaller(this);
901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
902
903 LogFlowThisFunc(("ENTER\n"));
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 = 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
954STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
955{
956 CheckComArgOutPointerValid(aAccessError);
957
958 AutoLimitedCaller autoCaller(this);
959 if (FAILED(autoCaller.rc())) return autoCaller.rc();
960
961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
962
963 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
964 {
965 /* return shortly */
966 aAccessError = NULL;
967 return S_OK;
968 }
969
970 HRESULT rc = S_OK;
971
972 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
973 rc = errorInfo.createObject();
974 if (SUCCEEDED(rc))
975 {
976 errorInfo->init(mData->mAccessError.getResultCode(),
977 mData->mAccessError.getInterfaceID().ref(),
978 Utf8Str(mData->mAccessError.getComponent()).c_str(),
979 Utf8Str(mData->mAccessError.getText()));
980 rc = errorInfo.queryInterfaceTo(aAccessError);
981 }
982
983 return rc;
984}
985
986STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
987{
988 CheckComArgOutPointerValid(aName);
989
990 AutoCaller autoCaller(this);
991 if (FAILED(autoCaller.rc())) return autoCaller.rc();
992
993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 mUserData->s.strName.cloneTo(aName);
996
997 return S_OK;
998}
999
1000STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1001{
1002 CheckComArgStrNotEmptyOrNull(aName);
1003
1004 AutoCaller autoCaller(this);
1005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1006
1007 // prohibit setting a UUID only as the machine name, or else it can
1008 // never be found by findMachine()
1009 Guid test(aName);
1010
1011 if (test.isValid())
1012 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1013
1014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1015
1016 HRESULT rc = checkStateDependency(MutableStateDep);
1017 if (FAILED(rc)) return rc;
1018
1019 setModified(IsModified_MachineData);
1020 mUserData.backup();
1021 mUserData->s.strName = aName;
1022
1023 return S_OK;
1024}
1025
1026STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1027{
1028 CheckComArgOutPointerValid(aDescription);
1029
1030 AutoCaller autoCaller(this);
1031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1032
1033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 mUserData->s.strDescription.cloneTo(aDescription);
1036
1037 return S_OK;
1038}
1039
1040STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1041{
1042 AutoCaller autoCaller(this);
1043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1044
1045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1046
1047 // this can be done in principle in any state as it doesn't affect the VM
1048 // significantly, but play safe by not messing around while complex
1049 // activities are going on
1050 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1051 if (FAILED(rc)) return rc;
1052
1053 setModified(IsModified_MachineData);
1054 mUserData.backup();
1055 mUserData->s.strDescription = aDescription;
1056
1057 return S_OK;
1058}
1059
1060STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1061{
1062 CheckComArgOutPointerValid(aId);
1063
1064 AutoLimitedCaller autoCaller(this);
1065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1066
1067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1068
1069 mData->mUuid.toUtf16().cloneTo(aId);
1070
1071 return S_OK;
1072}
1073
1074STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1075{
1076 CheckComArgOutSafeArrayPointerValid(aGroups);
1077
1078 AutoCaller autoCaller(this);
1079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1080
1081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1082 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1083 size_t i = 0;
1084 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1085 it != mUserData->s.llGroups.end();
1086 ++it, i++)
1087 {
1088 Bstr tmp = *it;
1089 tmp.cloneTo(&groups[i]);
1090 }
1091 groups.detachTo(ComSafeArrayOutArg(aGroups));
1092
1093 return S_OK;
1094}
1095
1096STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1097{
1098 AutoCaller autoCaller(this);
1099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1100 std::vector<com::Utf8Str> tmplist;
1101 SafeArray<IN_BSTR> tmp(ComSafeArrayInArg(aGroups));
1102 tmplist.resize(tmp.size());
1103 for (size_t i = 0; i < tmp.size(); ++i)
1104 tmplist[i] = Utf8Str(tmp[i]);
1105
1106 StringsList llGroups;
1107 HRESULT rc = mParent->i_convertMachineGroups(tmplist, &llGroups);
1108 if (FAILED(rc))
1109 return rc;
1110
1111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1112
1113 // changing machine groups is possible while the VM is offline
1114 rc = checkStateDependency(OfflineStateDep);
1115 if (FAILED(rc)) return rc;
1116
1117 setModified(IsModified_MachineData);
1118 mUserData.backup();
1119 mUserData->s.llGroups = llGroups;
1120
1121 return S_OK;
1122}
1123
1124STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1125{
1126 CheckComArgOutPointerValid(aOSTypeId);
1127
1128 AutoCaller autoCaller(this);
1129 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1130
1131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1132
1133 mUserData->s.strOsType.cloneTo(aOSTypeId);
1134
1135 return S_OK;
1136}
1137
1138STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1139{
1140 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1141
1142 AutoCaller autoCaller(this);
1143 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1144
1145 /* look up the object by Id to check it is valid */
1146 ComPtr<IGuestOSType> guestOSType;
1147 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1148 if (FAILED(rc)) return rc;
1149
1150 /* when setting, always use the "etalon" value for consistency -- lookup
1151 * by ID is case-insensitive and the input value may have different case */
1152 Bstr osTypeId;
1153 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1154 if (FAILED(rc)) return rc;
1155
1156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 rc = checkStateDependency(MutableStateDep);
1159 if (FAILED(rc)) return rc;
1160
1161 setModified(IsModified_MachineData);
1162 mUserData.backup();
1163 mUserData->s.strOsType = osTypeId;
1164
1165 return S_OK;
1166}
1167
1168
1169STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1170{
1171 CheckComArgOutPointerValid(aFirmwareType);
1172
1173 AutoCaller autoCaller(this);
1174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1175
1176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 *aFirmwareType = mHWData->mFirmwareType;
1179
1180 return S_OK;
1181}
1182
1183STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1184{
1185 AutoCaller autoCaller(this);
1186 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1188
1189 HRESULT rc = checkStateDependency(MutableStateDep);
1190 if (FAILED(rc)) return rc;
1191
1192 setModified(IsModified_MachineData);
1193 mHWData.backup();
1194 mHWData->mFirmwareType = aFirmwareType;
1195
1196 return S_OK;
1197}
1198
1199STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1200{
1201 CheckComArgOutPointerValid(aKeyboardHIDType);
1202
1203 AutoCaller autoCaller(this);
1204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1205
1206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1209
1210 return S_OK;
1211}
1212
1213STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1214{
1215 AutoCaller autoCaller(this);
1216 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1218
1219 HRESULT rc = checkStateDependency(MutableStateDep);
1220 if (FAILED(rc)) return rc;
1221
1222 setModified(IsModified_MachineData);
1223 mHWData.backup();
1224 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1225
1226 return S_OK;
1227}
1228
1229STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1230{
1231 CheckComArgOutPointerValid(aPointingHIDType);
1232
1233 AutoCaller autoCaller(this);
1234 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1235
1236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1237
1238 *aPointingHIDType = mHWData->mPointingHIDType;
1239
1240 return S_OK;
1241}
1242
1243STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1244{
1245 AutoCaller autoCaller(this);
1246 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 HRESULT rc = checkStateDependency(MutableStateDep);
1250 if (FAILED(rc)) return rc;
1251
1252 setModified(IsModified_MachineData);
1253 mHWData.backup();
1254 mHWData->mPointingHIDType = aPointingHIDType;
1255
1256 return S_OK;
1257}
1258
1259STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1260{
1261 CheckComArgOutPointerValid(aChipsetType);
1262
1263 AutoCaller autoCaller(this);
1264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1265
1266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 *aChipsetType = mHWData->mChipsetType;
1269
1270 return S_OK;
1271}
1272
1273STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1274{
1275 AutoCaller autoCaller(this);
1276 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1278
1279 HRESULT rc = checkStateDependency(MutableStateDep);
1280 if (FAILED(rc)) return rc;
1281
1282 if (aChipsetType != mHWData->mChipsetType)
1283 {
1284 setModified(IsModified_MachineData);
1285 mHWData.backup();
1286 mHWData->mChipsetType = aChipsetType;
1287
1288 // Resize network adapter array, to be finalized on commit/rollback.
1289 // We must not throw away entries yet, otherwise settings are lost
1290 // without a way to roll back.
1291 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1292 size_t oldCount = mNetworkAdapters.size();
1293 if (newCount > oldCount)
1294 {
1295 mNetworkAdapters.resize(newCount);
1296 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1297 {
1298 unconst(mNetworkAdapters[slot]).createObject();
1299 mNetworkAdapters[slot]->init(this, slot);
1300 }
1301 }
1302 }
1303
1304 return S_OK;
1305}
1306
1307STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1308{
1309 CheckComArgOutPointerValid(aHWVersion);
1310
1311 AutoCaller autoCaller(this);
1312 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1313
1314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1315
1316 mHWData->mHWVersion.cloneTo(aHWVersion);
1317
1318 return S_OK;
1319}
1320
1321STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1322{
1323 /* check known version */
1324 Utf8Str hwVersion = aHWVersion;
1325 if ( hwVersion.compare("1") != 0
1326 && hwVersion.compare("2") != 0)
1327 return setError(E_INVALIDARG,
1328 tr("Invalid hardware version: %ls\n"), aHWVersion);
1329
1330 AutoCaller autoCaller(this);
1331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1332
1333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1334
1335 HRESULT rc = checkStateDependency(MutableStateDep);
1336 if (FAILED(rc)) return rc;
1337
1338 setModified(IsModified_MachineData);
1339 mHWData.backup();
1340 mHWData->mHWVersion = hwVersion;
1341
1342 return S_OK;
1343}
1344
1345STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1346{
1347 CheckComArgOutPointerValid(aUUID);
1348
1349 AutoCaller autoCaller(this);
1350 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1351
1352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1353
1354 if (!mHWData->mHardwareUUID.isZero())
1355 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1356 else
1357 mData->mUuid.toUtf16().cloneTo(aUUID);
1358
1359 return S_OK;
1360}
1361
1362STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1363{
1364 Guid hardwareUUID(aUUID);
1365 if (!hardwareUUID.isValid())
1366 return E_INVALIDARG;
1367
1368 AutoCaller autoCaller(this);
1369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1370
1371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1372
1373 HRESULT rc = checkStateDependency(MutableStateDep);
1374 if (FAILED(rc)) return rc;
1375
1376 setModified(IsModified_MachineData);
1377 mHWData.backup();
1378 if (hardwareUUID == mData->mUuid)
1379 mHWData->mHardwareUUID.clear();
1380 else
1381 mHWData->mHardwareUUID = hardwareUUID;
1382
1383 return S_OK;
1384}
1385
1386STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1387{
1388 CheckComArgOutPointerValid(memorySize);
1389
1390 AutoCaller autoCaller(this);
1391 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1392
1393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1394
1395 *memorySize = mHWData->mMemorySize;
1396
1397 return S_OK;
1398}
1399
1400STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1401{
1402 /* check RAM limits */
1403 if ( memorySize < MM_RAM_MIN_IN_MB
1404 || memorySize > MM_RAM_MAX_IN_MB
1405 )
1406 return setError(E_INVALIDARG,
1407 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1408 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1409
1410 AutoCaller autoCaller(this);
1411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1412
1413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1414
1415 HRESULT rc = checkStateDependency(MutableStateDep);
1416 if (FAILED(rc)) return rc;
1417
1418 setModified(IsModified_MachineData);
1419 mHWData.backup();
1420 mHWData->mMemorySize = memorySize;
1421
1422 return S_OK;
1423}
1424
1425STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1426{
1427 CheckComArgOutPointerValid(CPUCount);
1428
1429 AutoCaller autoCaller(this);
1430 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1431
1432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1433
1434 *CPUCount = mHWData->mCPUCount;
1435
1436 return S_OK;
1437}
1438
1439STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1440{
1441 /* check CPU limits */
1442 if ( CPUCount < SchemaDefs::MinCPUCount
1443 || CPUCount > SchemaDefs::MaxCPUCount
1444 )
1445 return setError(E_INVALIDARG,
1446 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1447 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1448
1449 AutoCaller autoCaller(this);
1450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1451
1452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1455 if (mHWData->mCPUHotPlugEnabled)
1456 {
1457 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1458 {
1459 if (mHWData->mCPUAttached[idx])
1460 return setError(E_INVALIDARG,
1461 tr("There is still a CPU attached to socket %lu."
1462 "Detach the CPU before removing the socket"),
1463 CPUCount, idx+1);
1464 }
1465 }
1466
1467 HRESULT rc = checkStateDependency(MutableStateDep);
1468 if (FAILED(rc)) return rc;
1469
1470 setModified(IsModified_MachineData);
1471 mHWData.backup();
1472 mHWData->mCPUCount = CPUCount;
1473
1474 return S_OK;
1475}
1476
1477STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1478{
1479 CheckComArgOutPointerValid(aExecutionCap);
1480
1481 AutoCaller autoCaller(this);
1482 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1483
1484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1485
1486 *aExecutionCap = mHWData->mCpuExecutionCap;
1487
1488 return S_OK;
1489}
1490
1491STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1492{
1493 HRESULT rc = S_OK;
1494
1495 /* check throttle limits */
1496 if ( aExecutionCap < 1
1497 || aExecutionCap > 100
1498 )
1499 return setError(E_INVALIDARG,
1500 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1501 aExecutionCap, 1, 100);
1502
1503 AutoCaller autoCaller(this);
1504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1505
1506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1507
1508 alock.release();
1509 rc = onCPUExecutionCapChange(aExecutionCap);
1510 alock.acquire();
1511 if (FAILED(rc)) return rc;
1512
1513 setModified(IsModified_MachineData);
1514 mHWData.backup();
1515 mHWData->mCpuExecutionCap = aExecutionCap;
1516
1517 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1518 if (Global::IsOnline(mData->mMachineState))
1519 saveSettings(NULL);
1520
1521 return S_OK;
1522}
1523
1524
1525STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1526{
1527 CheckComArgOutPointerValid(aEnabled);
1528
1529 AutoCaller autoCaller(this);
1530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1531
1532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 *aEnabled = mHWData->mCPUHotPlugEnabled;
1535
1536 return S_OK;
1537}
1538
1539STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1540{
1541 HRESULT rc = S_OK;
1542
1543 AutoCaller autoCaller(this);
1544 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1545
1546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1547
1548 rc = checkStateDependency(MutableStateDep);
1549 if (FAILED(rc)) return rc;
1550
1551 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1552 {
1553 if (aEnabled)
1554 {
1555 setModified(IsModified_MachineData);
1556 mHWData.backup();
1557
1558 /* Add the amount of CPUs currently attached */
1559 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1560 {
1561 mHWData->mCPUAttached[i] = true;
1562 }
1563 }
1564 else
1565 {
1566 /*
1567 * We can disable hotplug only if the amount of maximum CPUs is equal
1568 * to the amount of attached CPUs
1569 */
1570 unsigned cCpusAttached = 0;
1571 unsigned iHighestId = 0;
1572
1573 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1574 {
1575 if (mHWData->mCPUAttached[i])
1576 {
1577 cCpusAttached++;
1578 iHighestId = i;
1579 }
1580 }
1581
1582 if ( (cCpusAttached != mHWData->mCPUCount)
1583 || (iHighestId >= mHWData->mCPUCount))
1584 return setError(E_INVALIDARG,
1585 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1586
1587 setModified(IsModified_MachineData);
1588 mHWData.backup();
1589 }
1590 }
1591
1592 mHWData->mCPUHotPlugEnabled = aEnabled;
1593
1594 return rc;
1595}
1596
1597STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1598{
1599#ifdef VBOX_WITH_USB_CARDREADER
1600 CheckComArgOutPointerValid(aEnabled);
1601
1602 AutoCaller autoCaller(this);
1603 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1604
1605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1606
1607 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1608
1609 return S_OK;
1610#else
1611 NOREF(aEnabled);
1612 return E_NOTIMPL;
1613#endif
1614}
1615
1616STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1617{
1618#ifdef VBOX_WITH_USB_CARDREADER
1619 AutoCaller autoCaller(this);
1620 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1622
1623 HRESULT rc = checkStateDependency(MutableStateDep);
1624 if (FAILED(rc)) return rc;
1625
1626 setModified(IsModified_MachineData);
1627 mHWData.backup();
1628 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1629
1630 return S_OK;
1631#else
1632 NOREF(aEnabled);
1633 return E_NOTIMPL;
1634#endif
1635}
1636
1637STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1638{
1639 CheckComArgOutPointerValid(aEnabled);
1640
1641 AutoCaller autoCaller(this);
1642 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1644
1645 *aEnabled = mHWData->mHPETEnabled;
1646
1647 return S_OK;
1648}
1649
1650STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1651{
1652 HRESULT rc = S_OK;
1653
1654 AutoCaller autoCaller(this);
1655 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1657
1658 rc = checkStateDependency(MutableStateDep);
1659 if (FAILED(rc)) return rc;
1660
1661 setModified(IsModified_MachineData);
1662 mHWData.backup();
1663
1664 mHWData->mHPETEnabled = aEnabled;
1665
1666 return rc;
1667}
1668
1669STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1670{
1671 AutoCaller autoCaller(this);
1672 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1673
1674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1675
1676 *fEnabled = mHWData->mVideoCaptureEnabled;
1677 return S_OK;
1678}
1679
1680STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1681{
1682 HRESULT rc = S_OK;
1683
1684 AutoCaller autoCaller(this);
1685 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1687
1688 setModified(IsModified_MachineData);
1689 mHWData.backup();
1690 mHWData->mVideoCaptureEnabled = fEnabled;
1691
1692 alock.release();
1693 rc = onVideoCaptureChange();
1694 alock.acquire();
1695 if (FAILED(rc))
1696 {
1697 /*
1698 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1699 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1700 * determine if it should start or stop capturing. Therefore we need to manually
1701 * undo change.
1702 */
1703 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1704 return rc;
1705 }
1706
1707 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1708 if (Global::IsOnline(mData->mMachineState))
1709 saveSettings(NULL);
1710
1711 return rc;
1712}
1713
1714STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1715{
1716 CheckComArgOutSafeArrayPointerValid(aScreens);
1717
1718 AutoCaller autoCaller(this);
1719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1720
1721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1722
1723 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1724 for (unsigned i = 0; i < screens.size(); i++)
1725 screens[i] = mHWData->maVideoCaptureScreens[i];
1726 screens.detachTo(ComSafeArrayOutArg(aScreens));
1727 return S_OK;
1728}
1729
1730STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1731{
1732 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1733 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1734 bool fChanged = false;
1735
1736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 for (unsigned i = 0; i < screens.size(); i++)
1739 {
1740 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1741 {
1742 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1743 fChanged = true;
1744 }
1745 }
1746 if (fChanged)
1747 {
1748 alock.release();
1749 HRESULT rc = onVideoCaptureChange();
1750 alock.acquire();
1751 if (FAILED(rc)) return rc;
1752 setModified(IsModified_MachineData);
1753
1754 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1755 if (Global::IsOnline(mData->mMachineState))
1756 saveSettings(NULL);
1757 }
1758
1759 return S_OK;
1760}
1761
1762STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1763{
1764 AutoCaller autoCaller(this);
1765 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1766
1767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1768 if (mHWData->mVideoCaptureFile.isEmpty())
1769 {
1770 Utf8Str defaultFile;
1771 getDefaultVideoCaptureFile(defaultFile);
1772 defaultFile.cloneTo(apFile);
1773 }
1774 else
1775 mHWData->mVideoCaptureFile.cloneTo(apFile);
1776 return S_OK;
1777}
1778
1779STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1780{
1781 Utf8Str strFile(aFile);
1782 AutoCaller autoCaller(this);
1783 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1784
1785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1786
1787 if ( Global::IsOnline(mData->mMachineState)
1788 && mHWData->mVideoCaptureEnabled)
1789 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1790
1791 if (!RTPathStartsWithRoot(strFile.c_str()))
1792 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1793
1794 if (!strFile.isEmpty())
1795 {
1796 Utf8Str defaultFile;
1797 getDefaultVideoCaptureFile(defaultFile);
1798 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1799 strFile.setNull();
1800 }
1801
1802 setModified(IsModified_MachineData);
1803 mHWData.backup();
1804 mHWData->mVideoCaptureFile = strFile;
1805
1806 return S_OK;
1807}
1808
1809STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1810{
1811 AutoCaller autoCaller(this);
1812 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1813
1814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1815 *aHorzRes = mHWData->mVideoCaptureWidth;
1816 return S_OK;
1817}
1818
1819STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1820{
1821 AutoCaller autoCaller(this);
1822 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1823
1824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1825
1826 if ( Global::IsOnline(mData->mMachineState)
1827 && mHWData->mVideoCaptureEnabled)
1828 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1829
1830 setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mVideoCaptureWidth = aHorzRes;
1833
1834 return S_OK;
1835}
1836
1837STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1838{
1839 AutoCaller autoCaller(this);
1840 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1841
1842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1843 *aVertRes = mHWData->mVideoCaptureHeight;
1844 return S_OK;
1845}
1846
1847STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1848{
1849 AutoCaller autoCaller(this);
1850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1851
1852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1853
1854 if ( Global::IsOnline(mData->mMachineState)
1855 && mHWData->mVideoCaptureEnabled)
1856 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1857
1858 setModified(IsModified_MachineData);
1859 mHWData.backup();
1860 mHWData->mVideoCaptureHeight = aVertRes;
1861
1862 return S_OK;
1863}
1864
1865STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1866{
1867 AutoCaller autoCaller(this);
1868 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1869
1870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1871 *aRate = mHWData->mVideoCaptureRate;
1872 return S_OK;
1873}
1874
1875STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1876{
1877 AutoCaller autoCaller(this);
1878 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1879
1880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1881
1882 if ( Global::IsOnline(mData->mMachineState)
1883 && mHWData->mVideoCaptureEnabled)
1884 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1885
1886 setModified(IsModified_MachineData);
1887 mHWData.backup();
1888 mHWData->mVideoCaptureRate = aRate;
1889
1890 return S_OK;
1891}
1892
1893STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1894{
1895 AutoCaller autoCaller(this);
1896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1897
1898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1899 *aFPS = mHWData->mVideoCaptureFPS;
1900 return S_OK;
1901}
1902
1903STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1904{
1905 AutoCaller autoCaller(this);
1906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1907
1908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1909
1910 if ( Global::IsOnline(mData->mMachineState)
1911 && mHWData->mVideoCaptureEnabled)
1912 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1913
1914 setModified(IsModified_MachineData);
1915 mHWData.backup();
1916 mHWData->mVideoCaptureFPS = aFPS;
1917
1918 return S_OK;
1919}
1920
1921STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1922{
1923 CheckComArgOutPointerValid(aGraphicsControllerType);
1924
1925 AutoCaller autoCaller(this);
1926 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1927
1928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1929
1930 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1931
1932 return S_OK;
1933}
1934
1935STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1936{
1937 switch (aGraphicsControllerType)
1938 {
1939 case GraphicsControllerType_Null:
1940 case GraphicsControllerType_VBoxVGA:
1941#ifdef VBOX_WITH_VMSVGA
1942 case GraphicsControllerType_VMSVGA:
1943#endif
1944 break;
1945 default:
1946 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1947 }
1948
1949 AutoCaller autoCaller(this);
1950 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1951
1952 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1953
1954 HRESULT rc = checkStateDependency(MutableStateDep);
1955 if (FAILED(rc)) return rc;
1956
1957 setModified(IsModified_MachineData);
1958 mHWData.backup();
1959 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1960
1961 return S_OK;
1962}
1963
1964STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1965{
1966 CheckComArgOutPointerValid(memorySize);
1967
1968 AutoCaller autoCaller(this);
1969 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1970
1971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 *memorySize = mHWData->mVRAMSize;
1974
1975 return S_OK;
1976}
1977
1978STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1979{
1980 /* check VRAM limits */
1981 if (memorySize < SchemaDefs::MinGuestVRAM ||
1982 memorySize > SchemaDefs::MaxGuestVRAM)
1983 return setError(E_INVALIDARG,
1984 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1985 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1986
1987 AutoCaller autoCaller(this);
1988 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1989
1990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1991
1992 HRESULT rc = checkStateDependency(MutableStateDep);
1993 if (FAILED(rc)) return rc;
1994
1995 setModified(IsModified_MachineData);
1996 mHWData.backup();
1997 mHWData->mVRAMSize = memorySize;
1998
1999 return S_OK;
2000}
2001
2002/** @todo this method should not be public */
2003STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2004{
2005 CheckComArgOutPointerValid(memoryBalloonSize);
2006
2007 AutoCaller autoCaller(this);
2008 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2009
2010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2011
2012 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2013
2014 return S_OK;
2015}
2016
2017/**
2018 * Set the memory balloon size.
2019 *
2020 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2021 * we have to make sure that we never call IGuest from here.
2022 */
2023STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2024{
2025 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2026#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2027 /* check limits */
2028 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2029 return setError(E_INVALIDARG,
2030 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2031 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2032
2033 AutoCaller autoCaller(this);
2034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2035
2036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2037
2038 setModified(IsModified_MachineData);
2039 mHWData.backup();
2040 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2041
2042 return S_OK;
2043#else
2044 NOREF(memoryBalloonSize);
2045 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2046#endif
2047}
2048
2049STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2050{
2051 CheckComArgOutPointerValid(aEnabled);
2052
2053 AutoCaller autoCaller(this);
2054 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2055
2056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2057
2058 *aEnabled = mHWData->mPageFusionEnabled;
2059 return S_OK;
2060}
2061
2062STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2063{
2064#ifdef VBOX_WITH_PAGE_SHARING
2065 AutoCaller autoCaller(this);
2066 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2067
2068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2069
2070 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2071 setModified(IsModified_MachineData);
2072 mHWData.backup();
2073 mHWData->mPageFusionEnabled = aEnabled;
2074 return S_OK;
2075#else
2076 NOREF(aEnabled);
2077 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2078#endif
2079}
2080
2081STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2082{
2083 CheckComArgOutPointerValid(aEnabled);
2084
2085 AutoCaller autoCaller(this);
2086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2087
2088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2089
2090 *aEnabled = mHWData->mAccelerate3DEnabled;
2091
2092 return S_OK;
2093}
2094
2095STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2096{
2097 AutoCaller autoCaller(this);
2098 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2099
2100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2101
2102 HRESULT rc = checkStateDependency(MutableStateDep);
2103 if (FAILED(rc)) return rc;
2104
2105 /** @todo check validity! */
2106
2107 setModified(IsModified_MachineData);
2108 mHWData.backup();
2109 mHWData->mAccelerate3DEnabled = enable;
2110
2111 return S_OK;
2112}
2113
2114
2115STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2116{
2117 CheckComArgOutPointerValid(aEnabled);
2118
2119 AutoCaller autoCaller(this);
2120 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2121
2122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2123
2124 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2125
2126 return S_OK;
2127}
2128
2129STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2130{
2131 AutoCaller autoCaller(this);
2132 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2133
2134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2135
2136 HRESULT rc = checkStateDependency(MutableStateDep);
2137 if (FAILED(rc)) return rc;
2138
2139 /** @todo check validity! */
2140
2141 setModified(IsModified_MachineData);
2142 mHWData.backup();
2143 mHWData->mAccelerate2DVideoEnabled = enable;
2144
2145 return S_OK;
2146}
2147
2148STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2149{
2150 CheckComArgOutPointerValid(monitorCount);
2151
2152 AutoCaller autoCaller(this);
2153 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2154
2155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2156
2157 *monitorCount = mHWData->mMonitorCount;
2158
2159 return S_OK;
2160}
2161
2162STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2163{
2164 /* make sure monitor count is a sensible number */
2165 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2166 return setError(E_INVALIDARG,
2167 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2168 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2169
2170 AutoCaller autoCaller(this);
2171 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2172
2173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2174
2175 HRESULT rc = checkStateDependency(MutableStateDep);
2176 if (FAILED(rc)) return rc;
2177
2178 setModified(IsModified_MachineData);
2179 mHWData.backup();
2180 mHWData->mMonitorCount = monitorCount;
2181
2182 return S_OK;
2183}
2184
2185STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2186{
2187 CheckComArgOutPointerValid(biosSettings);
2188
2189 AutoCaller autoCaller(this);
2190 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2191
2192 /* mBIOSSettings is constant during life time, no need to lock */
2193 mBIOSSettings.queryInterfaceTo(biosSettings);
2194
2195 return S_OK;
2196}
2197
2198STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2199{
2200 CheckComArgOutPointerValid(aVal);
2201
2202 AutoCaller autoCaller(this);
2203 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2204
2205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2206
2207 switch (property)
2208 {
2209 case CPUPropertyType_PAE:
2210 *aVal = mHWData->mPAEEnabled;
2211 break;
2212
2213 case CPUPropertyType_Synthetic:
2214 *aVal = mHWData->mSyntheticCpu;
2215 break;
2216
2217 case CPUPropertyType_LongMode:
2218 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2219 *aVal = TRUE;
2220 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2221 *aVal = FALSE;
2222#if HC_ARCH_BITS == 64
2223 else
2224 *aVal = TRUE;
2225#else
2226 else
2227 {
2228 *aVal = FALSE;
2229
2230 ComPtr<IGuestOSType> ptrGuestOSType;
2231 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2232 if (SUCCEEDED(hrc2))
2233 {
2234 BOOL fIs64Bit = FALSE;
2235 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2236 if (SUCCEEDED(hrc2) && fIs64Bit)
2237 {
2238 ComObjPtr<Host> ptrHost = mParent->i_host();
2239 alock.release();
2240
2241 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2242 if (FAILED(hrc2))
2243 *aVal = FALSE;
2244 }
2245 }
2246 }
2247#endif
2248 break;
2249
2250 case CPUPropertyType_TripleFaultReset:
2251 *aVal = mHWData->mTripleFaultReset;
2252 break;
2253
2254 default:
2255 return E_INVALIDARG;
2256 }
2257 return S_OK;
2258}
2259
2260STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2261{
2262 AutoCaller autoCaller(this);
2263 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2264
2265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2266
2267 HRESULT rc = checkStateDependency(MutableStateDep);
2268 if (FAILED(rc)) return rc;
2269
2270 switch (property)
2271 {
2272 case CPUPropertyType_PAE:
2273 setModified(IsModified_MachineData);
2274 mHWData.backup();
2275 mHWData->mPAEEnabled = !!aVal;
2276 break;
2277
2278 case CPUPropertyType_Synthetic:
2279 setModified(IsModified_MachineData);
2280 mHWData.backup();
2281 mHWData->mSyntheticCpu = !!aVal;
2282 break;
2283
2284 case CPUPropertyType_LongMode:
2285 setModified(IsModified_MachineData);
2286 mHWData.backup();
2287 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2288 break;
2289
2290 case CPUPropertyType_TripleFaultReset:
2291 setModified(IsModified_MachineData);
2292 mHWData.backup();
2293 mHWData->mTripleFaultReset = !!aVal;
2294 break;
2295
2296 default:
2297 return E_INVALIDARG;
2298 }
2299 return S_OK;
2300}
2301
2302STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2303{
2304 CheckComArgOutPointerValid(aValEax);
2305 CheckComArgOutPointerValid(aValEbx);
2306 CheckComArgOutPointerValid(aValEcx);
2307 CheckComArgOutPointerValid(aValEdx);
2308
2309 AutoCaller autoCaller(this);
2310 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2311
2312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2313
2314 switch(aId)
2315 {
2316 case 0x0:
2317 case 0x1:
2318 case 0x2:
2319 case 0x3:
2320 case 0x4:
2321 case 0x5:
2322 case 0x6:
2323 case 0x7:
2324 case 0x8:
2325 case 0x9:
2326 case 0xA:
2327 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2328 return E_INVALIDARG;
2329
2330 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2331 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2332 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2333 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2334 break;
2335
2336 case 0x80000000:
2337 case 0x80000001:
2338 case 0x80000002:
2339 case 0x80000003:
2340 case 0x80000004:
2341 case 0x80000005:
2342 case 0x80000006:
2343 case 0x80000007:
2344 case 0x80000008:
2345 case 0x80000009:
2346 case 0x8000000A:
2347 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2348 return E_INVALIDARG;
2349
2350 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2351 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2352 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2353 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2354 break;
2355
2356 default:
2357 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2358 }
2359 return S_OK;
2360}
2361
2362STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2363{
2364 AutoCaller autoCaller(this);
2365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2366
2367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2368
2369 HRESULT rc = checkStateDependency(MutableStateDep);
2370 if (FAILED(rc)) return rc;
2371
2372 switch(aId)
2373 {
2374 case 0x0:
2375 case 0x1:
2376 case 0x2:
2377 case 0x3:
2378 case 0x4:
2379 case 0x5:
2380 case 0x6:
2381 case 0x7:
2382 case 0x8:
2383 case 0x9:
2384 case 0xA:
2385 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2386 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2387 setModified(IsModified_MachineData);
2388 mHWData.backup();
2389 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2390 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2391 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2392 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2393 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2394 break;
2395
2396 case 0x80000000:
2397 case 0x80000001:
2398 case 0x80000002:
2399 case 0x80000003:
2400 case 0x80000004:
2401 case 0x80000005:
2402 case 0x80000006:
2403 case 0x80000007:
2404 case 0x80000008:
2405 case 0x80000009:
2406 case 0x8000000A:
2407 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2408 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2409 setModified(IsModified_MachineData);
2410 mHWData.backup();
2411 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2412 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2413 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2414 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2415 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2416 break;
2417
2418 default:
2419 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2420 }
2421 return S_OK;
2422}
2423
2424STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2425{
2426 AutoCaller autoCaller(this);
2427 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2428
2429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2430
2431 HRESULT rc = checkStateDependency(MutableStateDep);
2432 if (FAILED(rc)) return rc;
2433
2434 switch(aId)
2435 {
2436 case 0x0:
2437 case 0x1:
2438 case 0x2:
2439 case 0x3:
2440 case 0x4:
2441 case 0x5:
2442 case 0x6:
2443 case 0x7:
2444 case 0x8:
2445 case 0x9:
2446 case 0xA:
2447 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2448 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2449 setModified(IsModified_MachineData);
2450 mHWData.backup();
2451 /* Invalidate leaf. */
2452 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2453 break;
2454
2455 case 0x80000000:
2456 case 0x80000001:
2457 case 0x80000002:
2458 case 0x80000003:
2459 case 0x80000004:
2460 case 0x80000005:
2461 case 0x80000006:
2462 case 0x80000007:
2463 case 0x80000008:
2464 case 0x80000009:
2465 case 0x8000000A:
2466 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2467 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2468 setModified(IsModified_MachineData);
2469 mHWData.backup();
2470 /* Invalidate leaf. */
2471 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2472 break;
2473
2474 default:
2475 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2476 }
2477 return S_OK;
2478}
2479
2480STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2481{
2482 AutoCaller autoCaller(this);
2483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2484
2485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2486
2487 HRESULT rc = checkStateDependency(MutableStateDep);
2488 if (FAILED(rc)) return rc;
2489
2490 setModified(IsModified_MachineData);
2491 mHWData.backup();
2492
2493 /* Invalidate all standard leafs. */
2494 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2495 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2496
2497 /* Invalidate all extended leafs. */
2498 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2499 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2500
2501 return S_OK;
2502}
2503
2504STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2505{
2506 CheckComArgOutPointerValid(aVal);
2507
2508 AutoCaller autoCaller(this);
2509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2510
2511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2512
2513 switch(property)
2514 {
2515 case HWVirtExPropertyType_Enabled:
2516 *aVal = mHWData->mHWVirtExEnabled;
2517 break;
2518
2519 case HWVirtExPropertyType_VPID:
2520 *aVal = mHWData->mHWVirtExVPIDEnabled;
2521 break;
2522
2523 case HWVirtExPropertyType_NestedPaging:
2524 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2525 break;
2526
2527 case HWVirtExPropertyType_UnrestrictedExecution:
2528 *aVal = mHWData->mHWVirtExUXEnabled;
2529 break;
2530
2531 case HWVirtExPropertyType_LargePages:
2532 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2533#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2534 *aVal = FALSE;
2535#endif
2536 break;
2537
2538 case HWVirtExPropertyType_Force:
2539 *aVal = mHWData->mHWVirtExForceEnabled;
2540 break;
2541
2542 default:
2543 return E_INVALIDARG;
2544 }
2545 return S_OK;
2546}
2547
2548STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2549{
2550 AutoCaller autoCaller(this);
2551 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2552
2553 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2554
2555 HRESULT rc = checkStateDependency(MutableStateDep);
2556 if (FAILED(rc)) return rc;
2557
2558 switch(property)
2559 {
2560 case HWVirtExPropertyType_Enabled:
2561 setModified(IsModified_MachineData);
2562 mHWData.backup();
2563 mHWData->mHWVirtExEnabled = !!aVal;
2564 break;
2565
2566 case HWVirtExPropertyType_VPID:
2567 setModified(IsModified_MachineData);
2568 mHWData.backup();
2569 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2570 break;
2571
2572 case HWVirtExPropertyType_NestedPaging:
2573 setModified(IsModified_MachineData);
2574 mHWData.backup();
2575 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2576 break;
2577
2578 case HWVirtExPropertyType_UnrestrictedExecution:
2579 setModified(IsModified_MachineData);
2580 mHWData.backup();
2581 mHWData->mHWVirtExUXEnabled = !!aVal;
2582 break;
2583
2584 case HWVirtExPropertyType_LargePages:
2585 setModified(IsModified_MachineData);
2586 mHWData.backup();
2587 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2588 break;
2589
2590 case HWVirtExPropertyType_Force:
2591 setModified(IsModified_MachineData);
2592 mHWData.backup();
2593 mHWData->mHWVirtExForceEnabled = !!aVal;
2594 break;
2595
2596 default:
2597 return E_INVALIDARG;
2598 }
2599
2600 return S_OK;
2601}
2602
2603STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2604{
2605 CheckComArgOutPointerValid(aSnapshotFolder);
2606
2607 AutoCaller autoCaller(this);
2608 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2609
2610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2611
2612 Utf8Str strFullSnapshotFolder;
2613 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2614 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2615
2616 return S_OK;
2617}
2618
2619STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2620{
2621 /* @todo (r=dmik):
2622 * 1. Allow to change the name of the snapshot folder containing snapshots
2623 * 2. Rename the folder on disk instead of just changing the property
2624 * value (to be smart and not to leave garbage). Note that it cannot be
2625 * done here because the change may be rolled back. Thus, the right
2626 * place is #saveSettings().
2627 */
2628
2629 AutoCaller autoCaller(this);
2630 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2631
2632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 HRESULT rc = checkStateDependency(MutableStateDep);
2635 if (FAILED(rc)) return rc;
2636
2637 if (!mData->mCurrentSnapshot.isNull())
2638 return setError(E_FAIL,
2639 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2640
2641 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2642
2643 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2644 if (strSnapshotFolder.isEmpty())
2645 strSnapshotFolder = "Snapshots";
2646 int vrc = calculateFullPath(strSnapshotFolder,
2647 strSnapshotFolder);
2648 if (RT_FAILURE(vrc))
2649 return setError(E_FAIL,
2650 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2651 aSnapshotFolder, vrc);
2652
2653 setModified(IsModified_MachineData);
2654 mUserData.backup();
2655
2656 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2657
2658 return S_OK;
2659}
2660
2661STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2662{
2663 CheckComArgOutSafeArrayPointerValid(aAttachments);
2664
2665 AutoCaller autoCaller(this);
2666 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2667
2668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2669
2670 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2671 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2672
2673 return S_OK;
2674}
2675
2676STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2677{
2678 CheckComArgOutPointerValid(vrdeServer);
2679
2680 AutoCaller autoCaller(this);
2681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2682
2683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2684
2685 Assert(!!mVRDEServer);
2686 mVRDEServer.queryInterfaceTo(vrdeServer);
2687
2688 return S_OK;
2689}
2690
2691STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2692{
2693 CheckComArgOutPointerValid(audioAdapter);
2694
2695 AutoCaller autoCaller(this);
2696 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2697
2698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2699
2700 mAudioAdapter.queryInterfaceTo(audioAdapter);
2701 return S_OK;
2702}
2703
2704STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2705{
2706#ifdef VBOX_WITH_VUSB
2707 CheckComArgOutPointerValid(aUSBControllers);
2708
2709 AutoCaller autoCaller(this);
2710 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2711
2712 clearError();
2713 MultiResult rc(S_OK);
2714
2715# ifdef VBOX_WITH_USB
2716 rc = mParent->i_host()->i_checkUSBProxyService();
2717 if (FAILED(rc)) return rc;
2718# endif
2719
2720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2721
2722 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2723 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2724 return S_OK;
2725#else
2726 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2727 * extended error info to indicate that USB is simply not available
2728 * (w/o treating it as a failure), for example, as in OSE */
2729 NOREF(aUSBControllers);
2730 ReturnComNotImplemented();
2731#endif /* VBOX_WITH_VUSB */
2732}
2733
2734STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2735{
2736#ifdef VBOX_WITH_VUSB
2737 CheckComArgOutPointerValid(aUSBDeviceFilters);
2738
2739 AutoCaller autoCaller(this);
2740 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2741
2742 clearError();
2743 MultiResult rc(S_OK);
2744
2745# ifdef VBOX_WITH_USB
2746 rc = mParent->i_host()->i_checkUSBProxyService();
2747 if (FAILED(rc)) return rc;
2748# endif
2749
2750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2751
2752 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2753#else
2754 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2755 * extended error info to indicate that USB is simply not available
2756 * (w/o treating it as a failure), for example, as in OSE */
2757 NOREF(aUSBDeviceFilters);
2758 ReturnComNotImplemented();
2759#endif /* VBOX_WITH_VUSB */
2760}
2761
2762STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2763{
2764 CheckComArgOutPointerValid(aFilePath);
2765
2766 AutoLimitedCaller autoCaller(this);
2767 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2768
2769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2770
2771 mData->m_strConfigFileFull.cloneTo(aFilePath);
2772 return S_OK;
2773}
2774
2775STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2776{
2777 CheckComArgOutPointerValid(aModified);
2778
2779 AutoCaller autoCaller(this);
2780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2781
2782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2783
2784 HRESULT rc = checkStateDependency(MutableStateDep);
2785 if (FAILED(rc)) return rc;
2786
2787 if (!mData->pMachineConfigFile->fileExists())
2788 // this is a new machine, and no config file exists yet:
2789 *aModified = TRUE;
2790 else
2791 *aModified = (mData->flModifications != 0);
2792
2793 return S_OK;
2794}
2795
2796STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2797{
2798 CheckComArgOutPointerValid(aSessionState);
2799
2800 AutoCaller autoCaller(this);
2801 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2802
2803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2804
2805 *aSessionState = mData->mSession.mState;
2806
2807 return S_OK;
2808}
2809
2810STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2811{
2812 CheckComArgOutPointerValid(aSessionType);
2813
2814 AutoCaller autoCaller(this);
2815 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2816
2817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2818
2819 mData->mSession.mType.cloneTo(aSessionType);
2820
2821 return S_OK;
2822}
2823
2824STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2825{
2826 CheckComArgOutPointerValid(aSessionPID);
2827
2828 AutoCaller autoCaller(this);
2829 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2830
2831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2832
2833 *aSessionPID = mData->mSession.mPID;
2834
2835 return S_OK;
2836}
2837
2838STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2839{
2840 CheckComArgOutPointerValid(machineState);
2841
2842 AutoCaller autoCaller(this);
2843 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2844
2845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2846
2847 *machineState = mData->mMachineState;
2848
2849 return S_OK;
2850}
2851
2852STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2853{
2854 CheckComArgOutPointerValid(aLastStateChange);
2855
2856 AutoCaller autoCaller(this);
2857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2858
2859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2860
2861 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2862
2863 return S_OK;
2864}
2865
2866STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2867{
2868 CheckComArgOutPointerValid(aStateFilePath);
2869
2870 AutoCaller autoCaller(this);
2871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2872
2873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2874
2875 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2876
2877 return S_OK;
2878}
2879
2880STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2881{
2882 CheckComArgOutPointerValid(aLogFolder);
2883
2884 AutoCaller autoCaller(this);
2885 AssertComRCReturnRC(autoCaller.rc());
2886
2887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2888
2889 Utf8Str logFolder;
2890 getLogFolder(logFolder);
2891 logFolder.cloneTo(aLogFolder);
2892
2893 return S_OK;
2894}
2895
2896STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2897{
2898 CheckComArgOutPointerValid(aCurrentSnapshot);
2899
2900 AutoCaller autoCaller(this);
2901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2902
2903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2904
2905 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2906
2907 return S_OK;
2908}
2909
2910STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2911{
2912 CheckComArgOutPointerValid(aSnapshotCount);
2913
2914 AutoCaller autoCaller(this);
2915 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2916
2917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2918
2919 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2920 ? 0
2921 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2922
2923 return S_OK;
2924}
2925
2926STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2927{
2928 CheckComArgOutPointerValid(aCurrentStateModified);
2929
2930 AutoCaller autoCaller(this);
2931 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2932
2933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2934
2935 /* Note: for machines with no snapshots, we always return FALSE
2936 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2937 * reasons :) */
2938
2939 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2940 ? FALSE
2941 : mData->mCurrentStateModified;
2942
2943 return S_OK;
2944}
2945
2946STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2947{
2948 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2949
2950 AutoCaller autoCaller(this);
2951 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2952
2953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2956 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2957
2958 return S_OK;
2959}
2960
2961STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2962{
2963 CheckComArgOutPointerValid(aClipboardMode);
2964
2965 AutoCaller autoCaller(this);
2966 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2967
2968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2969
2970 *aClipboardMode = mHWData->mClipboardMode;
2971
2972 return S_OK;
2973}
2974
2975STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2976{
2977 HRESULT rc = S_OK;
2978
2979 AutoCaller autoCaller(this);
2980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2981
2982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2983
2984 alock.release();
2985 rc = onClipboardModeChange(aClipboardMode);
2986 alock.acquire();
2987 if (FAILED(rc)) return rc;
2988
2989 setModified(IsModified_MachineData);
2990 mHWData.backup();
2991 mHWData->mClipboardMode = aClipboardMode;
2992
2993 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2994 if (Global::IsOnline(mData->mMachineState))
2995 saveSettings(NULL);
2996
2997 return S_OK;
2998}
2999
3000STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
3001{
3002 CheckComArgOutPointerValid(aDragAndDropMode);
3003
3004 AutoCaller autoCaller(this);
3005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3006
3007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3008
3009 *aDragAndDropMode = mHWData->mDragAndDropMode;
3010
3011 return S_OK;
3012}
3013
3014STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3015{
3016 HRESULT rc = S_OK;
3017
3018 AutoCaller autoCaller(this);
3019 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3020
3021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3022
3023 alock.release();
3024 rc = onDragAndDropModeChange(aDragAndDropMode);
3025 alock.acquire();
3026 if (FAILED(rc)) return rc;
3027
3028 setModified(IsModified_MachineData);
3029 mHWData.backup();
3030 mHWData->mDragAndDropMode = aDragAndDropMode;
3031
3032 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3033 if (Global::IsOnline(mData->mMachineState))
3034 saveSettings(NULL);
3035
3036 return S_OK;
3037}
3038
3039STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3040{
3041 CheckComArgOutPointerValid(aPatterns);
3042
3043 AutoCaller autoCaller(this);
3044 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3045
3046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3047
3048 try
3049 {
3050 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3051 }
3052 catch (...)
3053 {
3054 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3055 }
3056
3057 return S_OK;
3058}
3059
3060STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3061{
3062 AutoCaller autoCaller(this);
3063 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3064
3065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3066
3067 HRESULT rc = checkStateDependency(MutableStateDep);
3068 if (FAILED(rc)) return rc;
3069
3070 setModified(IsModified_MachineData);
3071 mHWData.backup();
3072 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3073 return rc;
3074}
3075
3076STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3077{
3078 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3079
3080 AutoCaller autoCaller(this);
3081 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3082
3083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3084
3085 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3086 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3087
3088 return S_OK;
3089}
3090
3091STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3092{
3093 CheckComArgOutPointerValid(aEnabled);
3094
3095 AutoCaller autoCaller(this);
3096 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3097
3098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3099
3100 *aEnabled = mUserData->s.fTeleporterEnabled;
3101
3102 return S_OK;
3103}
3104
3105STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3106{
3107 AutoCaller autoCaller(this);
3108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3109
3110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3111
3112 /* Only allow it to be set to true when PoweredOff or Aborted.
3113 (Clearing it is always permitted.) */
3114 if ( aEnabled
3115 && mData->mRegistered
3116 && ( !isSessionMachine()
3117 || ( mData->mMachineState != MachineState_PoweredOff
3118 && mData->mMachineState != MachineState_Teleported
3119 && mData->mMachineState != MachineState_Aborted
3120 )
3121 )
3122 )
3123 return setError(VBOX_E_INVALID_VM_STATE,
3124 tr("The machine is not powered off (state is %s)"),
3125 Global::stringifyMachineState(mData->mMachineState));
3126
3127 setModified(IsModified_MachineData);
3128 mUserData.backup();
3129 mUserData->s.fTeleporterEnabled = !!aEnabled;
3130
3131 return S_OK;
3132}
3133
3134STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3135{
3136 CheckComArgOutPointerValid(aPort);
3137
3138 AutoCaller autoCaller(this);
3139 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3140
3141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3142
3143 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3144
3145 return S_OK;
3146}
3147
3148STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3149{
3150 if (aPort >= _64K)
3151 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3152
3153 AutoCaller autoCaller(this);
3154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3155
3156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3157
3158 HRESULT rc = checkStateDependency(MutableStateDep);
3159 if (FAILED(rc)) return rc;
3160
3161 setModified(IsModified_MachineData);
3162 mUserData.backup();
3163 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3164
3165 return S_OK;
3166}
3167
3168STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3169{
3170 CheckComArgOutPointerValid(aAddress);
3171
3172 AutoCaller autoCaller(this);
3173 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3174
3175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3176
3177 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3178
3179 return S_OK;
3180}
3181
3182STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3183{
3184 AutoCaller autoCaller(this);
3185 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3186
3187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3188
3189 HRESULT rc = checkStateDependency(MutableStateDep);
3190 if (FAILED(rc)) return rc;
3191
3192 setModified(IsModified_MachineData);
3193 mUserData.backup();
3194 mUserData->s.strTeleporterAddress = aAddress;
3195
3196 return S_OK;
3197}
3198
3199STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3200{
3201 CheckComArgOutPointerValid(aPassword);
3202
3203 AutoCaller autoCaller(this);
3204 HRESULT hrc = autoCaller.rc();
3205 if (SUCCEEDED(hrc))
3206 {
3207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3208 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3209 }
3210
3211 return hrc;
3212}
3213
3214STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3215{
3216 /*
3217 * Hash the password first.
3218 */
3219 Utf8Str strPassword(aPassword);
3220 if (!strPassword.isEmpty())
3221 {
3222 if (VBoxIsPasswordHashed(&strPassword))
3223 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3224 VBoxHashPassword(&strPassword);
3225 }
3226
3227 /*
3228 * Do the update.
3229 */
3230 AutoCaller autoCaller(this);
3231 HRESULT hrc = autoCaller.rc();
3232 if (SUCCEEDED(hrc))
3233 {
3234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3235 hrc = checkStateDependency(MutableStateDep);
3236 if (SUCCEEDED(hrc))
3237 {
3238 setModified(IsModified_MachineData);
3239 mUserData.backup();
3240 mUserData->s.strTeleporterPassword = strPassword;
3241 }
3242 }
3243
3244 return hrc;
3245}
3246
3247STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3248{
3249 CheckComArgOutPointerValid(aState);
3250
3251 AutoCaller autoCaller(this);
3252 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3253
3254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3255
3256 *aState = mUserData->s.enmFaultToleranceState;
3257 return S_OK;
3258}
3259
3260STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3261{
3262 AutoCaller autoCaller(this);
3263 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3264
3265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3266
3267 /* @todo deal with running state change. */
3268 HRESULT rc = checkStateDependency(MutableStateDep);
3269 if (FAILED(rc)) return rc;
3270
3271 setModified(IsModified_MachineData);
3272 mUserData.backup();
3273 mUserData->s.enmFaultToleranceState = aState;
3274 return S_OK;
3275}
3276
3277STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3278{
3279 CheckComArgOutPointerValid(aAddress);
3280
3281 AutoCaller autoCaller(this);
3282 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3283
3284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3285
3286 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3287 return S_OK;
3288}
3289
3290STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3291{
3292 AutoCaller autoCaller(this);
3293 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3294
3295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3296
3297 /* @todo deal with running state change. */
3298 HRESULT rc = checkStateDependency(MutableStateDep);
3299 if (FAILED(rc)) return rc;
3300
3301 setModified(IsModified_MachineData);
3302 mUserData.backup();
3303 mUserData->s.strFaultToleranceAddress = aAddress;
3304 return S_OK;
3305}
3306
3307STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3308{
3309 CheckComArgOutPointerValid(aPort);
3310
3311 AutoCaller autoCaller(this);
3312 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3313
3314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3315
3316 *aPort = mUserData->s.uFaultTolerancePort;
3317 return S_OK;
3318}
3319
3320STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3321{
3322 AutoCaller autoCaller(this);
3323 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3324
3325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3326
3327 /* @todo deal with running state change. */
3328 HRESULT rc = checkStateDependency(MutableStateDep);
3329 if (FAILED(rc)) return rc;
3330
3331 setModified(IsModified_MachineData);
3332 mUserData.backup();
3333 mUserData->s.uFaultTolerancePort = aPort;
3334 return S_OK;
3335}
3336
3337STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3338{
3339 CheckComArgOutPointerValid(aPassword);
3340
3341 AutoCaller autoCaller(this);
3342 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3343
3344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3345
3346 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3347
3348 return S_OK;
3349}
3350
3351STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3352{
3353 AutoCaller autoCaller(this);
3354 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3355
3356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3357
3358 /* @todo deal with running state change. */
3359 HRESULT rc = checkStateDependency(MutableStateDep);
3360 if (FAILED(rc)) return rc;
3361
3362 setModified(IsModified_MachineData);
3363 mUserData.backup();
3364 mUserData->s.strFaultTolerancePassword = aPassword;
3365
3366 return S_OK;
3367}
3368
3369STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3370{
3371 CheckComArgOutPointerValid(aInterval);
3372
3373 AutoCaller autoCaller(this);
3374 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3375
3376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3377
3378 *aInterval = mUserData->s.uFaultToleranceInterval;
3379 return S_OK;
3380}
3381
3382STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3383{
3384 AutoCaller autoCaller(this);
3385 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3386
3387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3388
3389 /* @todo deal with running state change. */
3390 HRESULT rc = checkStateDependency(MutableStateDep);
3391 if (FAILED(rc)) return rc;
3392
3393 setModified(IsModified_MachineData);
3394 mUserData.backup();
3395 mUserData->s.uFaultToleranceInterval = aInterval;
3396 return S_OK;
3397}
3398
3399STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3400{
3401 CheckComArgOutPointerValid(aEnabled);
3402
3403 AutoCaller autoCaller(this);
3404 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3405
3406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3407
3408 *aEnabled = mUserData->s.fRTCUseUTC;
3409
3410 return S_OK;
3411}
3412
3413STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3414{
3415 AutoCaller autoCaller(this);
3416 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3417
3418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3419
3420 /* Only allow it to be set to true when PoweredOff or Aborted.
3421 (Clearing it is always permitted.) */
3422 if ( aEnabled
3423 && mData->mRegistered
3424 && ( !isSessionMachine()
3425 || ( mData->mMachineState != MachineState_PoweredOff
3426 && mData->mMachineState != MachineState_Teleported
3427 && mData->mMachineState != MachineState_Aborted
3428 )
3429 )
3430 )
3431 return setError(VBOX_E_INVALID_VM_STATE,
3432 tr("The machine is not powered off (state is %s)"),
3433 Global::stringifyMachineState(mData->mMachineState));
3434
3435 setModified(IsModified_MachineData);
3436 mUserData.backup();
3437 mUserData->s.fRTCUseUTC = !!aEnabled;
3438
3439 return S_OK;
3440}
3441
3442STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3443{
3444 CheckComArgOutPointerValid(aEnabled);
3445
3446 AutoCaller autoCaller(this);
3447 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3448
3449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3450
3451 *aEnabled = mHWData->mIOCacheEnabled;
3452
3453 return S_OK;
3454}
3455
3456STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3457{
3458 AutoCaller autoCaller(this);
3459 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3460
3461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3462
3463 HRESULT rc = checkStateDependency(MutableStateDep);
3464 if (FAILED(rc)) return rc;
3465
3466 setModified(IsModified_MachineData);
3467 mHWData.backup();
3468 mHWData->mIOCacheEnabled = aEnabled;
3469
3470 return S_OK;
3471}
3472
3473STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3474{
3475 CheckComArgOutPointerValid(aIOCacheSize);
3476
3477 AutoCaller autoCaller(this);
3478 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3479
3480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3481
3482 *aIOCacheSize = mHWData->mIOCacheSize;
3483
3484 return S_OK;
3485}
3486
3487STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3488{
3489 AutoCaller autoCaller(this);
3490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3491
3492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3493
3494 HRESULT rc = checkStateDependency(MutableStateDep);
3495 if (FAILED(rc)) return rc;
3496
3497 setModified(IsModified_MachineData);
3498 mHWData.backup();
3499 mHWData->mIOCacheSize = aIOCacheSize;
3500
3501 return S_OK;
3502}
3503
3504
3505/**
3506 * @note Locks objects!
3507 */
3508STDMETHODIMP Machine::LockMachine(ISession *aSession,
3509 LockType_T lockType)
3510{
3511 CheckComArgNotNull(aSession);
3512
3513 AutoCaller autoCaller(this);
3514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3515
3516 /* check the session state */
3517 SessionState_T state;
3518 HRESULT rc = aSession->COMGETTER(State)(&state);
3519 if (FAILED(rc)) return rc;
3520
3521 if (state != SessionState_Unlocked)
3522 return setError(VBOX_E_INVALID_OBJECT_STATE,
3523 tr("The given session is busy"));
3524
3525 // get the client's IInternalSessionControl interface
3526 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3527 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3528 E_INVALIDARG);
3529
3530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3531
3532 if (!mData->mRegistered)
3533 return setError(E_UNEXPECTED,
3534 tr("The machine '%s' is not registered"),
3535 mUserData->s.strName.c_str());
3536
3537 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3538
3539 SessionState_T oldState = mData->mSession.mState;
3540 /* Hack: in case the session is closing and there is a progress object
3541 * which allows waiting for the session to be closed, take the opportunity
3542 * and do a limited wait (max. 1 second). This helps a lot when the system
3543 * is busy and thus session closing can take a little while. */
3544 if ( mData->mSession.mState == SessionState_Unlocking
3545 && mData->mSession.mProgress)
3546 {
3547 alock.release();
3548 mData->mSession.mProgress->WaitForCompletion(1000);
3549 alock.acquire();
3550 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3551 }
3552
3553 // try again now
3554 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3555 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3556 )
3557 {
3558 // OK, share the session... we are now dealing with three processes:
3559 // 1) VBoxSVC (where this code runs);
3560 // 2) process C: the caller's client process (who wants a shared session);
3561 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3562
3563 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3564 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3565 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3566 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3567 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3568
3569 /*
3570 * Release the lock before calling the client process. It's safe here
3571 * since the only thing to do after we get the lock again is to add
3572 * the remote control to the list (which doesn't directly influence
3573 * anything).
3574 */
3575 alock.release();
3576
3577 // get the console of the session holding the write lock (this is a remote call)
3578 ComPtr<IConsole> pConsoleW;
3579 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3580 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3581 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3582 if (FAILED(rc))
3583 // the failure may occur w/o any error info (from RPC), so provide one
3584 return setError(VBOX_E_VM_ERROR,
3585 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3586
3587 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3588
3589 // share the session machine and W's console with the caller's session
3590 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3591 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3592 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3593
3594 if (FAILED(rc))
3595 // the failure may occur w/o any error info (from RPC), so provide one
3596 return setError(VBOX_E_VM_ERROR,
3597 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3598 alock.acquire();
3599
3600 // need to revalidate the state after acquiring the lock again
3601 if (mData->mSession.mState != SessionState_Locked)
3602 {
3603 pSessionControl->Uninitialize();
3604 return setError(VBOX_E_INVALID_SESSION_STATE,
3605 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3606 mUserData->s.strName.c_str());
3607 }
3608
3609 // add the caller's session to the list
3610 mData->mSession.mRemoteControls.push_back(pSessionControl);
3611 }
3612 else if ( mData->mSession.mState == SessionState_Locked
3613 || mData->mSession.mState == SessionState_Unlocking
3614 )
3615 {
3616 // sharing not permitted, or machine still unlocking:
3617 return setError(VBOX_E_INVALID_OBJECT_STATE,
3618 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3619 mUserData->s.strName.c_str());
3620 }
3621 else
3622 {
3623 // machine is not locked: then write-lock the machine (create the session machine)
3624
3625 // must not be busy
3626 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3627
3628 // get the caller's session PID
3629 RTPROCESS pid = NIL_RTPROCESS;
3630 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3631 pSessionControl->GetPID((ULONG*)&pid);
3632 Assert(pid != NIL_RTPROCESS);
3633
3634 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3635
3636 if (fLaunchingVMProcess)
3637 {
3638 if (mData->mSession.mPID == NIL_RTPROCESS)
3639 {
3640 // two or more clients racing for a lock, the one which set the
3641 // session state to Spawning will win, the others will get an
3642 // error as we can't decide here if waiting a little would help
3643 // (only for shared locks this would avoid an error)
3644 return setError(VBOX_E_INVALID_OBJECT_STATE,
3645 tr("The machine '%s' already has a lock request pending"),
3646 mUserData->s.strName.c_str());
3647 }
3648
3649 // this machine is awaiting for a spawning session to be opened:
3650 // then the calling process must be the one that got started by
3651 // LaunchVMProcess()
3652
3653 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3654 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3655
3656 if (mData->mSession.mPID != pid)
3657 return setError(E_ACCESSDENIED,
3658 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3659 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3660 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3661 }
3662
3663 // create the mutable SessionMachine from the current machine
3664 ComObjPtr<SessionMachine> sessionMachine;
3665 sessionMachine.createObject();
3666 rc = sessionMachine->init(this);
3667 AssertComRC(rc);
3668
3669 /* NOTE: doing return from this function after this point but
3670 * before the end is forbidden since it may call SessionMachine::uninit()
3671 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3672 * lock while still holding the Machine lock in alock so that a deadlock
3673 * is possible due to the wrong lock order. */
3674
3675 if (SUCCEEDED(rc))
3676 {
3677 /*
3678 * Set the session state to Spawning to protect against subsequent
3679 * attempts to open a session and to unregister the machine after
3680 * we release the lock.
3681 */
3682 SessionState_T origState = mData->mSession.mState;
3683 mData->mSession.mState = SessionState_Spawning;
3684
3685#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3686 /* Get the client token ID to be passed to the client process */
3687 Utf8Str strTokenId;
3688 sessionMachine->getTokenId(strTokenId);
3689 Assert(!strTokenId.isEmpty());
3690#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3691 /* Get the client token to be passed to the client process */
3692 ComPtr<IToken> pToken(sessionMachine->getToken());
3693 /* The token is now "owned" by pToken, fix refcount */
3694 if (!pToken.isNull())
3695 pToken->Release();
3696#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3697
3698 /*
3699 * Release the lock before calling the client process -- it will call
3700 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3701 * because the state is Spawning, so that LaunchVMProcess() and
3702 * LockMachine() calls will fail. This method, called before we
3703 * acquire the lock again, will fail because of the wrong PID.
3704 *
3705 * Note that mData->mSession.mRemoteControls accessed outside
3706 * the lock may not be modified when state is Spawning, so it's safe.
3707 */
3708 alock.release();
3709
3710 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3711#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3712 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3713#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3714 rc = pSessionControl->AssignMachine(sessionMachine, lockType, pToken);
3715 /* Now the token is owned by the client process. */
3716 pToken.setNull();
3717#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3718 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3719
3720 /* The failure may occur w/o any error info (from RPC), so provide one */
3721 if (FAILED(rc))
3722 setError(VBOX_E_VM_ERROR,
3723 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3724
3725 if ( SUCCEEDED(rc)
3726 && fLaunchingVMProcess
3727 )
3728 {
3729 /* complete the remote session initialization */
3730
3731 /* get the console from the direct session */
3732 ComPtr<IConsole> console;
3733 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3734 ComAssertComRC(rc);
3735
3736 if (SUCCEEDED(rc) && !console)
3737 {
3738 ComAssert(!!console);
3739 rc = E_FAIL;
3740 }
3741
3742 /* assign machine & console to the remote session */
3743 if (SUCCEEDED(rc))
3744 {
3745 /*
3746 * after LaunchVMProcess(), the first and the only
3747 * entry in remoteControls is that remote session
3748 */
3749 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3750 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3751 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3752
3753 /* The failure may occur w/o any error info (from RPC), so provide one */
3754 if (FAILED(rc))
3755 setError(VBOX_E_VM_ERROR,
3756 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3757 }
3758
3759 if (FAILED(rc))
3760 pSessionControl->Uninitialize();
3761 }
3762
3763 /* acquire the lock again */
3764 alock.acquire();
3765
3766 /* Restore the session state */
3767 mData->mSession.mState = origState;
3768 }
3769
3770 // finalize spawning anyway (this is why we don't return on errors above)
3771 if (fLaunchingVMProcess)
3772 {
3773 /* Note that the progress object is finalized later */
3774 /** @todo Consider checking mData->mSession.mProgress for cancellation
3775 * around here. */
3776
3777 /* We don't reset mSession.mPID here because it is necessary for
3778 * SessionMachine::uninit() to reap the child process later. */
3779
3780 if (FAILED(rc))
3781 {
3782 /* Close the remote session, remove the remote control from the list
3783 * and reset session state to Closed (@note keep the code in sync
3784 * with the relevant part in checkForSpawnFailure()). */
3785
3786 Assert(mData->mSession.mRemoteControls.size() == 1);
3787 if (mData->mSession.mRemoteControls.size() == 1)
3788 {
3789 ErrorInfoKeeper eik;
3790 mData->mSession.mRemoteControls.front()->Uninitialize();
3791 }
3792
3793 mData->mSession.mRemoteControls.clear();
3794 mData->mSession.mState = SessionState_Unlocked;
3795 }
3796 }
3797 else
3798 {
3799 /* memorize PID of the directly opened session */
3800 if (SUCCEEDED(rc))
3801 mData->mSession.mPID = pid;
3802 }
3803
3804 if (SUCCEEDED(rc))
3805 {
3806 /* memorize the direct session control and cache IUnknown for it */
3807 mData->mSession.mDirectControl = pSessionControl;
3808 mData->mSession.mState = SessionState_Locked;
3809 /* associate the SessionMachine with this Machine */
3810 mData->mSession.mMachine = sessionMachine;
3811
3812 /* request an IUnknown pointer early from the remote party for later
3813 * identity checks (it will be internally cached within mDirectControl
3814 * at least on XPCOM) */
3815 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3816 NOREF(unk);
3817 }
3818
3819 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3820 * would break the lock order */
3821 alock.release();
3822
3823 /* uninitialize the created session machine on failure */
3824 if (FAILED(rc))
3825 sessionMachine->uninit();
3826
3827 }
3828
3829 if (SUCCEEDED(rc))
3830 {
3831 /*
3832 * tell the client watcher thread to update the set of
3833 * machines that have open sessions
3834 */
3835 mParent->i_updateClientWatcher();
3836
3837 if (oldState != SessionState_Locked)
3838 /* fire an event */
3839 mParent->i_onSessionStateChange(getId(), SessionState_Locked);
3840 }
3841
3842 return rc;
3843}
3844
3845/**
3846 * @note Locks objects!
3847 */
3848STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3849 IN_BSTR aFrontend,
3850 IN_BSTR aEnvironment,
3851 IProgress **aProgress)
3852{
3853 CheckComArgStr(aFrontend);
3854 Utf8Str strFrontend(aFrontend);
3855 Utf8Str strEnvironment(aEnvironment);
3856 /* "emergencystop" doesn't need the session, so skip the checks/interface
3857 * retrieval. This code doesn't quite fit in here, but introducing a
3858 * special API method would be even more effort, and would require explicit
3859 * support by every API client. It's better to hide the feature a bit. */
3860 if (strFrontend != "emergencystop")
3861 CheckComArgNotNull(aSession);
3862 CheckComArgOutPointerValid(aProgress);
3863
3864 AutoCaller autoCaller(this);
3865 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3866
3867 HRESULT rc = S_OK;
3868 if (strFrontend.isEmpty())
3869 {
3870 Bstr bstrFrontend;
3871 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3872 if (FAILED(rc))
3873 return rc;
3874 strFrontend = bstrFrontend;
3875 if (strFrontend.isEmpty())
3876 {
3877 ComPtr<ISystemProperties> systemProperties;
3878 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3879 if (FAILED(rc))
3880 return rc;
3881 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3882 if (FAILED(rc))
3883 return rc;
3884 strFrontend = bstrFrontend;
3885 }
3886 /* paranoia - emergencystop is not a valid default */
3887 if (strFrontend == "emergencystop")
3888 strFrontend = Utf8Str::Empty;
3889 }
3890 /* default frontend: Qt GUI */
3891 if (strFrontend.isEmpty())
3892 strFrontend = "GUI/Qt";
3893
3894 if (strFrontend != "emergencystop")
3895 {
3896 /* check the session state */
3897 SessionState_T state;
3898 rc = aSession->COMGETTER(State)(&state);
3899 if (FAILED(rc))
3900 return rc;
3901
3902 if (state != SessionState_Unlocked)
3903 return setError(VBOX_E_INVALID_OBJECT_STATE,
3904 tr("The given session is busy"));
3905
3906 /* get the IInternalSessionControl interface */
3907 ComPtr<IInternalSessionControl> control(aSession);
3908 ComAssertMsgRet(!control.isNull(),
3909 ("No IInternalSessionControl interface"),
3910 E_INVALIDARG);
3911
3912 /* get the teleporter enable state for the progress object init. */
3913 BOOL fTeleporterEnabled;
3914 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3915 if (FAILED(rc))
3916 return rc;
3917
3918 /* create a progress object */
3919 ComObjPtr<ProgressProxy> progress;
3920 progress.createObject();
3921 rc = progress->init(mParent,
3922 static_cast<IMachine*>(this),
3923 Bstr(tr("Starting VM")).raw(),
3924 TRUE /* aCancelable */,
3925 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3926 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3927 2 /* uFirstOperationWeight */,
3928 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3929
3930 if (SUCCEEDED(rc))
3931 {
3932 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3933 if (SUCCEEDED(rc))
3934 {
3935 progress.queryInterfaceTo(aProgress);
3936
3937 /* signal the client watcher thread */
3938 mParent->i_updateClientWatcher();
3939
3940 /* fire an event */
3941 mParent->i_onSessionStateChange(getId(), SessionState_Spawning);
3942 }
3943 }
3944 }
3945 else
3946 {
3947 /* no progress object - either instant success or failure */
3948 *aProgress = NULL;
3949
3950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3951
3952 if (mData->mSession.mState != SessionState_Locked)
3953 return setError(VBOX_E_INVALID_OBJECT_STATE,
3954 tr("The machine '%s' is not locked by a session"),
3955 mUserData->s.strName.c_str());
3956
3957 /* must have a VM process associated - do not kill normal API clients
3958 * with an open session */
3959 if (!Global::IsOnline(mData->mMachineState))
3960 return setError(VBOX_E_INVALID_OBJECT_STATE,
3961 tr("The machine '%s' does not have a VM process"),
3962 mUserData->s.strName.c_str());
3963
3964 /* forcibly terminate the VM process */
3965 if (mData->mSession.mPID != NIL_RTPROCESS)
3966 RTProcTerminate(mData->mSession.mPID);
3967
3968 /* signal the client watcher thread, as most likely the client has
3969 * been terminated */
3970 mParent->i_updateClientWatcher();
3971 }
3972
3973 return rc;
3974}
3975
3976STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3977{
3978 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3979 return setError(E_INVALIDARG,
3980 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3981 aPosition, SchemaDefs::MaxBootPosition);
3982
3983 if (aDevice == DeviceType_USB)
3984 return setError(E_NOTIMPL,
3985 tr("Booting from USB device is currently not supported"));
3986
3987 AutoCaller autoCaller(this);
3988 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3989
3990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3991
3992 HRESULT rc = checkStateDependency(MutableStateDep);
3993 if (FAILED(rc)) return rc;
3994
3995 setModified(IsModified_MachineData);
3996 mHWData.backup();
3997 mHWData->mBootOrder[aPosition - 1] = aDevice;
3998
3999 return S_OK;
4000}
4001
4002STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
4003{
4004 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
4005 return setError(E_INVALIDARG,
4006 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
4007 aPosition, SchemaDefs::MaxBootPosition);
4008
4009 AutoCaller autoCaller(this);
4010 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4011
4012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4013
4014 *aDevice = mHWData->mBootOrder[aPosition - 1];
4015
4016 return S_OK;
4017}
4018
4019STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4020 LONG aControllerPort,
4021 LONG aDevice,
4022 DeviceType_T aType,
4023 IMedium *aMedium)
4024{
4025 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4026 aControllerName, aControllerPort, aDevice, aType, aMedium));
4027
4028 CheckComArgStrNotEmptyOrNull(aControllerName);
4029
4030 AutoCaller autoCaller(this);
4031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4032
4033 // request the host lock first, since might be calling Host methods for getting host drives;
4034 // next, protect the media tree all the while we're in here, as well as our member variables
4035 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
4036 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4037
4038 HRESULT rc = checkStateDependency(MutableStateDep);
4039 if (FAILED(rc)) return rc;
4040
4041 /// @todo NEWMEDIA implicit machine registration
4042 if (!mData->mRegistered)
4043 return setError(VBOX_E_INVALID_OBJECT_STATE,
4044 tr("Cannot attach storage devices to an unregistered machine"));
4045
4046 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4047
4048 /* Check for an existing controller. */
4049 ComObjPtr<StorageController> ctl;
4050 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4051 if (FAILED(rc)) return rc;
4052
4053 StorageControllerType_T ctrlType;
4054 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4055 if (FAILED(rc))
4056 return setError(E_FAIL,
4057 tr("Could not get type of controller '%ls'"),
4058 aControllerName);
4059
4060 bool fSilent = false;
4061 Utf8Str strReconfig;
4062
4063 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4064 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4065 if ( mData->mMachineState == MachineState_Paused
4066 && strReconfig == "1")
4067 fSilent = true;
4068
4069 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4070 bool fHotplug = false;
4071 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4072 fHotplug = true;
4073
4074 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4075 return setError(VBOX_E_INVALID_VM_STATE,
4076 tr("Controller '%ls' does not support hotplugging"),
4077 aControllerName);
4078
4079 // check that the port and device are not out of range
4080 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
4081 if (FAILED(rc)) return rc;
4082
4083 /* check if the device slot is already busy */
4084 MediumAttachment *pAttachTemp;
4085 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4086 aControllerName,
4087 aControllerPort,
4088 aDevice)))
4089 {
4090 Medium *pMedium = pAttachTemp->i_getMedium();
4091 if (pMedium)
4092 {
4093 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4094 return setError(VBOX_E_OBJECT_IN_USE,
4095 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4096 pMedium->i_getLocationFull().c_str(),
4097 aControllerPort,
4098 aDevice,
4099 aControllerName);
4100 }
4101 else
4102 return setError(VBOX_E_OBJECT_IN_USE,
4103 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4104 aControllerPort, aDevice, aControllerName);
4105 }
4106
4107 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4108 if (aMedium && medium.isNull())
4109 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4110
4111 AutoCaller mediumCaller(medium);
4112 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4113
4114 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4115
4116 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4117 && !medium.isNull()
4118 )
4119 return setError(VBOX_E_OBJECT_IN_USE,
4120 tr("Medium '%s' is already attached to this virtual machine"),
4121 medium->i_getLocationFull().c_str());
4122
4123 if (!medium.isNull())
4124 {
4125 MediumType_T mtype = medium->i_getType();
4126 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4127 // For DVDs it's not written to the config file, so needs no global config
4128 // version bump. For floppies it's a new attribute "type", which is ignored
4129 // by older VirtualBox version, so needs no global config version bump either.
4130 // For hard disks this type is not accepted.
4131 if (mtype == MediumType_MultiAttach)
4132 {
4133 // This type is new with VirtualBox 4.0 and therefore requires settings
4134 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4135 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4136 // two reasons: The medium type is a property of the media registry tree, which
4137 // can reside in the global config file (for pre-4.0 media); we would therefore
4138 // possibly need to bump the global config version. We don't want to do that though
4139 // because that might make downgrading to pre-4.0 impossible.
4140 // As a result, we can only use these two new types if the medium is NOT in the
4141 // global registry:
4142 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
4143 if ( medium->i_isInRegistry(uuidGlobalRegistry)
4144 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4145 )
4146 return setError(VBOX_E_INVALID_OBJECT_STATE,
4147 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4148 "to machines that were created with VirtualBox 4.0 or later"),
4149 medium->i_getLocationFull().c_str());
4150 }
4151 }
4152
4153 bool fIndirect = false;
4154 if (!medium.isNull())
4155 fIndirect = medium->i_isReadOnly();
4156 bool associate = true;
4157
4158 do
4159 {
4160 if ( aType == DeviceType_HardDisk
4161 && mMediaData.isBackedUp())
4162 {
4163 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4164
4165 /* check if the medium was attached to the VM before we started
4166 * changing attachments in which case the attachment just needs to
4167 * be restored */
4168 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4169 {
4170 AssertReturn(!fIndirect, E_FAIL);
4171
4172 /* see if it's the same bus/channel/device */
4173 if (pAttachTemp->i_matches(aControllerName, aControllerPort, aDevice))
4174 {
4175 /* the simplest case: restore the whole attachment
4176 * and return, nothing else to do */
4177 mMediaData->mAttachments.push_back(pAttachTemp);
4178
4179 /* Reattach the medium to the VM. */
4180 if (fHotplug || fSilent)
4181 {
4182 mediumLock.release();
4183 treeLock.release();
4184 alock.release();
4185
4186 MediumLockList *pMediumLockList(new MediumLockList());
4187
4188 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4189 true /* fMediumLockWrite */,
4190 NULL,
4191 *pMediumLockList);
4192 alock.acquire();
4193 if (FAILED(rc))
4194 delete pMediumLockList;
4195 else
4196 {
4197 mData->mSession.mLockedMedia.Unlock();
4198 alock.release();
4199 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4200 mData->mSession.mLockedMedia.Lock();
4201 alock.acquire();
4202 }
4203 alock.release();
4204
4205 if (SUCCEEDED(rc))
4206 {
4207 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4208 /* Remove lock list in case of error. */
4209 if (FAILED(rc))
4210 {
4211 mData->mSession.mLockedMedia.Unlock();
4212 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4213 mData->mSession.mLockedMedia.Lock();
4214 }
4215 }
4216 }
4217
4218 return S_OK;
4219 }
4220
4221 /* bus/channel/device differ; we need a new attachment object,
4222 * but don't try to associate it again */
4223 associate = false;
4224 break;
4225 }
4226 }
4227
4228 /* go further only if the attachment is to be indirect */
4229 if (!fIndirect)
4230 break;
4231
4232 /* perform the so called smart attachment logic for indirect
4233 * attachments. Note that smart attachment is only applicable to base
4234 * hard disks. */
4235
4236 if (medium->i_getParent().isNull())
4237 {
4238 /* first, investigate the backup copy of the current hard disk
4239 * attachments to make it possible to re-attach existing diffs to
4240 * another device slot w/o losing their contents */
4241 if (mMediaData.isBackedUp())
4242 {
4243 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4244
4245 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4246 uint32_t foundLevel = 0;
4247
4248 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4249 it != oldAtts.end();
4250 ++it)
4251 {
4252 uint32_t level = 0;
4253 MediumAttachment *pAttach = *it;
4254 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4255 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4256 if (pMedium.isNull())
4257 continue;
4258
4259 if (pMedium->i_getBase(&level) == medium)
4260 {
4261 /* skip the hard disk if its currently attached (we
4262 * cannot attach the same hard disk twice) */
4263 if (findAttachment(mMediaData->mAttachments,
4264 pMedium))
4265 continue;
4266
4267 /* matched device, channel and bus (i.e. attached to the
4268 * same place) will win and immediately stop the search;
4269 * otherwise the attachment that has the youngest
4270 * descendant of medium will be used
4271 */
4272 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
4273 {
4274 /* the simplest case: restore the whole attachment
4275 * and return, nothing else to do */
4276 mMediaData->mAttachments.push_back(*it);
4277
4278 /* Reattach the medium to the VM. */
4279 if (fHotplug || fSilent)
4280 {
4281 mediumLock.release();
4282 treeLock.release();
4283 alock.release();
4284
4285 MediumLockList *pMediumLockList(new MediumLockList());
4286
4287 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4288 true /* fMediumLockWrite */,
4289 NULL,
4290 *pMediumLockList);
4291 alock.acquire();
4292 if (FAILED(rc))
4293 delete pMediumLockList;
4294 else
4295 {
4296 mData->mSession.mLockedMedia.Unlock();
4297 alock.release();
4298 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4299 mData->mSession.mLockedMedia.Lock();
4300 alock.acquire();
4301 }
4302 alock.release();
4303
4304 if (SUCCEEDED(rc))
4305 {
4306 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4307 /* Remove lock list in case of error. */
4308 if (FAILED(rc))
4309 {
4310 mData->mSession.mLockedMedia.Unlock();
4311 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4312 mData->mSession.mLockedMedia.Lock();
4313 }
4314 }
4315 }
4316
4317 return S_OK;
4318 }
4319 else if ( foundIt == oldAtts.end()
4320 || level > foundLevel /* prefer younger */
4321 )
4322 {
4323 foundIt = it;
4324 foundLevel = level;
4325 }
4326 }
4327 }
4328
4329 if (foundIt != oldAtts.end())
4330 {
4331 /* use the previously attached hard disk */
4332 medium = (*foundIt)->i_getMedium();
4333 mediumCaller.attach(medium);
4334 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4335 mediumLock.attach(medium);
4336 /* not implicit, doesn't require association with this VM */
4337 fIndirect = false;
4338 associate = false;
4339 /* go right to the MediumAttachment creation */
4340 break;
4341 }
4342 }
4343
4344 /* must give up the medium lock and medium tree lock as below we
4345 * go over snapshots, which needs a lock with higher lock order. */
4346 mediumLock.release();
4347 treeLock.release();
4348
4349 /* then, search through snapshots for the best diff in the given
4350 * hard disk's chain to base the new diff on */
4351
4352 ComObjPtr<Medium> base;
4353 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4354 while (snap)
4355 {
4356 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4357
4358 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4359
4360 MediumAttachment *pAttachFound = NULL;
4361 uint32_t foundLevel = 0;
4362
4363 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4364 it != snapAtts.end();
4365 ++it)
4366 {
4367 MediumAttachment *pAttach = *it;
4368 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4369 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4370 if (pMedium.isNull())
4371 continue;
4372
4373 uint32_t level = 0;
4374 if (pMedium->i_getBase(&level) == medium)
4375 {
4376 /* matched device, channel and bus (i.e. attached to the
4377 * same place) will win and immediately stop the search;
4378 * otherwise the attachment that has the youngest
4379 * descendant of medium will be used
4380 */
4381 if ( pAttach->i_getDevice() == aDevice
4382 && pAttach->i_getPort() == aControllerPort
4383 && pAttach->i_getControllerName() == aControllerName
4384 )
4385 {
4386 pAttachFound = pAttach;
4387 break;
4388 }
4389 else if ( !pAttachFound
4390 || level > foundLevel /* prefer younger */
4391 )
4392 {
4393 pAttachFound = pAttach;
4394 foundLevel = level;
4395 }
4396 }
4397 }
4398
4399 if (pAttachFound)
4400 {
4401 base = pAttachFound->i_getMedium();
4402 break;
4403 }
4404
4405 snap = snap->i_getParent();
4406 }
4407
4408 /* re-lock medium tree and the medium, as we need it below */
4409 treeLock.acquire();
4410 mediumLock.acquire();
4411
4412 /* found a suitable diff, use it as a base */
4413 if (!base.isNull())
4414 {
4415 medium = base;
4416 mediumCaller.attach(medium);
4417 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4418 mediumLock.attach(medium);
4419 }
4420 }
4421
4422 Utf8Str strFullSnapshotFolder;
4423 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4424
4425 ComObjPtr<Medium> diff;
4426 diff.createObject();
4427 // store this diff in the same registry as the parent
4428 Guid uuidRegistryParent;
4429 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4430 {
4431 // parent image has no registry: this can happen if we're attaching a new immutable
4432 // image that has not yet been attached (medium then points to the base and we're
4433 // creating the diff image for the immutable, and the parent is not yet registered);
4434 // put the parent in the machine registry then
4435 mediumLock.release();
4436 treeLock.release();
4437 alock.release();
4438 addMediumToRegistry(medium);
4439 alock.acquire();
4440 treeLock.acquire();
4441 mediumLock.acquire();
4442 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4443 }
4444 rc = diff->init(mParent,
4445 medium->i_getPreferredDiffFormat(),
4446 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4447 uuidRegistryParent);
4448 if (FAILED(rc)) return rc;
4449
4450 /* Apply the normal locking logic to the entire chain. */
4451 MediumLockList *pMediumLockList(new MediumLockList());
4452 mediumLock.release();
4453 treeLock.release();
4454 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4455 true /* fMediumLockWrite */,
4456 medium,
4457 *pMediumLockList);
4458 treeLock.acquire();
4459 mediumLock.acquire();
4460 if (SUCCEEDED(rc))
4461 {
4462 mediumLock.release();
4463 treeLock.release();
4464 rc = pMediumLockList->Lock();
4465 treeLock.acquire();
4466 mediumLock.acquire();
4467 if (FAILED(rc))
4468 setError(rc,
4469 tr("Could not lock medium when creating diff '%s'"),
4470 diff->i_getLocationFull().c_str());
4471 else
4472 {
4473 /* will release the lock before the potentially lengthy
4474 * operation, so protect with the special state */
4475 MachineState_T oldState = mData->mMachineState;
4476 setMachineState(MachineState_SettingUp);
4477
4478 mediumLock.release();
4479 treeLock.release();
4480 alock.release();
4481
4482 rc = medium->i_createDiffStorage(diff,
4483 MediumVariant_Standard,
4484 pMediumLockList,
4485 NULL /* aProgress */,
4486 true /* aWait */);
4487
4488 alock.acquire();
4489 treeLock.acquire();
4490 mediumLock.acquire();
4491
4492 setMachineState(oldState);
4493 }
4494 }
4495
4496 /* Unlock the media and free the associated memory. */
4497 delete pMediumLockList;
4498
4499 if (FAILED(rc)) return rc;
4500
4501 /* use the created diff for the actual attachment */
4502 medium = diff;
4503 mediumCaller.attach(medium);
4504 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4505 mediumLock.attach(medium);
4506 }
4507 while (0);
4508
4509 ComObjPtr<MediumAttachment> attachment;
4510 attachment.createObject();
4511 rc = attachment->init(this,
4512 medium,
4513 aControllerName,
4514 aControllerPort,
4515 aDevice,
4516 aType,
4517 fIndirect,
4518 false /* fPassthrough */,
4519 false /* fTempEject */,
4520 false /* fNonRotational */,
4521 false /* fDiscard */,
4522 fHotplug /* fHotPluggable */,
4523 Utf8Str::Empty);
4524 if (FAILED(rc)) return rc;
4525
4526 if (associate && !medium.isNull())
4527 {
4528 // as the last step, associate the medium to the VM
4529 rc = medium->i_addBackReference(mData->mUuid);
4530 // here we can fail because of Deleting, or being in process of creating a Diff
4531 if (FAILED(rc)) return rc;
4532
4533 mediumLock.release();
4534 treeLock.release();
4535 alock.release();
4536 addMediumToRegistry(medium);
4537 alock.acquire();
4538 treeLock.acquire();
4539 mediumLock.acquire();
4540 }
4541
4542 /* success: finally remember the attachment */
4543 setModified(IsModified_Storage);
4544 mMediaData.backup();
4545 mMediaData->mAttachments.push_back(attachment);
4546
4547 mediumLock.release();
4548 treeLock.release();
4549 alock.release();
4550
4551 if (fHotplug || fSilent)
4552 {
4553 if (!medium.isNull())
4554 {
4555 MediumLockList *pMediumLockList(new MediumLockList());
4556
4557 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4558 true /* fMediumLockWrite */,
4559 NULL,
4560 *pMediumLockList);
4561 alock.acquire();
4562 if (FAILED(rc))
4563 delete pMediumLockList;
4564 else
4565 {
4566 mData->mSession.mLockedMedia.Unlock();
4567 alock.release();
4568 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4569 mData->mSession.mLockedMedia.Lock();
4570 alock.acquire();
4571 }
4572 alock.release();
4573 }
4574
4575 if (SUCCEEDED(rc))
4576 {
4577 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4578 /* Remove lock list in case of error. */
4579 if (FAILED(rc))
4580 {
4581 mData->mSession.mLockedMedia.Unlock();
4582 mData->mSession.mLockedMedia.Remove(attachment);
4583 mData->mSession.mLockedMedia.Lock();
4584 }
4585 }
4586 }
4587
4588 mParent->i_saveModifiedRegistries();
4589
4590 return rc;
4591}
4592
4593STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4594 LONG aDevice)
4595{
4596 CheckComArgStrNotEmptyOrNull(aControllerName);
4597
4598 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4599 aControllerName, aControllerPort, aDevice));
4600
4601 AutoCaller autoCaller(this);
4602 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4603
4604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4605
4606 HRESULT rc = checkStateDependency(MutableStateDep);
4607 if (FAILED(rc)) return rc;
4608
4609 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4610
4611 /* Check for an existing controller. */
4612 ComObjPtr<StorageController> ctl;
4613 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4614 if (FAILED(rc)) return rc;
4615
4616 StorageControllerType_T ctrlType;
4617 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4618 if (FAILED(rc))
4619 return setError(E_FAIL,
4620 tr("Could not get type of controller '%ls'"),
4621 aControllerName);
4622
4623 bool fSilent = false;
4624 Utf8Str strReconfig;
4625
4626 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4627 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4628 if ( mData->mMachineState == MachineState_Paused
4629 && strReconfig == "1")
4630 fSilent = true;
4631
4632 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4633 bool fHotplug = false;
4634 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4635 fHotplug = true;
4636
4637 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4638 return setError(VBOX_E_INVALID_VM_STATE,
4639 tr("Controller '%ls' does not support hotplugging"),
4640 aControllerName);
4641
4642 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4643 aControllerName,
4644 aControllerPort,
4645 aDevice);
4646 if (!pAttach)
4647 return setError(VBOX_E_OBJECT_NOT_FOUND,
4648 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4649 aDevice, aControllerPort, aControllerName);
4650
4651 if (fHotplug && !pAttach->i_getHotPluggable())
4652 return setError(VBOX_E_NOT_SUPPORTED,
4653 tr("The device slot %d on port %d of controller '%ls' does not support hotplugging"),
4654 aDevice, aControllerPort, aControllerName);
4655
4656 /*
4657 * The VM has to detach the device before we delete any implicit diffs.
4658 * If this fails we can roll back without loosing data.
4659 */
4660 if (fHotplug || fSilent)
4661 {
4662 alock.release();
4663 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4664 alock.acquire();
4665 }
4666 if (FAILED(rc)) return rc;
4667
4668 /* If we are here everything went well and we can delete the implicit now. */
4669 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4670
4671 alock.release();
4672
4673 mParent->i_saveModifiedRegistries();
4674
4675 return rc;
4676}
4677
4678STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4679 LONG aDevice, BOOL aPassthrough)
4680{
4681 CheckComArgStrNotEmptyOrNull(aControllerName);
4682
4683 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4684 aControllerName, aControllerPort, aDevice, aPassthrough));
4685
4686 AutoCaller autoCaller(this);
4687 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4688
4689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4690
4691 HRESULT rc = checkStateDependency(MutableStateDep);
4692 if (FAILED(rc)) return rc;
4693
4694 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4695
4696 if (Global::IsOnlineOrTransient(mData->mMachineState))
4697 return setError(VBOX_E_INVALID_VM_STATE,
4698 tr("Invalid machine state: %s"),
4699 Global::stringifyMachineState(mData->mMachineState));
4700
4701 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4702 aControllerName,
4703 aControllerPort,
4704 aDevice);
4705 if (!pAttach)
4706 return setError(VBOX_E_OBJECT_NOT_FOUND,
4707 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4708 aDevice, aControllerPort, aControllerName);
4709
4710
4711 setModified(IsModified_Storage);
4712 mMediaData.backup();
4713
4714 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4715
4716 if (pAttach->i_getType() != DeviceType_DVD)
4717 return setError(E_INVALIDARG,
4718 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4719 aDevice, aControllerPort, aControllerName);
4720 pAttach->i_updatePassthrough(!!aPassthrough);
4721
4722 return S_OK;
4723}
4724
4725STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4726 LONG aDevice, BOOL aTemporaryEject)
4727{
4728 CheckComArgStrNotEmptyOrNull(aControllerName);
4729
4730 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4731 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4732
4733 AutoCaller autoCaller(this);
4734 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4735
4736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4737
4738 HRESULT rc = checkStateDependency(MutableStateDep);
4739 if (FAILED(rc)) return rc;
4740
4741 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4742 aControllerName,
4743 aControllerPort,
4744 aDevice);
4745 if (!pAttach)
4746 return setError(VBOX_E_OBJECT_NOT_FOUND,
4747 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4748 aDevice, aControllerPort, aControllerName);
4749
4750
4751 setModified(IsModified_Storage);
4752 mMediaData.backup();
4753
4754 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4755
4756 if (pAttach->i_getType() != DeviceType_DVD)
4757 return setError(E_INVALIDARG,
4758 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4759 aDevice, aControllerPort, aControllerName);
4760 pAttach->i_updateTempEject(!!aTemporaryEject);
4761
4762 return S_OK;
4763}
4764
4765STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4766 LONG aDevice, BOOL aNonRotational)
4767{
4768 CheckComArgStrNotEmptyOrNull(aControllerName);
4769
4770 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4771 aControllerName, aControllerPort, aDevice, aNonRotational));
4772
4773 AutoCaller autoCaller(this);
4774 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4775
4776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4777
4778 HRESULT rc = checkStateDependency(MutableStateDep);
4779 if (FAILED(rc)) return rc;
4780
4781 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4782
4783 if (Global::IsOnlineOrTransient(mData->mMachineState))
4784 return setError(VBOX_E_INVALID_VM_STATE,
4785 tr("Invalid machine state: %s"),
4786 Global::stringifyMachineState(mData->mMachineState));
4787
4788 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4789 aControllerName,
4790 aControllerPort,
4791 aDevice);
4792 if (!pAttach)
4793 return setError(VBOX_E_OBJECT_NOT_FOUND,
4794 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4795 aDevice, aControllerPort, aControllerName);
4796
4797
4798 setModified(IsModified_Storage);
4799 mMediaData.backup();
4800
4801 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4802
4803 if (pAttach->i_getType() != DeviceType_HardDisk)
4804 return setError(E_INVALIDARG,
4805 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4806 aDevice, aControllerPort, aControllerName);
4807 pAttach->i_updateNonRotational(!!aNonRotational);
4808
4809 return S_OK;
4810}
4811
4812STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4813 LONG aDevice, BOOL aDiscard)
4814{
4815 CheckComArgStrNotEmptyOrNull(aControllerName);
4816
4817 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4818 aControllerName, aControllerPort, aDevice, aDiscard));
4819
4820 AutoCaller autoCaller(this);
4821 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4822
4823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4824
4825 HRESULT rc = checkStateDependency(MutableStateDep);
4826 if (FAILED(rc)) return rc;
4827
4828 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4829
4830 if (Global::IsOnlineOrTransient(mData->mMachineState))
4831 return setError(VBOX_E_INVALID_VM_STATE,
4832 tr("Invalid machine state: %s"),
4833 Global::stringifyMachineState(mData->mMachineState));
4834
4835 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4836 aControllerName,
4837 aControllerPort,
4838 aDevice);
4839 if (!pAttach)
4840 return setError(VBOX_E_OBJECT_NOT_FOUND,
4841 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4842 aDevice, aControllerPort, aControllerName);
4843
4844
4845 setModified(IsModified_Storage);
4846 mMediaData.backup();
4847
4848 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4849
4850 if (pAttach->i_getType() != DeviceType_HardDisk)
4851 return setError(E_INVALIDARG,
4852 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4853 aDevice, aControllerPort, aControllerName);
4854 pAttach->i_updateDiscard(!!aDiscard);
4855
4856 return S_OK;
4857}
4858
4859STDMETHODIMP Machine::SetHotPluggableForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4860 LONG aDevice, BOOL aHotPluggable)
4861{
4862 CheckComArgStrNotEmptyOrNull(aControllerName);
4863
4864 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4865 aControllerName, aControllerPort, aDevice, aHotPluggable));
4866
4867 AutoCaller autoCaller(this);
4868 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4869
4870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4871
4872 HRESULT rc = checkStateDependency(MutableStateDep);
4873 if (FAILED(rc)) return rc;
4874
4875 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4876
4877 if (Global::IsOnlineOrTransient(mData->mMachineState))
4878 return setError(VBOX_E_INVALID_VM_STATE,
4879 tr("Invalid machine state: %s"),
4880 Global::stringifyMachineState(mData->mMachineState));
4881
4882 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4883 aControllerName,
4884 aControllerPort,
4885 aDevice);
4886 if (!pAttach)
4887 return setError(VBOX_E_OBJECT_NOT_FOUND,
4888 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4889 aDevice, aControllerPort, aControllerName);
4890
4891 /* Check for an existing controller. */
4892 ComObjPtr<StorageController> ctl;
4893 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4894 if (FAILED(rc)) return rc;
4895
4896 StorageControllerType_T ctrlType;
4897 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4898 if (FAILED(rc))
4899 return setError(E_FAIL,
4900 tr("Could not get type of controller '%ls'"),
4901 aControllerName);
4902
4903 if (!isControllerHotplugCapable(ctrlType))
4904 return setError(VBOX_E_NOT_SUPPORTED,
4905 tr("Controller '%ls' does not support changing the hot-pluggable device flag"),
4906 aControllerName);
4907
4908 setModified(IsModified_Storage);
4909 mMediaData.backup();
4910
4911 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4912
4913 if (pAttach->i_getType() == DeviceType_Floppy)
4914 return setError(E_INVALIDARG,
4915 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%ls' is a floppy drive"),
4916 aDevice, aControllerPort, aControllerName);
4917 pAttach->i_updateHotPluggable(!!aHotPluggable);
4918
4919 return S_OK;
4920}
4921
4922STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4923 LONG aDevice)
4924{
4925 int rc = S_OK;
4926 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4927 aControllerName, aControllerPort, aDevice));
4928
4929 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4930
4931 return rc;
4932}
4933
4934STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4935 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4936{
4937 CheckComArgStrNotEmptyOrNull(aControllerName);
4938
4939 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4940 aControllerName, aControllerPort, aDevice));
4941
4942 AutoCaller autoCaller(this);
4943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4944
4945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4946
4947 HRESULT rc = checkStateDependency(MutableStateDep);
4948 if (FAILED(rc)) return rc;
4949
4950 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4951
4952 if (Global::IsOnlineOrTransient(mData->mMachineState))
4953 return setError(VBOX_E_INVALID_VM_STATE,
4954 tr("Invalid machine state: %s"),
4955 Global::stringifyMachineState(mData->mMachineState));
4956
4957 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4958 aControllerName,
4959 aControllerPort,
4960 aDevice);
4961 if (!pAttach)
4962 return setError(VBOX_E_OBJECT_NOT_FOUND,
4963 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4964 aDevice, aControllerPort, aControllerName);
4965
4966
4967 setModified(IsModified_Storage);
4968 mMediaData.backup();
4969
4970 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4971 if (aBandwidthGroup && group.isNull())
4972 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4973
4974 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4975
4976 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4977 if (strBandwidthGroupOld.isNotEmpty())
4978 {
4979 /* Get the bandwidth group object and release it - this must not fail. */
4980 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4981 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4982 Assert(SUCCEEDED(rc));
4983
4984 pBandwidthGroupOld->i_release();
4985 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4986 }
4987
4988 if (!group.isNull())
4989 {
4990 group->i_reference();
4991 pAttach->i_updateBandwidthGroup(group->i_getName());
4992 }
4993
4994 return S_OK;
4995}
4996
4997STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4998 LONG aControllerPort,
4999 LONG aDevice,
5000 DeviceType_T aType)
5001{
5002 HRESULT rc = S_OK;
5003
5004 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
5005 aControllerName, aControllerPort, aDevice, aType));
5006
5007 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
5008
5009 return rc;
5010}
5011
5012
5013
5014STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
5015 LONG aControllerPort,
5016 LONG aDevice,
5017 BOOL aForce)
5018{
5019 int rc = S_OK;
5020 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
5021 aControllerName, aControllerPort, aForce));
5022
5023 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
5024
5025 return rc;
5026}
5027
5028STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
5029 LONG aControllerPort,
5030 LONG aDevice,
5031 IMedium *aMedium,
5032 BOOL aForce)
5033{
5034 int rc = S_OK;
5035 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
5036 aControllerName, aControllerPort, aDevice, aForce));
5037
5038 CheckComArgStrNotEmptyOrNull(aControllerName);
5039
5040 AutoCaller autoCaller(this);
5041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5042
5043 // request the host lock first, since might be calling Host methods for getting host drives;
5044 // next, protect the media tree all the while we're in here, as well as our member variables
5045 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
5046 this->lockHandle(),
5047 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5048
5049 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5050 aControllerName,
5051 aControllerPort,
5052 aDevice);
5053 if (pAttach.isNull())
5054 return setError(VBOX_E_OBJECT_NOT_FOUND,
5055 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
5056 aDevice, aControllerPort, aControllerName);
5057
5058 /* Remember previously mounted medium. The medium before taking the
5059 * backup is not necessarily the same thing. */
5060 ComObjPtr<Medium> oldmedium;
5061 oldmedium = pAttach->i_getMedium();
5062
5063 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
5064 if (aMedium && pMedium.isNull())
5065 return setError(E_INVALIDARG, "The given medium pointer is invalid");
5066
5067 AutoCaller mediumCaller(pMedium);
5068 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
5069
5070 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5071 if (pMedium)
5072 {
5073 DeviceType_T mediumType = pAttach->i_getType();
5074 switch (mediumType)
5075 {
5076 case DeviceType_DVD:
5077 case DeviceType_Floppy:
5078 break;
5079
5080 default:
5081 return setError(VBOX_E_INVALID_OBJECT_STATE,
5082 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5083 aControllerPort,
5084 aDevice,
5085 aControllerName);
5086 }
5087 }
5088
5089 setModified(IsModified_Storage);
5090 mMediaData.backup();
5091
5092 {
5093 // The backup operation makes the pAttach reference point to the
5094 // old settings. Re-get the correct reference.
5095 pAttach = findAttachment(mMediaData->mAttachments,
5096 aControllerName,
5097 aControllerPort,
5098 aDevice);
5099 if (!oldmedium.isNull())
5100 oldmedium->i_removeBackReference(mData->mUuid);
5101 if (!pMedium.isNull())
5102 {
5103 pMedium->i_addBackReference(mData->mUuid);
5104
5105 mediumLock.release();
5106 multiLock.release();
5107 addMediumToRegistry(pMedium);
5108 multiLock.acquire();
5109 mediumLock.acquire();
5110 }
5111
5112 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5113 pAttach->i_updateMedium(pMedium);
5114 }
5115
5116 setModified(IsModified_Storage);
5117
5118 mediumLock.release();
5119 multiLock.release();
5120 rc = onMediumChange(pAttach, aForce);
5121 multiLock.acquire();
5122 mediumLock.acquire();
5123
5124 /* On error roll back this change only. */
5125 if (FAILED(rc))
5126 {
5127 if (!pMedium.isNull())
5128 pMedium->i_removeBackReference(mData->mUuid);
5129 pAttach = findAttachment(mMediaData->mAttachments,
5130 aControllerName,
5131 aControllerPort,
5132 aDevice);
5133 /* If the attachment is gone in the meantime, bail out. */
5134 if (pAttach.isNull())
5135 return rc;
5136 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5137 if (!oldmedium.isNull())
5138 oldmedium->i_addBackReference(mData->mUuid);
5139 pAttach->i_updateMedium(oldmedium);
5140 }
5141
5142 mediumLock.release();
5143 multiLock.release();
5144
5145 mParent->i_saveModifiedRegistries();
5146
5147 return rc;
5148}
5149
5150STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5151 LONG aControllerPort,
5152 LONG aDevice,
5153 IMedium **aMedium)
5154{
5155 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5156 aControllerName, aControllerPort, aDevice));
5157
5158 CheckComArgStrNotEmptyOrNull(aControllerName);
5159 CheckComArgOutPointerValid(aMedium);
5160
5161 AutoCaller autoCaller(this);
5162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5163
5164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5165
5166 *aMedium = NULL;
5167
5168 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5169 aControllerName,
5170 aControllerPort,
5171 aDevice);
5172 if (pAttach.isNull())
5173 return setError(VBOX_E_OBJECT_NOT_FOUND,
5174 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5175 aDevice, aControllerPort, aControllerName);
5176
5177 pAttach->i_getMedium().queryInterfaceTo(aMedium);
5178
5179 return S_OK;
5180}
5181
5182STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5183{
5184 CheckComArgOutPointerValid(port);
5185 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5186
5187 AutoCaller autoCaller(this);
5188 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5189
5190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5191
5192 mSerialPorts[slot].queryInterfaceTo(port);
5193
5194 return S_OK;
5195}
5196
5197STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5198{
5199 CheckComArgOutPointerValid(port);
5200 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5201
5202 AutoCaller autoCaller(this);
5203 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5204
5205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5206
5207 mParallelPorts[slot].queryInterfaceTo(port);
5208
5209 return S_OK;
5210}
5211
5212STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5213{
5214 CheckComArgOutPointerValid(adapter);
5215 /* Do not assert if slot is out of range, just return the advertised
5216 status. testdriver/vbox.py triggers this in logVmInfo. */
5217 if (slot >= mNetworkAdapters.size())
5218 return setError(E_INVALIDARG,
5219 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5220 slot, mNetworkAdapters.size());
5221
5222 AutoCaller autoCaller(this);
5223 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5224
5225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5226
5227 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5228
5229 return S_OK;
5230}
5231
5232STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5233{
5234 CheckComArgOutSafeArrayPointerValid(aKeys);
5235
5236 AutoCaller autoCaller(this);
5237 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5238
5239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5240
5241 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5242 int i = 0;
5243 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5244 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5245 ++it, ++i)
5246 {
5247 const Utf8Str &strKey = it->first;
5248 strKey.cloneTo(&saKeys[i]);
5249 }
5250 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5251
5252 return S_OK;
5253 }
5254
5255 /**
5256 * @note Locks this object for reading.
5257 */
5258STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5259 BSTR *aValue)
5260{
5261 CheckComArgStrNotEmptyOrNull(aKey);
5262 CheckComArgOutPointerValid(aValue);
5263
5264 AutoCaller autoCaller(this);
5265 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5266
5267 /* start with nothing found */
5268 Bstr bstrResult("");
5269
5270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5271
5272 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5273 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5274 // found:
5275 bstrResult = it->second; // source is a Utf8Str
5276
5277 /* return the result to caller (may be empty) */
5278 bstrResult.cloneTo(aValue);
5279
5280 return S_OK;
5281}
5282
5283 /**
5284 * @note Locks mParent for writing + this object for writing.
5285 */
5286STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5287{
5288 CheckComArgStrNotEmptyOrNull(aKey);
5289
5290 AutoCaller autoCaller(this);
5291 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5292
5293 Utf8Str strKey(aKey);
5294 Utf8Str strValue(aValue);
5295 Utf8Str strOldValue; // empty
5296
5297 // locking note: we only hold the read lock briefly to look up the old value,
5298 // then release it and call the onExtraCanChange callbacks. There is a small
5299 // chance of a race insofar as the callback might be called twice if two callers
5300 // change the same key at the same time, but that's a much better solution
5301 // than the deadlock we had here before. The actual changing of the extradata
5302 // is then performed under the write lock and race-free.
5303
5304 // look up the old value first; if nothing has changed then we need not do anything
5305 {
5306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5307 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5308 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5309 strOldValue = it->second;
5310 }
5311
5312 bool fChanged;
5313 if ((fChanged = (strOldValue != strValue)))
5314 {
5315 // ask for permission from all listeners outside the locks;
5316 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5317 // lock to copy the list of callbacks to invoke
5318 Bstr error;
5319 Bstr bstrValue(aValue);
5320
5321 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5322 {
5323 const char *sep = error.isEmpty() ? "" : ": ";
5324 CBSTR err = error.raw();
5325 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5326 sep, err));
5327 return setError(E_ACCESSDENIED,
5328 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5329 aKey,
5330 bstrValue.raw(),
5331 sep,
5332 err);
5333 }
5334
5335 // data is changing and change not vetoed: then write it out under the lock
5336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5337
5338 if (isSnapshotMachine())
5339 {
5340 HRESULT rc = checkStateDependency(MutableStateDep);
5341 if (FAILED(rc)) return rc;
5342 }
5343
5344 if (strValue.isEmpty())
5345 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5346 else
5347 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5348 // creates a new key if needed
5349
5350 bool fNeedsGlobalSaveSettings = false;
5351 saveSettings(&fNeedsGlobalSaveSettings);
5352
5353 if (fNeedsGlobalSaveSettings)
5354 {
5355 // save the global settings; for that we should hold only the VirtualBox lock
5356 alock.release();
5357 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5358 mParent->i_saveSettings();
5359 }
5360 }
5361
5362 // fire notification outside the lock
5363 if (fChanged)
5364 mParent->i_onExtraDataChange(mData->mUuid, aKey, aValue);
5365
5366 return S_OK;
5367}
5368
5369STDMETHODIMP Machine::SetSettingsFilePath(IN_BSTR aFilePath, IProgress **aProgress)
5370{
5371 CheckComArgStrNotEmptyOrNull(aFilePath);
5372 CheckComArgOutPointerValid(aProgress);
5373
5374 AutoCaller autoCaller(this);
5375 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5376
5377 *aProgress = NULL;
5378 ReturnComNotImplemented();
5379}
5380
5381STDMETHODIMP Machine::SaveSettings()
5382{
5383 AutoCaller autoCaller(this);
5384 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5385
5386 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5387
5388 /* when there was auto-conversion, we want to save the file even if
5389 * the VM is saved */
5390 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5391 if (FAILED(rc)) return rc;
5392
5393 /* the settings file path may never be null */
5394 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5395
5396 /* save all VM data excluding snapshots */
5397 bool fNeedsGlobalSaveSettings = false;
5398 rc = saveSettings(&fNeedsGlobalSaveSettings);
5399 mlock.release();
5400
5401 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5402 {
5403 // save the global settings; for that we should hold only the VirtualBox lock
5404 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5405 rc = mParent->i_saveSettings();
5406 }
5407
5408 return rc;
5409}
5410
5411STDMETHODIMP Machine::DiscardSettings()
5412{
5413 AutoCaller autoCaller(this);
5414 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5415
5416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5417
5418 HRESULT rc = checkStateDependency(MutableStateDep);
5419 if (FAILED(rc)) return rc;
5420
5421 /*
5422 * during this rollback, the session will be notified if data has
5423 * been actually changed
5424 */
5425 rollback(true /* aNotify */);
5426
5427 return S_OK;
5428}
5429
5430/** @note Locks objects! */
5431STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5432 ComSafeArrayOut(IMedium*, aMedia))
5433{
5434 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5435 AutoLimitedCaller autoCaller(this);
5436 AssertComRCReturnRC(autoCaller.rc());
5437
5438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5439
5440 Guid id(getId());
5441
5442 if (mData->mSession.mState != SessionState_Unlocked)
5443 return setError(VBOX_E_INVALID_OBJECT_STATE,
5444 tr("Cannot unregister the machine '%s' while it is locked"),
5445 mUserData->s.strName.c_str());
5446
5447 // wait for state dependents to drop to zero
5448 ensureNoStateDependencies();
5449
5450 if (!mData->mAccessible)
5451 {
5452 // inaccessible maschines can only be unregistered; uninitialize ourselves
5453 // here because currently there may be no unregistered that are inaccessible
5454 // (this state combination is not supported). Note releasing the caller and
5455 // leaving the lock before calling uninit()
5456 alock.release();
5457 autoCaller.release();
5458
5459 uninit();
5460
5461 mParent->i_unregisterMachine(this, id);
5462 // calls VirtualBox::saveSettings()
5463
5464 return S_OK;
5465 }
5466
5467 HRESULT rc = S_OK;
5468
5469 // discard saved state
5470 if (mData->mMachineState == MachineState_Saved)
5471 {
5472 // add the saved state file to the list of files the caller should delete
5473 Assert(!mSSData->strStateFilePath.isEmpty());
5474 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5475
5476 mSSData->strStateFilePath.setNull();
5477
5478 // unconditionally set the machine state to powered off, we now
5479 // know no session has locked the machine
5480 mData->mMachineState = MachineState_PoweredOff;
5481 }
5482
5483 size_t cSnapshots = 0;
5484 if (mData->mFirstSnapshot)
5485 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5486 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5487 // fail now before we start detaching media
5488 return setError(VBOX_E_INVALID_OBJECT_STATE,
5489 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5490 mUserData->s.strName.c_str(), cSnapshots);
5491
5492 // This list collects the medium objects from all medium attachments
5493 // which we will detach from the machine and its snapshots, in a specific
5494 // order which allows for closing all media without getting "media in use"
5495 // errors, simply by going through the list from the front to the back:
5496 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5497 // and must be closed before the parent media from the snapshots, or closing the parents
5498 // will fail because they still have children);
5499 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5500 // the root ("first") snapshot of the machine.
5501 MediaList llMedia;
5502
5503 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5504 && mMediaData->mAttachments.size()
5505 )
5506 {
5507 // we have media attachments: detach them all and add the Medium objects to our list
5508 if (cleanupMode != CleanupMode_UnregisterOnly)
5509 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5510 else
5511 return setError(VBOX_E_INVALID_OBJECT_STATE,
5512 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5513 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5514 }
5515
5516 if (cSnapshots)
5517 {
5518 // autoCleanup must be true here, or we would have failed above
5519
5520 // add the media from the medium attachments of the snapshots to llMedia
5521 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5522 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5523 // into the children first
5524
5525 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5526 MachineState_T oldState = mData->mMachineState;
5527 mData->mMachineState = MachineState_DeletingSnapshot;
5528
5529 // make a copy of the first snapshot so the refcount does not drop to 0
5530 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5531 // because of the AutoCaller voodoo)
5532 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5533
5534 // GO!
5535 pFirstSnapshot->i_uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5536
5537 mData->mMachineState = oldState;
5538 }
5539
5540 if (FAILED(rc))
5541 {
5542 rollbackMedia();
5543 return rc;
5544 }
5545
5546 // commit all the media changes made above
5547 commitMedia();
5548
5549 mData->mRegistered = false;
5550
5551 // machine lock no longer needed
5552 alock.release();
5553
5554 // return media to caller
5555 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5556 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5557
5558 mParent->i_unregisterMachine(this, id);
5559 // calls VirtualBox::saveSettings() and VirtualBox::i_saveModifiedRegistries()
5560
5561 return S_OK;
5562}
5563
5564struct Machine::DeleteTask
5565{
5566 ComObjPtr<Machine> pMachine;
5567 RTCList<ComPtr<IMedium> > llMediums;
5568 StringsList llFilesToDelete;
5569 ComObjPtr<Progress> pProgress;
5570};
5571
5572STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5573{
5574 LogFlowFuncEnter();
5575
5576 AutoCaller autoCaller(this);
5577 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5578
5579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5580
5581 HRESULT rc = checkStateDependency(MutableStateDep);
5582 if (FAILED(rc)) return rc;
5583
5584 if (mData->mRegistered)
5585 return setError(VBOX_E_INVALID_VM_STATE,
5586 tr("Cannot delete settings of a registered machine"));
5587
5588 DeleteTask *pTask = new DeleteTask;
5589 pTask->pMachine = this;
5590 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5591
5592 // collect files to delete
5593 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5594
5595 for (size_t i = 0; i < sfaMedia.size(); ++i)
5596 {
5597 IMedium *pIMedium(sfaMedia[i]);
5598 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5599 if (pMedium.isNull())
5600 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5601 SafeArray<BSTR> ids;
5602 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5603 if (FAILED(rc)) return rc;
5604 /* At this point the medium should not have any back references
5605 * anymore. If it has it is attached to another VM and *must* not
5606 * deleted. */
5607 if (ids.size() < 1)
5608 pTask->llMediums.append(pMedium);
5609 }
5610 if (mData->pMachineConfigFile->fileExists())
5611 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5612
5613 pTask->pProgress.createObject();
5614 pTask->pProgress->init(getVirtualBox(),
5615 static_cast<IMachine*>(this) /* aInitiator */,
5616 Bstr(tr("Deleting files")).raw(),
5617 true /* fCancellable */,
5618 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5619 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5620
5621 int vrc = RTThreadCreate(NULL,
5622 Machine::deleteThread,
5623 (void*)pTask,
5624 0,
5625 RTTHREADTYPE_MAIN_WORKER,
5626 0,
5627 "MachineDelete");
5628
5629 pTask->pProgress.queryInterfaceTo(aProgress);
5630
5631 if (RT_FAILURE(vrc))
5632 {
5633 delete pTask;
5634 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5635 }
5636
5637 LogFlowFuncLeave();
5638
5639 return S_OK;
5640}
5641
5642/**
5643 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5644 * calls Machine::deleteTaskWorker() on the actual machine object.
5645 * @param Thread
5646 * @param pvUser
5647 * @return
5648 */
5649/*static*/
5650DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5651{
5652 LogFlowFuncEnter();
5653
5654 DeleteTask *pTask = (DeleteTask*)pvUser;
5655 Assert(pTask);
5656 Assert(pTask->pMachine);
5657 Assert(pTask->pProgress);
5658
5659 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5660 pTask->pProgress->notifyComplete(rc);
5661
5662 delete pTask;
5663
5664 LogFlowFuncLeave();
5665
5666 NOREF(Thread);
5667
5668 return VINF_SUCCESS;
5669}
5670
5671/**
5672 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5673 * @param task
5674 * @return
5675 */
5676HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5677{
5678 AutoCaller autoCaller(this);
5679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5680
5681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5682
5683 HRESULT rc = S_OK;
5684
5685 try
5686 {
5687 ULONG uLogHistoryCount = 3;
5688 ComPtr<ISystemProperties> systemProperties;
5689 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5690 if (FAILED(rc)) throw rc;
5691
5692 if (!systemProperties.isNull())
5693 {
5694 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5695 if (FAILED(rc)) throw rc;
5696 }
5697
5698 MachineState_T oldState = mData->mMachineState;
5699 setMachineState(MachineState_SettingUp);
5700 alock.release();
5701 for (size_t i = 0; i < task.llMediums.size(); ++i)
5702 {
5703 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5704 {
5705 AutoCaller mac(pMedium);
5706 if (FAILED(mac.rc())) throw mac.rc();
5707 Utf8Str strLocation = pMedium->i_getLocationFull();
5708 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5709 if (FAILED(rc)) throw rc;
5710 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5711 }
5712 ComPtr<IProgress> pProgress2;
5713 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5714 if (FAILED(rc)) throw rc;
5715 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5716 if (FAILED(rc)) throw rc;
5717 /* Check the result of the asynchronous process. */
5718 LONG iRc;
5719 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5720 if (FAILED(rc)) throw rc;
5721 /* If the thread of the progress object has an error, then
5722 * retrieve the error info from there, or it'll be lost. */
5723 if (FAILED(iRc))
5724 throw setError(ProgressErrorInfo(pProgress2));
5725
5726 /* Close the medium, deliberately without checking the return
5727 * code, and without leaving any trace in the error info, as
5728 * a failure here is a very minor issue, which shouldn't happen
5729 * as above we even managed to delete the medium. */
5730 {
5731 ErrorInfoKeeper eik;
5732 pMedium->Close();
5733 }
5734 }
5735 setMachineState(oldState);
5736 alock.acquire();
5737
5738 // delete the files pushed on the task list by Machine::Delete()
5739 // (this includes saved states of the machine and snapshots and
5740 // medium storage files from the IMedium list passed in, and the
5741 // machine XML file)
5742 StringsList::const_iterator it = task.llFilesToDelete.begin();
5743 while (it != task.llFilesToDelete.end())
5744 {
5745 const Utf8Str &strFile = *it;
5746 LogFunc(("Deleting file %s\n", strFile.c_str()));
5747 int vrc = RTFileDelete(strFile.c_str());
5748 if (RT_FAILURE(vrc))
5749 throw setError(VBOX_E_IPRT_ERROR,
5750 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5751
5752 ++it;
5753 if (it == task.llFilesToDelete.end())
5754 {
5755 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5756 if (FAILED(rc)) throw rc;
5757 break;
5758 }
5759
5760 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5761 if (FAILED(rc)) throw rc;
5762 }
5763
5764 /* delete the settings only when the file actually exists */
5765 if (mData->pMachineConfigFile->fileExists())
5766 {
5767 /* Delete any backup or uncommitted XML files. Ignore failures.
5768 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5769 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5770 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5771 RTFileDelete(otherXml.c_str());
5772 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5773 RTFileDelete(otherXml.c_str());
5774
5775 /* delete the Logs folder, nothing important should be left
5776 * there (we don't check for errors because the user might have
5777 * some private files there that we don't want to delete) */
5778 Utf8Str logFolder;
5779 getLogFolder(logFolder);
5780 Assert(logFolder.length());
5781 if (RTDirExists(logFolder.c_str()))
5782 {
5783 /* Delete all VBox.log[.N] files from the Logs folder
5784 * (this must be in sync with the rotation logic in
5785 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5786 * files that may have been created by the GUI. */
5787 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5788 logFolder.c_str(), RTPATH_DELIMITER);
5789 RTFileDelete(log.c_str());
5790 log = Utf8StrFmt("%s%cVBox.png",
5791 logFolder.c_str(), RTPATH_DELIMITER);
5792 RTFileDelete(log.c_str());
5793 for (int i = uLogHistoryCount; i > 0; i--)
5794 {
5795 log = Utf8StrFmt("%s%cVBox.log.%d",
5796 logFolder.c_str(), RTPATH_DELIMITER, i);
5797 RTFileDelete(log.c_str());
5798 log = Utf8StrFmt("%s%cVBox.png.%d",
5799 logFolder.c_str(), RTPATH_DELIMITER, i);
5800 RTFileDelete(log.c_str());
5801 }
5802
5803 RTDirRemove(logFolder.c_str());
5804 }
5805
5806 /* delete the Snapshots folder, nothing important should be left
5807 * there (we don't check for errors because the user might have
5808 * some private files there that we don't want to delete) */
5809 Utf8Str strFullSnapshotFolder;
5810 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5811 Assert(!strFullSnapshotFolder.isEmpty());
5812 if (RTDirExists(strFullSnapshotFolder.c_str()))
5813 RTDirRemove(strFullSnapshotFolder.c_str());
5814
5815 // delete the directory that contains the settings file, but only
5816 // if it matches the VM name
5817 Utf8Str settingsDir;
5818 if (isInOwnDir(&settingsDir))
5819 RTDirRemove(settingsDir.c_str());
5820 }
5821
5822 alock.release();
5823
5824 mParent->i_saveModifiedRegistries();
5825 }
5826 catch (HRESULT aRC) { rc = aRC; }
5827
5828 return rc;
5829}
5830
5831STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5832{
5833 CheckComArgOutPointerValid(aSnapshot);
5834
5835 AutoCaller autoCaller(this);
5836 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5837
5838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5839
5840 ComObjPtr<Snapshot> pSnapshot;
5841 HRESULT rc;
5842
5843 if (!aNameOrId || !*aNameOrId)
5844 // null case (caller wants root snapshot): findSnapshotById() handles this
5845 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5846 else
5847 {
5848 Guid uuid(aNameOrId);
5849 if (uuid.isValid())
5850 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5851 else
5852 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5853 }
5854 pSnapshot.queryInterfaceTo(aSnapshot);
5855
5856 return rc;
5857}
5858
5859STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5860{
5861 CheckComArgStrNotEmptyOrNull(aName);
5862 CheckComArgStrNotEmptyOrNull(aHostPath);
5863
5864 AutoCaller autoCaller(this);
5865 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5866
5867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5868
5869 HRESULT rc = checkStateDependency(MutableStateDep);
5870 if (FAILED(rc)) return rc;
5871
5872 Utf8Str strName(aName);
5873
5874 ComObjPtr<SharedFolder> sharedFolder;
5875 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5876 if (SUCCEEDED(rc))
5877 return setError(VBOX_E_OBJECT_IN_USE,
5878 tr("Shared folder named '%s' already exists"),
5879 strName.c_str());
5880
5881 sharedFolder.createObject();
5882 rc = sharedFolder->init(getMachine(),
5883 strName,
5884 aHostPath,
5885 !!aWritable,
5886 !!aAutoMount,
5887 true /* fFailOnError */);
5888 if (FAILED(rc)) return rc;
5889
5890 setModified(IsModified_SharedFolders);
5891 mHWData.backup();
5892 mHWData->mSharedFolders.push_back(sharedFolder);
5893
5894 /* inform the direct session if any */
5895 alock.release();
5896 onSharedFolderChange();
5897
5898 return S_OK;
5899}
5900
5901STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5902{
5903 CheckComArgStrNotEmptyOrNull(aName);
5904
5905 AutoCaller autoCaller(this);
5906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5907
5908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5909
5910 HRESULT rc = checkStateDependency(MutableStateDep);
5911 if (FAILED(rc)) return rc;
5912
5913 ComObjPtr<SharedFolder> sharedFolder;
5914 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5915 if (FAILED(rc)) return rc;
5916
5917 setModified(IsModified_SharedFolders);
5918 mHWData.backup();
5919 mHWData->mSharedFolders.remove(sharedFolder);
5920
5921 /* inform the direct session if any */
5922 alock.release();
5923 onSharedFolderChange();
5924
5925 return S_OK;
5926}
5927
5928STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5929{
5930 CheckComArgOutPointerValid(aCanShow);
5931
5932 /* start with No */
5933 *aCanShow = FALSE;
5934
5935 AutoCaller autoCaller(this);
5936 AssertComRCReturnRC(autoCaller.rc());
5937
5938 ComPtr<IInternalSessionControl> directControl;
5939 {
5940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5941
5942 if (mData->mSession.mState != SessionState_Locked)
5943 return setError(VBOX_E_INVALID_VM_STATE,
5944 tr("Machine is not locked for session (session state: %s)"),
5945 Global::stringifySessionState(mData->mSession.mState));
5946
5947 directControl = mData->mSession.mDirectControl;
5948 }
5949
5950 /* ignore calls made after #OnSessionEnd() is called */
5951 if (!directControl)
5952 return S_OK;
5953
5954 LONG64 dummy;
5955 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5956}
5957
5958STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5959{
5960 CheckComArgOutPointerValid(aWinId);
5961
5962 AutoCaller autoCaller(this);
5963 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5964
5965 ComPtr<IInternalSessionControl> directControl;
5966 {
5967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5968
5969 if (mData->mSession.mState != SessionState_Locked)
5970 return setError(E_FAIL,
5971 tr("Machine is not locked for session (session state: %s)"),
5972 Global::stringifySessionState(mData->mSession.mState));
5973
5974 directControl = mData->mSession.mDirectControl;
5975 }
5976
5977 /* ignore calls made after #OnSessionEnd() is called */
5978 if (!directControl)
5979 return S_OK;
5980
5981 BOOL dummy;
5982 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5983}
5984
5985#ifdef VBOX_WITH_GUEST_PROPS
5986/**
5987 * Look up a guest property in VBoxSVC's internal structures.
5988 */
5989HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5990 BSTR *aValue,
5991 LONG64 *aTimestamp,
5992 BSTR *aFlags) const
5993{
5994 using namespace guestProp;
5995
5996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5997 Utf8Str strName(aName);
5998 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5999
6000 if (it != mHWData->mGuestProperties.end())
6001 {
6002 char szFlags[MAX_FLAGS_LEN + 1];
6003 it->second.strValue.cloneTo(aValue);
6004 *aTimestamp = it->second.mTimestamp;
6005 writeFlags(it->second.mFlags, szFlags);
6006 Bstr(szFlags).cloneTo(aFlags);
6007 }
6008
6009 return S_OK;
6010}
6011
6012/**
6013 * Query the VM that a guest property belongs to for the property.
6014 * @returns E_ACCESSDENIED if the VM process is not available or not
6015 * currently handling queries and the lookup should then be done in
6016 * VBoxSVC.
6017 */
6018HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
6019 BSTR *aValue,
6020 LONG64 *aTimestamp,
6021 BSTR *aFlags) const
6022{
6023 HRESULT rc;
6024 ComPtr<IInternalSessionControl> directControl;
6025 directControl = mData->mSession.mDirectControl;
6026
6027 /* fail if we were called after #OnSessionEnd() is called. This is a
6028 * silly race condition. */
6029
6030 /** @todo This code is bothering API clients (like python script clients) with
6031 * the AccessGuestProperty call, creating unncessary IPC. Need to
6032 * have a way of figuring out which kind of direct session it is... */
6033 if (!directControl)
6034 rc = E_ACCESSDENIED;
6035 else
6036 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
6037 false /* isSetter */,
6038 aValue, aTimestamp, aFlags);
6039 return rc;
6040}
6041#endif // VBOX_WITH_GUEST_PROPS
6042
6043STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
6044 BSTR *aValue,
6045 LONG64 *aTimestamp,
6046 BSTR *aFlags)
6047{
6048#ifndef VBOX_WITH_GUEST_PROPS
6049 ReturnComNotImplemented();
6050#else // VBOX_WITH_GUEST_PROPS
6051 CheckComArgStrNotEmptyOrNull(aName);
6052 CheckComArgOutPointerValid(aValue);
6053 CheckComArgOutPointerValid(aTimestamp);
6054 CheckComArgOutPointerValid(aFlags);
6055
6056 AutoCaller autoCaller(this);
6057 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6058
6059 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
6060 if (rc == E_ACCESSDENIED)
6061 /* The VM is not running or the service is not (yet) accessible */
6062 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
6063 return rc;
6064#endif // VBOX_WITH_GUEST_PROPS
6065}
6066
6067STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
6068{
6069 LONG64 dummyTimestamp;
6070 Bstr dummyFlags;
6071 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
6072}
6073
6074STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
6075{
6076 Bstr dummyValue;
6077 Bstr dummyFlags;
6078 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
6079}
6080
6081#ifdef VBOX_WITH_GUEST_PROPS
6082/**
6083 * Set a guest property in VBoxSVC's internal structures.
6084 */
6085HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
6086 IN_BSTR aFlags)
6087{
6088 using namespace guestProp;
6089
6090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6091 HRESULT rc = S_OK;
6092
6093 rc = checkStateDependency(MutableStateDep);
6094 if (FAILED(rc)) return rc;
6095
6096 try
6097 {
6098 Utf8Str utf8Name(aName);
6099 Utf8Str utf8Flags(aFlags);
6100 uint32_t fFlags = NILFLAG;
6101 if ( aFlags != NULL
6102 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6103 return setError(E_INVALIDARG,
6104 tr("Invalid guest property flag values: '%ls'"),
6105 aFlags);
6106
6107 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6108 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6109 if (it == mHWData->mGuestProperties.end())
6110 {
6111 if (!fDelete)
6112 {
6113 setModified(IsModified_MachineData);
6114 mHWData.backupEx();
6115
6116 RTTIMESPEC time;
6117 HWData::GuestProperty prop;
6118 prop.strValue = aValue;
6119 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6120 prop.mFlags = fFlags;
6121 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6122 }
6123 }
6124 else
6125 {
6126 if (it->second.mFlags & (RDONLYHOST))
6127 {
6128 rc = setError(E_ACCESSDENIED,
6129 tr("The property '%ls' cannot be changed by the host"),
6130 aName);
6131 }
6132 else
6133 {
6134 setModified(IsModified_MachineData);
6135 mHWData.backupEx();
6136
6137 /* The backupEx() operation invalidates our iterator,
6138 * so get a new one. */
6139 it = mHWData->mGuestProperties.find(utf8Name);
6140 Assert(it != mHWData->mGuestProperties.end());
6141
6142 if (!fDelete)
6143 {
6144 RTTIMESPEC time;
6145 it->second.strValue = aValue;
6146 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6147 it->second.mFlags = fFlags;
6148 }
6149 else
6150 mHWData->mGuestProperties.erase(it);
6151 }
6152 }
6153
6154 if ( SUCCEEDED(rc)
6155 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6156 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6157 RTSTR_MAX,
6158 utf8Name.c_str(),
6159 RTSTR_MAX,
6160 NULL)
6161 )
6162 )
6163 {
6164 alock.release();
6165
6166 mParent->i_onGuestPropertyChange(mData->mUuid, aName,
6167 aValue ? aValue : Bstr("").raw(),
6168 aFlags ? aFlags : Bstr("").raw());
6169 }
6170 }
6171 catch (std::bad_alloc &)
6172 {
6173 rc = E_OUTOFMEMORY;
6174 }
6175
6176 return rc;
6177}
6178
6179/**
6180 * Set a property on the VM that that property belongs to.
6181 * @returns E_ACCESSDENIED if the VM process is not available or not
6182 * currently handling queries and the setting should then be done in
6183 * VBoxSVC.
6184 */
6185HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6186 IN_BSTR aFlags)
6187{
6188 HRESULT rc;
6189
6190 try
6191 {
6192 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6193
6194 BSTR dummy = NULL; /* will not be changed (setter) */
6195 LONG64 dummy64;
6196 if (!directControl)
6197 rc = E_ACCESSDENIED;
6198 else
6199 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6200 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6201 true /* isSetter */,
6202 &dummy, &dummy64, &dummy);
6203 }
6204 catch (std::bad_alloc &)
6205 {
6206 rc = E_OUTOFMEMORY;
6207 }
6208
6209 return rc;
6210}
6211#endif // VBOX_WITH_GUEST_PROPS
6212
6213STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6214 IN_BSTR aFlags)
6215{
6216#ifndef VBOX_WITH_GUEST_PROPS
6217 ReturnComNotImplemented();
6218#else // VBOX_WITH_GUEST_PROPS
6219 CheckComArgStrNotEmptyOrNull(aName);
6220 CheckComArgMaybeNull(aFlags);
6221 CheckComArgMaybeNull(aValue);
6222
6223 AutoCaller autoCaller(this);
6224 if (FAILED(autoCaller.rc()))
6225 return autoCaller.rc();
6226
6227 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6228 if (rc == E_ACCESSDENIED)
6229 /* The VM is not running or the service is not (yet) accessible */
6230 rc = setGuestPropertyToService(aName, aValue, aFlags);
6231 return rc;
6232#endif // VBOX_WITH_GUEST_PROPS
6233}
6234
6235STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6236{
6237 return SetGuestProperty(aName, aValue, NULL);
6238}
6239
6240STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6241{
6242 return SetGuestProperty(aName, NULL, NULL);
6243}
6244
6245#ifdef VBOX_WITH_GUEST_PROPS
6246/**
6247 * Enumerate the guest properties in VBoxSVC's internal structures.
6248 */
6249HRESULT Machine::enumerateGuestPropertiesInService
6250 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6251 ComSafeArrayOut(BSTR, aValues),
6252 ComSafeArrayOut(LONG64, aTimestamps),
6253 ComSafeArrayOut(BSTR, aFlags))
6254{
6255 using namespace guestProp;
6256
6257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6258 Utf8Str strPatterns(aPatterns);
6259
6260 HWData::GuestPropertyMap propMap;
6261
6262 /*
6263 * Look for matching patterns and build up a list.
6264 */
6265 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6266 while (it != mHWData->mGuestProperties.end())
6267 {
6268 if ( strPatterns.isEmpty()
6269 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6270 RTSTR_MAX,
6271 it->first.c_str(),
6272 RTSTR_MAX,
6273 NULL)
6274 )
6275 {
6276 propMap.insert(*it);
6277 }
6278
6279 it++;
6280 }
6281
6282 alock.release();
6283
6284 /*
6285 * And build up the arrays for returning the property information.
6286 */
6287 size_t cEntries = propMap.size();
6288 SafeArray<BSTR> names(cEntries);
6289 SafeArray<BSTR> values(cEntries);
6290 SafeArray<LONG64> timestamps(cEntries);
6291 SafeArray<BSTR> flags(cEntries);
6292 size_t iProp = 0;
6293
6294 it = propMap.begin();
6295 while (it != propMap.end())
6296 {
6297 char szFlags[MAX_FLAGS_LEN + 1];
6298 it->first.cloneTo(&names[iProp]);
6299 it->second.strValue.cloneTo(&values[iProp]);
6300 timestamps[iProp] = it->second.mTimestamp;
6301 writeFlags(it->second.mFlags, szFlags);
6302 Bstr(szFlags).cloneTo(&flags[iProp++]);
6303 it++;
6304 }
6305 names.detachTo(ComSafeArrayOutArg(aNames));
6306 values.detachTo(ComSafeArrayOutArg(aValues));
6307 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6308 flags.detachTo(ComSafeArrayOutArg(aFlags));
6309 return S_OK;
6310}
6311
6312/**
6313 * Enumerate the properties managed by a VM.
6314 * @returns E_ACCESSDENIED if the VM process is not available or not
6315 * currently handling queries and the setting should then be done in
6316 * VBoxSVC.
6317 */
6318HRESULT Machine::enumerateGuestPropertiesOnVM
6319 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6320 ComSafeArrayOut(BSTR, aValues),
6321 ComSafeArrayOut(LONG64, aTimestamps),
6322 ComSafeArrayOut(BSTR, aFlags))
6323{
6324 HRESULT rc;
6325 ComPtr<IInternalSessionControl> directControl;
6326 directControl = mData->mSession.mDirectControl;
6327
6328 if (!directControl)
6329 rc = E_ACCESSDENIED;
6330 else
6331 rc = directControl->EnumerateGuestProperties
6332 (aPatterns, ComSafeArrayOutArg(aNames),
6333 ComSafeArrayOutArg(aValues),
6334 ComSafeArrayOutArg(aTimestamps),
6335 ComSafeArrayOutArg(aFlags));
6336 return rc;
6337}
6338#endif // VBOX_WITH_GUEST_PROPS
6339
6340STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6341 ComSafeArrayOut(BSTR, aNames),
6342 ComSafeArrayOut(BSTR, aValues),
6343 ComSafeArrayOut(LONG64, aTimestamps),
6344 ComSafeArrayOut(BSTR, aFlags))
6345{
6346#ifndef VBOX_WITH_GUEST_PROPS
6347 ReturnComNotImplemented();
6348#else // VBOX_WITH_GUEST_PROPS
6349 CheckComArgMaybeNull(aPatterns);
6350 CheckComArgOutSafeArrayPointerValid(aNames);
6351 CheckComArgOutSafeArrayPointerValid(aValues);
6352 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6353 CheckComArgOutSafeArrayPointerValid(aFlags);
6354
6355 AutoCaller autoCaller(this);
6356 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6357
6358 HRESULT rc = enumerateGuestPropertiesOnVM
6359 (aPatterns, ComSafeArrayOutArg(aNames),
6360 ComSafeArrayOutArg(aValues),
6361 ComSafeArrayOutArg(aTimestamps),
6362 ComSafeArrayOutArg(aFlags));
6363 if (rc == E_ACCESSDENIED)
6364 /* The VM is not running or the service is not (yet) accessible */
6365 rc = enumerateGuestPropertiesInService
6366 (aPatterns, ComSafeArrayOutArg(aNames),
6367 ComSafeArrayOutArg(aValues),
6368 ComSafeArrayOutArg(aTimestamps),
6369 ComSafeArrayOutArg(aFlags));
6370 return rc;
6371#endif // VBOX_WITH_GUEST_PROPS
6372}
6373
6374STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6375 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6376{
6377 MediaData::AttachmentList atts;
6378
6379 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6380 if (FAILED(rc)) return rc;
6381
6382 SafeIfaceArray<IMediumAttachment> attachments(atts);
6383 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6384
6385 return S_OK;
6386}
6387
6388STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6389 LONG aControllerPort,
6390 LONG aDevice,
6391 IMediumAttachment **aAttachment)
6392{
6393 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6394 aControllerName, aControllerPort, aDevice));
6395
6396 CheckComArgStrNotEmptyOrNull(aControllerName);
6397 CheckComArgOutPointerValid(aAttachment);
6398
6399 AutoCaller autoCaller(this);
6400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6401
6402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6403
6404 *aAttachment = NULL;
6405
6406 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6407 aControllerName,
6408 aControllerPort,
6409 aDevice);
6410 if (pAttach.isNull())
6411 return setError(VBOX_E_OBJECT_NOT_FOUND,
6412 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6413 aDevice, aControllerPort, aControllerName);
6414
6415 pAttach.queryInterfaceTo(aAttachment);
6416
6417 return S_OK;
6418}
6419
6420STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6421 StorageBus_T aConnectionType,
6422 IStorageController **controller)
6423{
6424 CheckComArgStrNotEmptyOrNull(aName);
6425
6426 if ( (aConnectionType <= StorageBus_Null)
6427 || (aConnectionType > StorageBus_USB))
6428 return setError(E_INVALIDARG,
6429 tr("Invalid connection type: %d"),
6430 aConnectionType);
6431
6432 AutoCaller autoCaller(this);
6433 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6434
6435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6436
6437 HRESULT rc = checkStateDependency(MutableStateDep);
6438 if (FAILED(rc)) return rc;
6439
6440 /* try to find one with the name first. */
6441 ComObjPtr<StorageController> ctrl;
6442
6443 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6444 if (SUCCEEDED(rc))
6445 return setError(VBOX_E_OBJECT_IN_USE,
6446 tr("Storage controller named '%ls' already exists"),
6447 aName);
6448
6449 ctrl.createObject();
6450
6451 /* get a new instance number for the storage controller */
6452 ULONG ulInstance = 0;
6453 bool fBootable = true;
6454 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6455 it != mStorageControllers->end();
6456 ++it)
6457 {
6458 if ((*it)->i_getStorageBus() == aConnectionType)
6459 {
6460 ULONG ulCurInst = (*it)->i_getInstance();
6461
6462 if (ulCurInst >= ulInstance)
6463 ulInstance = ulCurInst + 1;
6464
6465 /* Only one controller of each type can be marked as bootable. */
6466 if ((*it)->i_getBootable())
6467 fBootable = false;
6468 }
6469 }
6470
6471 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6472 if (FAILED(rc)) return rc;
6473
6474 setModified(IsModified_Storage);
6475 mStorageControllers.backup();
6476 mStorageControllers->push_back(ctrl);
6477
6478 ctrl.queryInterfaceTo(controller);
6479
6480 /* inform the direct session if any */
6481 alock.release();
6482 onStorageControllerChange();
6483
6484 return S_OK;
6485}
6486
6487STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6488 IStorageController **aStorageController)
6489{
6490 CheckComArgStrNotEmptyOrNull(aName);
6491
6492 AutoCaller autoCaller(this);
6493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6494
6495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6496
6497 ComObjPtr<StorageController> ctrl;
6498
6499 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6500 if (SUCCEEDED(rc))
6501 ctrl.queryInterfaceTo(aStorageController);
6502
6503 return rc;
6504}
6505
6506STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6507 IStorageController **aStorageController)
6508{
6509 AutoCaller autoCaller(this);
6510 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6511
6512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6513
6514 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6515 it != mStorageControllers->end();
6516 ++it)
6517 {
6518 if ((*it)->i_getInstance() == aInstance)
6519 {
6520 (*it).queryInterfaceTo(aStorageController);
6521 return S_OK;
6522 }
6523 }
6524
6525 return setError(VBOX_E_OBJECT_NOT_FOUND,
6526 tr("Could not find a storage controller with instance number '%lu'"),
6527 aInstance);
6528}
6529
6530STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6531{
6532 AutoCaller autoCaller(this);
6533 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6534
6535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6536
6537 HRESULT rc = checkStateDependency(MutableStateDep);
6538 if (FAILED(rc)) return rc;
6539
6540 ComObjPtr<StorageController> ctrl;
6541
6542 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6543 if (SUCCEEDED(rc))
6544 {
6545 /* Ensure that only one controller of each type is marked as bootable. */
6546 if (fBootable == TRUE)
6547 {
6548 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6549 it != mStorageControllers->end();
6550 ++it)
6551 {
6552 ComObjPtr<StorageController> aCtrl = (*it);
6553
6554 if ( (aCtrl->i_getName() != Utf8Str(aName))
6555 && aCtrl->i_getBootable() == TRUE
6556 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6557 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6558 {
6559 aCtrl->i_setBootable(FALSE);
6560 break;
6561 }
6562 }
6563 }
6564
6565 if (SUCCEEDED(rc))
6566 {
6567 ctrl->i_setBootable(fBootable);
6568 setModified(IsModified_Storage);
6569 }
6570 }
6571
6572 if (SUCCEEDED(rc))
6573 {
6574 /* inform the direct session if any */
6575 alock.release();
6576 onStorageControllerChange();
6577 }
6578
6579 return rc;
6580}
6581
6582STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6583{
6584 CheckComArgStrNotEmptyOrNull(aName);
6585
6586 AutoCaller autoCaller(this);
6587 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6588
6589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6590
6591 HRESULT rc = checkStateDependency(MutableStateDep);
6592 if (FAILED(rc)) return rc;
6593
6594 ComObjPtr<StorageController> ctrl;
6595 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6596 if (FAILED(rc)) return rc;
6597
6598 {
6599 /* find all attached devices to the appropriate storage controller and detach them all */
6600 // make a temporary list because detachDevice invalidates iterators into
6601 // mMediaData->mAttachments
6602 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6603
6604 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6605 it != llAttachments2.end();
6606 ++it)
6607 {
6608 MediumAttachment *pAttachTemp = *it;
6609
6610 AutoCaller localAutoCaller(pAttachTemp);
6611 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6612
6613 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6614
6615 if (pAttachTemp->i_getControllerName() == aName)
6616 {
6617 rc = detachDevice(pAttachTemp, alock, NULL);
6618 if (FAILED(rc)) return rc;
6619 }
6620 }
6621 }
6622
6623 /* We can remove it now. */
6624 setModified(IsModified_Storage);
6625 mStorageControllers.backup();
6626
6627 ctrl->i_unshare();
6628
6629 mStorageControllers->remove(ctrl);
6630
6631 /* inform the direct session if any */
6632 alock.release();
6633 onStorageControllerChange();
6634
6635 return S_OK;
6636}
6637
6638STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6639 IUSBController **controller)
6640{
6641 if ( (aType <= USBControllerType_Null)
6642 || (aType >= USBControllerType_Last))
6643 return setError(E_INVALIDARG,
6644 tr("Invalid USB controller type: %d"),
6645 aType);
6646
6647 AutoCaller autoCaller(this);
6648 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6649
6650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6651
6652 HRESULT rc = checkStateDependency(MutableStateDep);
6653 if (FAILED(rc)) return rc;
6654
6655 /* try to find one with the same type first. */
6656 ComObjPtr<USBController> ctrl;
6657
6658 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6659 if (SUCCEEDED(rc))
6660 return setError(VBOX_E_OBJECT_IN_USE,
6661 tr("USB controller named '%ls' already exists"),
6662 aName);
6663
6664 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6665 ULONG maxInstances;
6666 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6667 if (FAILED(rc))
6668 return rc;
6669
6670 ULONG cInstances = getUSBControllerCountByType(aType);
6671 if (cInstances >= maxInstances)
6672 return setError(E_INVALIDARG,
6673 tr("Too many USB controllers of this type"));
6674
6675 ctrl.createObject();
6676
6677 rc = ctrl->init(this, aName, aType);
6678 if (FAILED(rc)) return rc;
6679
6680 setModified(IsModified_USB);
6681 mUSBControllers.backup();
6682 mUSBControllers->push_back(ctrl);
6683
6684 ctrl.queryInterfaceTo(controller);
6685
6686 /* inform the direct session if any */
6687 alock.release();
6688 onUSBControllerChange();
6689
6690 return S_OK;
6691}
6692
6693STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6694{
6695 CheckComArgStrNotEmptyOrNull(aName);
6696
6697 AutoCaller autoCaller(this);
6698 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6699
6700 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6701
6702 ComObjPtr<USBController> ctrl;
6703
6704 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6705 if (SUCCEEDED(rc))
6706 ctrl.queryInterfaceTo(aUSBController);
6707
6708 return rc;
6709}
6710
6711STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6712 ULONG *aControllers)
6713{
6714 CheckComArgOutPointerValid(aControllers);
6715
6716 if ( (aType <= USBControllerType_Null)
6717 || (aType >= USBControllerType_Last))
6718 return setError(E_INVALIDARG,
6719 tr("Invalid USB controller type: %d"),
6720 aType);
6721
6722 AutoCaller autoCaller(this);
6723 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6724
6725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6726
6727 ComObjPtr<USBController> ctrl;
6728
6729 *aControllers = getUSBControllerCountByType(aType);
6730
6731 return S_OK;
6732}
6733
6734STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6735{
6736 CheckComArgStrNotEmptyOrNull(aName);
6737
6738 AutoCaller autoCaller(this);
6739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6740
6741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6742
6743 HRESULT rc = checkStateDependency(MutableStateDep);
6744 if (FAILED(rc)) return rc;
6745
6746 ComObjPtr<USBController> ctrl;
6747 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6748 if (FAILED(rc)) return rc;
6749
6750 setModified(IsModified_USB);
6751 mUSBControllers.backup();
6752
6753 ctrl->i_unshare();
6754
6755 mUSBControllers->remove(ctrl);
6756
6757 /* inform the direct session if any */
6758 alock.release();
6759 onUSBControllerChange();
6760
6761 return S_OK;
6762}
6763
6764STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6765 ULONG *puOriginX,
6766 ULONG *puOriginY,
6767 ULONG *puWidth,
6768 ULONG *puHeight,
6769 BOOL *pfEnabled)
6770{
6771 LogFlowThisFunc(("\n"));
6772
6773 CheckComArgNotNull(puOriginX);
6774 CheckComArgNotNull(puOriginY);
6775 CheckComArgNotNull(puWidth);
6776 CheckComArgNotNull(puHeight);
6777 CheckComArgNotNull(pfEnabled);
6778
6779 uint32_t u32OriginX= 0;
6780 uint32_t u32OriginY= 0;
6781 uint32_t u32Width = 0;
6782 uint32_t u32Height = 0;
6783 uint16_t u16Flags = 0;
6784
6785 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6786 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6787 if (RT_FAILURE(vrc))
6788 {
6789#ifdef RT_OS_WINDOWS
6790 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6791 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6792 * So just assign fEnable to TRUE again.
6793 * The right fix would be to change GUI API wrappers to make sure that parameters
6794 * are changed only if API succeeds.
6795 */
6796 *pfEnabled = TRUE;
6797#endif
6798 return setError(VBOX_E_IPRT_ERROR,
6799 tr("Saved guest size is not available (%Rrc)"),
6800 vrc);
6801 }
6802
6803 *puOriginX = u32OriginX;
6804 *puOriginY = u32OriginY;
6805 *puWidth = u32Width;
6806 *puHeight = u32Height;
6807 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6808
6809 return S_OK;
6810}
6811
6812STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6813{
6814 LogFlowThisFunc(("\n"));
6815
6816 CheckComArgNotNull(aSize);
6817 CheckComArgNotNull(aWidth);
6818 CheckComArgNotNull(aHeight);
6819
6820 if (aScreenId != 0)
6821 return E_NOTIMPL;
6822
6823 AutoCaller autoCaller(this);
6824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6825
6826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6827
6828 uint8_t *pu8Data = NULL;
6829 uint32_t cbData = 0;
6830 uint32_t u32Width = 0;
6831 uint32_t u32Height = 0;
6832
6833 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6834
6835 if (RT_FAILURE(vrc))
6836 return setError(VBOX_E_IPRT_ERROR,
6837 tr("Saved screenshot data is not available (%Rrc)"),
6838 vrc);
6839
6840 *aSize = cbData;
6841 *aWidth = u32Width;
6842 *aHeight = u32Height;
6843
6844 freeSavedDisplayScreenshot(pu8Data);
6845
6846 return S_OK;
6847}
6848
6849STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6850{
6851 LogFlowThisFunc(("\n"));
6852
6853 CheckComArgNotNull(aWidth);
6854 CheckComArgNotNull(aHeight);
6855 CheckComArgOutSafeArrayPointerValid(aData);
6856
6857 if (aScreenId != 0)
6858 return E_NOTIMPL;
6859
6860 AutoCaller autoCaller(this);
6861 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6862
6863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6864
6865 uint8_t *pu8Data = NULL;
6866 uint32_t cbData = 0;
6867 uint32_t u32Width = 0;
6868 uint32_t u32Height = 0;
6869
6870 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6871
6872 if (RT_FAILURE(vrc))
6873 return setError(VBOX_E_IPRT_ERROR,
6874 tr("Saved screenshot data is not available (%Rrc)"),
6875 vrc);
6876
6877 *aWidth = u32Width;
6878 *aHeight = u32Height;
6879
6880 com::SafeArray<BYTE> bitmap(cbData);
6881 /* Convert pixels to format expected by the API caller. */
6882 if (aBGR)
6883 {
6884 /* [0] B, [1] G, [2] R, [3] A. */
6885 for (unsigned i = 0; i < cbData; i += 4)
6886 {
6887 bitmap[i] = pu8Data[i];
6888 bitmap[i + 1] = pu8Data[i + 1];
6889 bitmap[i + 2] = pu8Data[i + 2];
6890 bitmap[i + 3] = 0xff;
6891 }
6892 }
6893 else
6894 {
6895 /* [0] R, [1] G, [2] B, [3] A. */
6896 for (unsigned i = 0; i < cbData; i += 4)
6897 {
6898 bitmap[i] = pu8Data[i + 2];
6899 bitmap[i + 1] = pu8Data[i + 1];
6900 bitmap[i + 2] = pu8Data[i];
6901 bitmap[i + 3] = 0xff;
6902 }
6903 }
6904 bitmap.detachTo(ComSafeArrayOutArg(aData));
6905
6906 freeSavedDisplayScreenshot(pu8Data);
6907
6908 return S_OK;
6909}
6910
6911
6912STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6913{
6914 LogFlowThisFunc(("\n"));
6915
6916 CheckComArgNotNull(aWidth);
6917 CheckComArgNotNull(aHeight);
6918 CheckComArgOutSafeArrayPointerValid(aData);
6919
6920 if (aScreenId != 0)
6921 return E_NOTIMPL;
6922
6923 AutoCaller autoCaller(this);
6924 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6925
6926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6927
6928 uint8_t *pu8Data = NULL;
6929 uint32_t cbData = 0;
6930 uint32_t u32Width = 0;
6931 uint32_t u32Height = 0;
6932
6933 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6934
6935 if (RT_FAILURE(vrc))
6936 return setError(VBOX_E_IPRT_ERROR,
6937 tr("Saved screenshot data is not available (%Rrc)"),
6938 vrc);
6939
6940 *aWidth = u32Width;
6941 *aHeight = u32Height;
6942
6943 HRESULT rc = S_OK;
6944 uint8_t *pu8PNG = NULL;
6945 uint32_t cbPNG = 0;
6946 uint32_t cxPNG = 0;
6947 uint32_t cyPNG = 0;
6948
6949 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6950
6951 if (RT_SUCCESS(vrc))
6952 {
6953 com::SafeArray<BYTE> screenData(cbPNG);
6954 screenData.initFrom(pu8PNG, cbPNG);
6955 if (pu8PNG)
6956 RTMemFree(pu8PNG);
6957 screenData.detachTo(ComSafeArrayOutArg(aData));
6958 }
6959 else
6960 {
6961 if (pu8PNG)
6962 RTMemFree(pu8PNG);
6963 return setError(VBOX_E_IPRT_ERROR,
6964 tr("Could not convert screenshot to PNG (%Rrc)"),
6965 vrc);
6966 }
6967
6968 freeSavedDisplayScreenshot(pu8Data);
6969
6970 return rc;
6971}
6972
6973STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6974{
6975 LogFlowThisFunc(("\n"));
6976
6977 CheckComArgNotNull(aSize);
6978 CheckComArgNotNull(aWidth);
6979 CheckComArgNotNull(aHeight);
6980
6981 if (aScreenId != 0)
6982 return E_NOTIMPL;
6983
6984 AutoCaller autoCaller(this);
6985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6986
6987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6988
6989 uint8_t *pu8Data = NULL;
6990 uint32_t cbData = 0;
6991 uint32_t u32Width = 0;
6992 uint32_t u32Height = 0;
6993
6994 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6995
6996 if (RT_FAILURE(vrc))
6997 return setError(VBOX_E_IPRT_ERROR,
6998 tr("Saved screenshot data is not available (%Rrc)"),
6999 vrc);
7000
7001 *aSize = cbData;
7002 *aWidth = u32Width;
7003 *aHeight = u32Height;
7004
7005 freeSavedDisplayScreenshot(pu8Data);
7006
7007 return S_OK;
7008}
7009
7010STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
7011{
7012 LogFlowThisFunc(("\n"));
7013
7014 CheckComArgNotNull(aWidth);
7015 CheckComArgNotNull(aHeight);
7016 CheckComArgOutSafeArrayPointerValid(aData);
7017
7018 if (aScreenId != 0)
7019 return E_NOTIMPL;
7020
7021 AutoCaller autoCaller(this);
7022 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7023
7024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7025
7026 uint8_t *pu8Data = NULL;
7027 uint32_t cbData = 0;
7028 uint32_t u32Width = 0;
7029 uint32_t u32Height = 0;
7030
7031 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
7032
7033 if (RT_FAILURE(vrc))
7034 return setError(VBOX_E_IPRT_ERROR,
7035 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
7036 vrc);
7037
7038 *aWidth = u32Width;
7039 *aHeight = u32Height;
7040
7041 com::SafeArray<BYTE> png(cbData);
7042 png.initFrom(pu8Data, cbData);
7043 png.detachTo(ComSafeArrayOutArg(aData));
7044
7045 freeSavedDisplayScreenshot(pu8Data);
7046
7047 return S_OK;
7048}
7049
7050STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
7051{
7052 HRESULT rc = S_OK;
7053 LogFlowThisFunc(("\n"));
7054
7055 AutoCaller autoCaller(this);
7056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7057
7058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7059
7060 if (!mHWData->mCPUHotPlugEnabled)
7061 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7062
7063 if (aCpu >= mHWData->mCPUCount)
7064 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
7065
7066 if (mHWData->mCPUAttached[aCpu])
7067 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
7068
7069 alock.release();
7070 rc = onCPUChange(aCpu, false);
7071 alock.acquire();
7072 if (FAILED(rc)) return rc;
7073
7074 setModified(IsModified_MachineData);
7075 mHWData.backup();
7076 mHWData->mCPUAttached[aCpu] = true;
7077
7078 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7079 if (Global::IsOnline(mData->mMachineState))
7080 saveSettings(NULL);
7081
7082 return S_OK;
7083}
7084
7085STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
7086{
7087 HRESULT rc = S_OK;
7088 LogFlowThisFunc(("\n"));
7089
7090 AutoCaller autoCaller(this);
7091 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7092
7093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7094
7095 if (!mHWData->mCPUHotPlugEnabled)
7096 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7097
7098 if (aCpu >= SchemaDefs::MaxCPUCount)
7099 return setError(E_INVALIDARG,
7100 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7101 SchemaDefs::MaxCPUCount);
7102
7103 if (!mHWData->mCPUAttached[aCpu])
7104 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7105
7106 /* CPU 0 can't be detached */
7107 if (aCpu == 0)
7108 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7109
7110 alock.release();
7111 rc = onCPUChange(aCpu, true);
7112 alock.acquire();
7113 if (FAILED(rc)) return rc;
7114
7115 setModified(IsModified_MachineData);
7116 mHWData.backup();
7117 mHWData->mCPUAttached[aCpu] = false;
7118
7119 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7120 if (Global::IsOnline(mData->mMachineState))
7121 saveSettings(NULL);
7122
7123 return S_OK;
7124}
7125
7126STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7127{
7128 LogFlowThisFunc(("\n"));
7129
7130 CheckComArgNotNull(aCpuAttached);
7131
7132 *aCpuAttached = false;
7133
7134 AutoCaller autoCaller(this);
7135 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7136
7137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7138
7139 /* If hotplug is enabled the CPU is always enabled. */
7140 if (!mHWData->mCPUHotPlugEnabled)
7141 {
7142 if (aCpu < mHWData->mCPUCount)
7143 *aCpuAttached = true;
7144 }
7145 else
7146 {
7147 if (aCpu < SchemaDefs::MaxCPUCount)
7148 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7149 }
7150
7151 return S_OK;
7152}
7153
7154STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7155{
7156 CheckComArgOutPointerValid(aName);
7157
7158 AutoCaller autoCaller(this);
7159 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7160
7161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7162
7163 Utf8Str log = queryLogFilename(aIdx);
7164 if (!RTFileExists(log.c_str()))
7165 log.setNull();
7166 log.cloneTo(aName);
7167
7168 return S_OK;
7169}
7170
7171STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7172{
7173 LogFlowThisFunc(("\n"));
7174 CheckComArgOutSafeArrayPointerValid(aData);
7175 if (aSize < 0)
7176 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7177
7178 AutoCaller autoCaller(this);
7179 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7180
7181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7182
7183 HRESULT rc = S_OK;
7184 Utf8Str log = queryLogFilename(aIdx);
7185
7186 /* do not unnecessarily hold the lock while doing something which does
7187 * not need the lock and potentially takes a long time. */
7188 alock.release();
7189
7190 /* Limit the chunk size to 32K for now, as that gives better performance
7191 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7192 * One byte expands to approx. 25 bytes of breathtaking XML. */
7193 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7194 com::SafeArray<BYTE> logData(cbData);
7195
7196 RTFILE LogFile;
7197 int vrc = RTFileOpen(&LogFile, log.c_str(),
7198 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7199 if (RT_SUCCESS(vrc))
7200 {
7201 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7202 if (RT_SUCCESS(vrc))
7203 logData.resize(cbData);
7204 else
7205 rc = setError(VBOX_E_IPRT_ERROR,
7206 tr("Could not read log file '%s' (%Rrc)"),
7207 log.c_str(), vrc);
7208 RTFileClose(LogFile);
7209 }
7210 else
7211 rc = setError(VBOX_E_IPRT_ERROR,
7212 tr("Could not open log file '%s' (%Rrc)"),
7213 log.c_str(), vrc);
7214
7215 if (FAILED(rc))
7216 logData.resize(0);
7217 logData.detachTo(ComSafeArrayOutArg(aData));
7218
7219 return rc;
7220}
7221
7222
7223/**
7224 * Currently this method doesn't attach device to the running VM,
7225 * just makes sure it's plugged on next VM start.
7226 */
7227STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7228{
7229 AutoCaller autoCaller(this);
7230 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7231
7232 // lock scope
7233 {
7234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7235
7236 HRESULT rc = checkStateDependency(MutableStateDep);
7237 if (FAILED(rc)) return rc;
7238
7239 ChipsetType_T aChipset = ChipsetType_PIIX3;
7240 COMGETTER(ChipsetType)(&aChipset);
7241
7242 if (aChipset != ChipsetType_ICH9)
7243 {
7244 return setError(E_INVALIDARG,
7245 tr("Host PCI attachment only supported with ICH9 chipset"));
7246 }
7247
7248 // check if device with this host PCI address already attached
7249 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7250 it != mHWData->mPCIDeviceAssignments.end();
7251 ++it)
7252 {
7253 LONG iHostAddress = -1;
7254 ComPtr<PCIDeviceAttachment> pAttach;
7255 pAttach = *it;
7256 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7257 if (iHostAddress == hostAddress)
7258 return setError(E_INVALIDARG,
7259 tr("Device with host PCI address already attached to this VM"));
7260 }
7261
7262 ComObjPtr<PCIDeviceAttachment> pda;
7263 char name[32];
7264
7265 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7266 Bstr bname(name);
7267 pda.createObject();
7268 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7269 setModified(IsModified_MachineData);
7270 mHWData.backup();
7271 mHWData->mPCIDeviceAssignments.push_back(pda);
7272 }
7273
7274 return S_OK;
7275}
7276
7277/**
7278 * Currently this method doesn't detach device from the running VM,
7279 * just makes sure it's not plugged on next VM start.
7280 */
7281STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7282{
7283 AutoCaller autoCaller(this);
7284 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7285
7286 ComObjPtr<PCIDeviceAttachment> pAttach;
7287 bool fRemoved = false;
7288 HRESULT rc;
7289
7290 // lock scope
7291 {
7292 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7293
7294 rc = checkStateDependency(MutableStateDep);
7295 if (FAILED(rc)) return rc;
7296
7297 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7298 it != mHWData->mPCIDeviceAssignments.end();
7299 ++it)
7300 {
7301 LONG iHostAddress = -1;
7302 pAttach = *it;
7303 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7304 if (iHostAddress != -1 && iHostAddress == hostAddress)
7305 {
7306 setModified(IsModified_MachineData);
7307 mHWData.backup();
7308 mHWData->mPCIDeviceAssignments.remove(pAttach);
7309 fRemoved = true;
7310 break;
7311 }
7312 }
7313 }
7314
7315
7316 /* Fire event outside of the lock */
7317 if (fRemoved)
7318 {
7319 Assert(!pAttach.isNull());
7320 ComPtr<IEventSource> es;
7321 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7322 Assert(SUCCEEDED(rc));
7323 Bstr mid;
7324 rc = this->COMGETTER(Id)(mid.asOutParam());
7325 Assert(SUCCEEDED(rc));
7326 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7327 }
7328
7329 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7330 tr("No host PCI device %08x attached"),
7331 hostAddress
7332 );
7333}
7334
7335STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7336{
7337 CheckComArgOutSafeArrayPointerValid(aAssignments);
7338
7339 AutoCaller autoCaller(this);
7340 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7341
7342 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7343
7344 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7345 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7346
7347 return S_OK;
7348}
7349
7350STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7351{
7352 CheckComArgOutPointerValid(aBandwidthControl);
7353
7354 AutoCaller autoCaller(this);
7355 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7356
7357 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7358
7359 return S_OK;
7360}
7361
7362STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7363{
7364 CheckComArgOutPointerValid(pfEnabled);
7365 AutoCaller autoCaller(this);
7366 HRESULT hrc = autoCaller.rc();
7367 if (SUCCEEDED(hrc))
7368 {
7369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7370 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7371 }
7372 return hrc;
7373}
7374
7375STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7376{
7377 AutoCaller autoCaller(this);
7378 HRESULT hrc = autoCaller.rc();
7379 if (SUCCEEDED(hrc))
7380 {
7381 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7382 hrc = checkStateDependency(MutableStateDep);
7383 if (SUCCEEDED(hrc))
7384 {
7385 hrc = mHWData.backupEx();
7386 if (SUCCEEDED(hrc))
7387 {
7388 setModified(IsModified_MachineData);
7389 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7390 }
7391 }
7392 }
7393 return hrc;
7394}
7395
7396STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7397{
7398 CheckComArgOutPointerValid(pbstrConfig);
7399 AutoCaller autoCaller(this);
7400 HRESULT hrc = autoCaller.rc();
7401 if (SUCCEEDED(hrc))
7402 {
7403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7404 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7405 }
7406 return hrc;
7407}
7408
7409STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7410{
7411 CheckComArgStr(bstrConfig);
7412 AutoCaller autoCaller(this);
7413 HRESULT hrc = autoCaller.rc();
7414 if (SUCCEEDED(hrc))
7415 {
7416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7417 hrc = checkStateDependency(MutableStateDep);
7418 if (SUCCEEDED(hrc))
7419 {
7420 hrc = mHWData.backupEx();
7421 if (SUCCEEDED(hrc))
7422 {
7423 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7424 if (SUCCEEDED(hrc))
7425 setModified(IsModified_MachineData);
7426 }
7427 }
7428 }
7429 return hrc;
7430
7431}
7432
7433STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7434{
7435 CheckComArgOutPointerValid(pfAllow);
7436 AutoCaller autoCaller(this);
7437 HRESULT hrc = autoCaller.rc();
7438 if (SUCCEEDED(hrc))
7439 {
7440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7441 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7442 }
7443 return hrc;
7444}
7445
7446STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7447{
7448 AutoCaller autoCaller(this);
7449 HRESULT hrc = autoCaller.rc();
7450 if (SUCCEEDED(hrc))
7451 {
7452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7453 hrc = checkStateDependency(MutableStateDep);
7454 if (SUCCEEDED(hrc))
7455 {
7456 hrc = mHWData.backupEx();
7457 if (SUCCEEDED(hrc))
7458 {
7459 setModified(IsModified_MachineData);
7460 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7461 }
7462 }
7463 }
7464 return hrc;
7465}
7466
7467STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7468{
7469 CheckComArgOutPointerValid(pfEnabled);
7470 AutoCaller autoCaller(this);
7471 HRESULT hrc = autoCaller.rc();
7472 if (SUCCEEDED(hrc))
7473 {
7474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7475 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7476 }
7477 return hrc;
7478}
7479
7480STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7481{
7482 AutoCaller autoCaller(this);
7483 HRESULT hrc = autoCaller.rc();
7484 if (SUCCEEDED(hrc))
7485 {
7486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7487 hrc = checkStateDependency(MutableStateDep);
7488 if ( SUCCEEDED(hrc)
7489 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7490 {
7491 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7492 int vrc;
7493
7494 if (fEnabled)
7495 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7496 else
7497 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7498
7499 if (RT_SUCCESS(vrc))
7500 {
7501 hrc = mHWData.backupEx();
7502 if (SUCCEEDED(hrc))
7503 {
7504 setModified(IsModified_MachineData);
7505 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7506 }
7507 }
7508 else if (vrc == VERR_NOT_SUPPORTED)
7509 hrc = setError(VBOX_E_NOT_SUPPORTED,
7510 tr("The VM autostart feature is not supported on this platform"));
7511 else if (vrc == VERR_PATH_NOT_FOUND)
7512 hrc = setError(E_FAIL,
7513 tr("The path to the autostart database is not set"));
7514 else
7515 hrc = setError(E_UNEXPECTED,
7516 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7517 fEnabled ? "Adding" : "Removing",
7518 mUserData->s.strName.c_str(), vrc);
7519 }
7520 }
7521 return hrc;
7522}
7523
7524STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7525{
7526 CheckComArgOutPointerValid(puDelay);
7527 AutoCaller autoCaller(this);
7528 HRESULT hrc = autoCaller.rc();
7529 if (SUCCEEDED(hrc))
7530 {
7531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7532 *puDelay = mHWData->mAutostart.uAutostartDelay;
7533 }
7534 return hrc;
7535}
7536
7537STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7538{
7539 AutoCaller autoCaller(this);
7540 HRESULT hrc = autoCaller.rc();
7541 if (SUCCEEDED(hrc))
7542 {
7543 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7544 hrc = checkStateDependency(MutableStateDep);
7545 if (SUCCEEDED(hrc))
7546 {
7547 hrc = mHWData.backupEx();
7548 if (SUCCEEDED(hrc))
7549 {
7550 setModified(IsModified_MachineData);
7551 mHWData->mAutostart.uAutostartDelay = uDelay;
7552 }
7553 }
7554 }
7555 return hrc;
7556}
7557
7558STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7559{
7560 CheckComArgOutPointerValid(penmAutostopType);
7561 AutoCaller autoCaller(this);
7562 HRESULT hrc = autoCaller.rc();
7563 if (SUCCEEDED(hrc))
7564 {
7565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7566 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7567 }
7568 return hrc;
7569}
7570
7571STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7572{
7573 AutoCaller autoCaller(this);
7574 HRESULT hrc = autoCaller.rc();
7575 if (SUCCEEDED(hrc))
7576 {
7577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7578 hrc = checkStateDependency(MutableStateDep);
7579 if ( SUCCEEDED(hrc)
7580 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7581 {
7582 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7583 int vrc;
7584
7585 if (enmAutostopType != AutostopType_Disabled)
7586 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7587 else
7588 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7589
7590 if (RT_SUCCESS(vrc))
7591 {
7592 hrc = mHWData.backupEx();
7593 if (SUCCEEDED(hrc))
7594 {
7595 setModified(IsModified_MachineData);
7596 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7597 }
7598 }
7599 else if (vrc == VERR_NOT_SUPPORTED)
7600 hrc = setError(VBOX_E_NOT_SUPPORTED,
7601 tr("The VM autostop feature is not supported on this platform"));
7602 else if (vrc == VERR_PATH_NOT_FOUND)
7603 hrc = setError(E_FAIL,
7604 tr("The path to the autostart database is not set"));
7605 else
7606 hrc = setError(E_UNEXPECTED,
7607 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7608 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7609 mUserData->s.strName.c_str(), vrc);
7610 }
7611 }
7612 return hrc;
7613}
7614
7615STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7616{
7617 CheckComArgOutPointerValid(aDefaultFrontend);
7618 AutoCaller autoCaller(this);
7619 HRESULT hrc = autoCaller.rc();
7620 if (SUCCEEDED(hrc))
7621 {
7622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7623 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7624 }
7625 return hrc;
7626}
7627
7628STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7629{
7630 CheckComArgStr(aDefaultFrontend);
7631 AutoCaller autoCaller(this);
7632 HRESULT hrc = autoCaller.rc();
7633 if (SUCCEEDED(hrc))
7634 {
7635 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7636 hrc = checkStateDependency(MutableOrSavedStateDep);
7637 if (SUCCEEDED(hrc))
7638 {
7639 hrc = mHWData.backupEx();
7640 if (SUCCEEDED(hrc))
7641 {
7642 setModified(IsModified_MachineData);
7643 mHWData->mDefaultFrontend = aDefaultFrontend;
7644 }
7645 }
7646 }
7647 return hrc;
7648}
7649
7650STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7651{
7652 CheckComArgSafeArrayNotNull(aIcon);
7653 CheckComArgOutSafeArrayPointerValid(aIcon);
7654 AutoCaller autoCaller(this);
7655 HRESULT hrc = autoCaller.rc();
7656 if (SUCCEEDED(hrc))
7657 {
7658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7659 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7660 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7661 icon.detachTo(ComSafeArrayOutArg(aIcon));
7662 }
7663 return hrc;
7664}
7665
7666STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7667{
7668 CheckComArgSafeArrayNotNull(aIcon);
7669 AutoCaller autoCaller(this);
7670 HRESULT hrc = autoCaller.rc();
7671 if (SUCCEEDED(hrc))
7672 {
7673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7674 hrc = checkStateDependency(MutableOrSavedStateDep);
7675 if (SUCCEEDED(hrc))
7676 {
7677 setModified(IsModified_MachineData);
7678 mUserData.backup();
7679 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7680 mUserData->mIcon.resize(icon.size());
7681 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7682 }
7683 }
7684 return hrc;
7685}
7686
7687STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7688{
7689 CheckComArgOutPointerValid(aAvailable);
7690
7691 AutoCaller autoCaller(this);
7692 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7693
7694#ifdef VBOX_WITH_USB
7695 *aAvailable = true;
7696#else
7697 *aAvailable = false;
7698#endif
7699 return S_OK;
7700}
7701
7702STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7703{
7704 LogFlowFuncEnter();
7705
7706 CheckComArgNotNull(pTarget);
7707 CheckComArgOutPointerValid(pProgress);
7708
7709 /* Convert the options. */
7710 RTCList<CloneOptions_T> optList;
7711 if (options != NULL)
7712 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7713
7714 if (optList.contains(CloneOptions_Link))
7715 {
7716 if (!isSnapshotMachine())
7717 return setError(E_INVALIDARG,
7718 tr("Linked clone can only be created from a snapshot"));
7719 if (mode != CloneMode_MachineState)
7720 return setError(E_INVALIDARG,
7721 tr("Linked clone can only be created for a single machine state"));
7722 }
7723 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7724
7725 AutoCaller autoCaller(this);
7726 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7727
7728
7729 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7730
7731 HRESULT rc = pWorker->start(pProgress);
7732
7733 LogFlowFuncLeave();
7734
7735 return rc;
7736}
7737
7738// public methods for internal purposes
7739/////////////////////////////////////////////////////////////////////////////
7740
7741/**
7742 * Adds the given IsModified_* flag to the dirty flags of the machine.
7743 * This must be called either during loadSettings or under the machine write lock.
7744 * @param fl
7745 */
7746void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7747{
7748 mData->flModifications |= fl;
7749 if (fAllowStateModification && isStateModificationAllowed())
7750 mData->mCurrentStateModified = true;
7751}
7752
7753/**
7754 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7755 * care of the write locking.
7756 *
7757 * @param fModifications The flag to add.
7758 */
7759void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7760{
7761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7762 setModified(fModification, fAllowStateModification);
7763}
7764
7765/**
7766 * Saves the registry entry of this machine to the given configuration node.
7767 *
7768 * @param aEntryNode Node to save the registry entry to.
7769 *
7770 * @note locks this object for reading.
7771 */
7772HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7773{
7774 AutoLimitedCaller autoCaller(this);
7775 AssertComRCReturnRC(autoCaller.rc());
7776
7777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7778
7779 data.uuid = mData->mUuid;
7780 data.strSettingsFile = mData->m_strConfigFile;
7781
7782 return S_OK;
7783}
7784
7785/**
7786 * Calculates the absolute path of the given path taking the directory of the
7787 * machine settings file as the current directory.
7788 *
7789 * @param aPath Path to calculate the absolute path for.
7790 * @param aResult Where to put the result (used only on success, can be the
7791 * same Utf8Str instance as passed in @a aPath).
7792 * @return IPRT result.
7793 *
7794 * @note Locks this object for reading.
7795 */
7796int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7797{
7798 AutoCaller autoCaller(this);
7799 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7800
7801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7802
7803 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7804
7805 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7806
7807 strSettingsDir.stripFilename();
7808 char folder[RTPATH_MAX];
7809 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7810 if (RT_SUCCESS(vrc))
7811 aResult = folder;
7812
7813 return vrc;
7814}
7815
7816/**
7817 * Copies strSource to strTarget, making it relative to the machine folder
7818 * if it is a subdirectory thereof, or simply copying it otherwise.
7819 *
7820 * @param strSource Path to evaluate and copy.
7821 * @param strTarget Buffer to receive target path.
7822 *
7823 * @note Locks this object for reading.
7824 */
7825void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7826 Utf8Str &strTarget)
7827{
7828 AutoCaller autoCaller(this);
7829 AssertComRCReturn(autoCaller.rc(), (void)0);
7830
7831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7832
7833 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7834 // use strTarget as a temporary buffer to hold the machine settings dir
7835 strTarget = mData->m_strConfigFileFull;
7836 strTarget.stripFilename();
7837 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7838 {
7839 // is relative: then append what's left
7840 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7841 // for empty paths (only possible for subdirs) use "." to avoid
7842 // triggering default settings for not present config attributes.
7843 if (strTarget.isEmpty())
7844 strTarget = ".";
7845 }
7846 else
7847 // is not relative: then overwrite
7848 strTarget = strSource;
7849}
7850
7851/**
7852 * Returns the full path to the machine's log folder in the
7853 * \a aLogFolder argument.
7854 */
7855void Machine::getLogFolder(Utf8Str &aLogFolder)
7856{
7857 AutoCaller autoCaller(this);
7858 AssertComRCReturnVoid(autoCaller.rc());
7859
7860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7861
7862 char szTmp[RTPATH_MAX];
7863 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7864 if (RT_SUCCESS(vrc))
7865 {
7866 if (szTmp[0] && !mUserData.isNull())
7867 {
7868 char szTmp2[RTPATH_MAX];
7869 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7870 if (RT_SUCCESS(vrc))
7871 aLogFolder = BstrFmt("%s%c%s",
7872 szTmp2,
7873 RTPATH_DELIMITER,
7874 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7875 }
7876 else
7877 vrc = VERR_PATH_IS_RELATIVE;
7878 }
7879
7880 if (RT_FAILURE(vrc))
7881 {
7882 // fallback if VBOX_USER_LOGHOME is not set or invalid
7883 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7884 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7885 aLogFolder.append(RTPATH_DELIMITER);
7886 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7887 }
7888}
7889
7890/**
7891 * Returns the full path to the machine's log file for an given index.
7892 */
7893Utf8Str Machine::queryLogFilename(ULONG idx)
7894{
7895 Utf8Str logFolder;
7896 getLogFolder(logFolder);
7897 Assert(logFolder.length());
7898 Utf8Str log;
7899 if (idx == 0)
7900 log = Utf8StrFmt("%s%cVBox.log",
7901 logFolder.c_str(), RTPATH_DELIMITER);
7902 else
7903 log = Utf8StrFmt("%s%cVBox.log.%d",
7904 logFolder.c_str(), RTPATH_DELIMITER, idx);
7905 return log;
7906}
7907
7908/**
7909 * Composes a unique saved state filename based on the current system time. The filename is
7910 * granular to the second so this will work so long as no more than one snapshot is taken on
7911 * a machine per second.
7912 *
7913 * Before version 4.1, we used this formula for saved state files:
7914 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7915 * which no longer works because saved state files can now be shared between the saved state of the
7916 * "saved" machine and an online snapshot, and the following would cause problems:
7917 * 1) save machine
7918 * 2) create online snapshot from that machine state --> reusing saved state file
7919 * 3) save machine again --> filename would be reused, breaking the online snapshot
7920 *
7921 * So instead we now use a timestamp.
7922 *
7923 * @param str
7924 */
7925void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7926{
7927 AutoCaller autoCaller(this);
7928 AssertComRCReturnVoid(autoCaller.rc());
7929
7930 {
7931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7932 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7933 }
7934
7935 RTTIMESPEC ts;
7936 RTTimeNow(&ts);
7937 RTTIME time;
7938 RTTimeExplode(&time, &ts);
7939
7940 strStateFilePath += RTPATH_DELIMITER;
7941 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7942 time.i32Year, time.u8Month, time.u8MonthDay,
7943 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7944}
7945
7946/**
7947 * Returns the full path to the default video capture file.
7948 */
7949void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7950{
7951 AutoCaller autoCaller(this);
7952 AssertComRCReturnVoid(autoCaller.rc());
7953
7954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7955
7956 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7957 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7958 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7959}
7960
7961/**
7962 * Returns whether at least one USB controller is present for the VM.
7963 */
7964bool Machine::isUSBControllerPresent()
7965{
7966 AutoCaller autoCaller(this);
7967 AssertComRCReturn(autoCaller.rc(), false);
7968
7969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7970
7971 return (mUSBControllers->size() > 0);
7972}
7973
7974/**
7975 * @note Locks this object for writing, calls the client process
7976 * (inside the lock).
7977 */
7978HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7979 const Utf8Str &strFrontend,
7980 const Utf8Str &strEnvironment,
7981 ProgressProxy *aProgress)
7982{
7983 LogFlowThisFuncEnter();
7984
7985 AssertReturn(aControl, E_FAIL);
7986 AssertReturn(aProgress, E_FAIL);
7987 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7988
7989 AutoCaller autoCaller(this);
7990 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7991
7992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7993
7994 if (!mData->mRegistered)
7995 return setError(E_UNEXPECTED,
7996 tr("The machine '%s' is not registered"),
7997 mUserData->s.strName.c_str());
7998
7999 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
8000
8001 if ( mData->mSession.mState == SessionState_Locked
8002 || mData->mSession.mState == SessionState_Spawning
8003 || mData->mSession.mState == SessionState_Unlocking)
8004 return setError(VBOX_E_INVALID_OBJECT_STATE,
8005 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
8006 mUserData->s.strName.c_str());
8007
8008 /* may not be busy */
8009 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
8010
8011 /* get the path to the executable */
8012 char szPath[RTPATH_MAX];
8013 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
8014 size_t sz = strlen(szPath);
8015 szPath[sz++] = RTPATH_DELIMITER;
8016 szPath[sz] = 0;
8017 char *cmd = szPath + sz;
8018 sz = sizeof(szPath) - sz;
8019
8020 int vrc = VINF_SUCCESS;
8021 RTPROCESS pid = NIL_RTPROCESS;
8022
8023 RTENV env = RTENV_DEFAULT;
8024
8025 if (!strEnvironment.isEmpty())
8026 {
8027 char *newEnvStr = NULL;
8028
8029 do
8030 {
8031 /* clone the current environment */
8032 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
8033 AssertRCBreakStmt(vrc2, vrc = vrc2);
8034
8035 newEnvStr = RTStrDup(strEnvironment.c_str());
8036 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
8037
8038 /* put new variables to the environment
8039 * (ignore empty variable names here since RTEnv API
8040 * intentionally doesn't do that) */
8041 char *var = newEnvStr;
8042 for (char *p = newEnvStr; *p; ++p)
8043 {
8044 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
8045 {
8046 *p = '\0';
8047 if (*var)
8048 {
8049 char *val = strchr(var, '=');
8050 if (val)
8051 {
8052 *val++ = '\0';
8053 vrc2 = RTEnvSetEx(env, var, val);
8054 }
8055 else
8056 vrc2 = RTEnvUnsetEx(env, var);
8057 if (RT_FAILURE(vrc2))
8058 break;
8059 }
8060 var = p + 1;
8061 }
8062 }
8063 if (RT_SUCCESS(vrc2) && *var)
8064 vrc2 = RTEnvPutEx(env, var);
8065
8066 AssertRCBreakStmt(vrc2, vrc = vrc2);
8067 }
8068 while (0);
8069
8070 if (newEnvStr != NULL)
8071 RTStrFree(newEnvStr);
8072 }
8073
8074#ifdef VBOX_WITH_QTGUI
8075 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
8076 {
8077# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
8078 /* Modify the base path so that we don't need to use ".." below. */
8079 RTPathStripTrailingSlash(szPath);
8080 RTPathStripFilename(szPath);
8081 sz = strlen(szPath);
8082 cmd = szPath + sz;
8083 sz = sizeof(szPath) - sz;
8084
8085#define OSX_APP_NAME "VirtualBoxVM"
8086#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
8087
8088 Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
8089 if ( strAppOverride.contains(".")
8090 || strAppOverride.contains("/")
8091 || strAppOverride.contains("\\")
8092 || strAppOverride.contains(":"))
8093 strAppOverride.setNull();
8094 Utf8Str strAppPath;
8095 if (!strAppOverride.isEmpty())
8096 {
8097 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
8098 Utf8Str strFullPath(szPath);
8099 strFullPath.append(strAppPath);
8100 /* there is a race, but people using this deserve the failure */
8101 if (!RTFileExists(strFullPath.c_str()))
8102 strAppOverride.setNull();
8103 }
8104 if (strAppOverride.isEmpty())
8105 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
8106 const char *VirtualBox_exe = strAppPath.c_str();
8107 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
8108# else
8109 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8110 Assert(sz >= sizeof(VirtualBox_exe));
8111# endif
8112 strcpy(cmd, VirtualBox_exe);
8113
8114 Utf8Str idStr = mData->mUuid.toString();
8115 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8116 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8117 }
8118#else /* !VBOX_WITH_QTGUI */
8119 if (0)
8120 ;
8121#endif /* VBOX_WITH_QTGUI */
8122
8123 else
8124
8125#ifdef VBOX_WITH_VBOXSDL
8126 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8127 {
8128 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8129 Assert(sz >= sizeof(VBoxSDL_exe));
8130 strcpy(cmd, VBoxSDL_exe);
8131
8132 Utf8Str idStr = mData->mUuid.toString();
8133 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8134 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8135 }
8136#else /* !VBOX_WITH_VBOXSDL */
8137 if (0)
8138 ;
8139#endif /* !VBOX_WITH_VBOXSDL */
8140
8141 else
8142
8143#ifdef VBOX_WITH_HEADLESS
8144 if ( strFrontend == "headless"
8145 || strFrontend == "capture"
8146 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8147 )
8148 {
8149 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8150 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8151 * and a VM works even if the server has not been installed.
8152 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8153 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8154 * differently in 4.0 and 3.x.
8155 */
8156 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8157 Assert(sz >= sizeof(VBoxHeadless_exe));
8158 strcpy(cmd, VBoxHeadless_exe);
8159
8160 Utf8Str idStr = mData->mUuid.toString();
8161 /* Leave space for "--capture" arg. */
8162 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8163 "--startvm", idStr.c_str(),
8164 "--vrde", "config",
8165 0, /* For "--capture". */
8166 0 };
8167 if (strFrontend == "capture")
8168 {
8169 unsigned pos = RT_ELEMENTS(args) - 2;
8170 args[pos] = "--capture";
8171 }
8172 vrc = RTProcCreate(szPath, args, env,
8173#ifdef RT_OS_WINDOWS
8174 RTPROC_FLAGS_NO_WINDOW
8175#else
8176 0
8177#endif
8178 , &pid);
8179 }
8180#else /* !VBOX_WITH_HEADLESS */
8181 if (0)
8182 ;
8183#endif /* !VBOX_WITH_HEADLESS */
8184 else
8185 {
8186 RTEnvDestroy(env);
8187 return setError(E_INVALIDARG,
8188 tr("Invalid frontend name: '%s'"),
8189 strFrontend.c_str());
8190 }
8191
8192 RTEnvDestroy(env);
8193
8194 if (RT_FAILURE(vrc))
8195 return setError(VBOX_E_IPRT_ERROR,
8196 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8197 mUserData->s.strName.c_str(), vrc);
8198
8199 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8200
8201 /*
8202 * Note that we don't release the lock here before calling the client,
8203 * because it doesn't need to call us back if called with a NULL argument.
8204 * Releasing the lock here is dangerous because we didn't prepare the
8205 * launch data yet, but the client we've just started may happen to be
8206 * too fast and call LockMachine() that will fail (because of PID, etc.),
8207 * so that the Machine will never get out of the Spawning session state.
8208 */
8209
8210 /* inform the session that it will be a remote one */
8211 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8212#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8213 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8214#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8215 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8216#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8217 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8218
8219 if (FAILED(rc))
8220 {
8221 /* restore the session state */
8222 mData->mSession.mState = SessionState_Unlocked;
8223 alock.release();
8224 mParent->i_addProcessToReap(pid);
8225 /* The failure may occur w/o any error info (from RPC), so provide one */
8226 return setError(VBOX_E_VM_ERROR,
8227 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8228 }
8229
8230 /* attach launch data to the machine */
8231 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8232 mData->mSession.mRemoteControls.push_back(aControl);
8233 mData->mSession.mProgress = aProgress;
8234 mData->mSession.mPID = pid;
8235 mData->mSession.mState = SessionState_Spawning;
8236 mData->mSession.mType = strFrontend;
8237
8238 alock.release();
8239 mParent->i_addProcessToReap(pid);
8240
8241 LogFlowThisFuncLeave();
8242 return S_OK;
8243}
8244
8245/**
8246 * Returns @c true if the given session machine instance has an open direct
8247 * session (and optionally also for direct sessions which are closing) and
8248 * returns the session control machine instance if so.
8249 *
8250 * Note that when the method returns @c false, the arguments remain unchanged.
8251 *
8252 * @param aMachine Session machine object.
8253 * @param aControl Direct session control object (optional).
8254 * @param aAllowClosing If true then additionally a session which is currently
8255 * being closed will also be allowed.
8256 *
8257 * @note locks this object for reading.
8258 */
8259bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8260 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8261 bool aAllowClosing /*= false*/)
8262{
8263 AutoLimitedCaller autoCaller(this);
8264 AssertComRCReturn(autoCaller.rc(), false);
8265
8266 /* just return false for inaccessible machines */
8267 if (autoCaller.state() != Ready)
8268 return false;
8269
8270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8271
8272 if ( mData->mSession.mState == SessionState_Locked
8273 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8274 )
8275 {
8276 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8277
8278 aMachine = mData->mSession.mMachine;
8279
8280 if (aControl != NULL)
8281 *aControl = mData->mSession.mDirectControl;
8282
8283 return true;
8284 }
8285
8286 return false;
8287}
8288
8289/**
8290 * Returns @c true if the given machine has an spawning direct session.
8291 *
8292 * @note locks this object for reading.
8293 */
8294bool Machine::isSessionSpawning()
8295{
8296 AutoLimitedCaller autoCaller(this);
8297 AssertComRCReturn(autoCaller.rc(), false);
8298
8299 /* just return false for inaccessible machines */
8300 if (autoCaller.state() != Ready)
8301 return false;
8302
8303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8304
8305 if (mData->mSession.mState == SessionState_Spawning)
8306 return true;
8307
8308 return false;
8309}
8310
8311/**
8312 * Called from the client watcher thread to check for unexpected client process
8313 * death during Session_Spawning state (e.g. before it successfully opened a
8314 * direct session).
8315 *
8316 * On Win32 and on OS/2, this method is called only when we've got the
8317 * direct client's process termination notification, so it always returns @c
8318 * true.
8319 *
8320 * On other platforms, this method returns @c true if the client process is
8321 * terminated and @c false if it's still alive.
8322 *
8323 * @note Locks this object for writing.
8324 */
8325bool Machine::checkForSpawnFailure()
8326{
8327 AutoCaller autoCaller(this);
8328 if (!autoCaller.isOk())
8329 {
8330 /* nothing to do */
8331 LogFlowThisFunc(("Already uninitialized!\n"));
8332 return true;
8333 }
8334
8335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8336
8337 if (mData->mSession.mState != SessionState_Spawning)
8338 {
8339 /* nothing to do */
8340 LogFlowThisFunc(("Not spawning any more!\n"));
8341 return true;
8342 }
8343
8344 HRESULT rc = S_OK;
8345
8346 /* PID not yet initialized, skip check. */
8347 if (mData->mSession.mPID == NIL_RTPROCESS)
8348 return false;
8349
8350 RTPROCSTATUS status;
8351 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8352
8353 if (vrc != VERR_PROCESS_RUNNING)
8354 {
8355 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8356 rc = setError(E_FAIL,
8357 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8358 getName().c_str(), status.iStatus);
8359 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8360 rc = setError(E_FAIL,
8361 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8362 getName().c_str(), status.iStatus);
8363 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8364 rc = setError(E_FAIL,
8365 tr("The virtual machine '%s' has terminated abnormally"),
8366 getName().c_str(), status.iStatus);
8367 else
8368 rc = setError(E_FAIL,
8369 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8370 getName().c_str(), vrc);
8371 }
8372
8373 if (FAILED(rc))
8374 {
8375 /* Close the remote session, remove the remote control from the list
8376 * and reset session state to Closed (@note keep the code in sync with
8377 * the relevant part in LockMachine()). */
8378
8379 Assert(mData->mSession.mRemoteControls.size() == 1);
8380 if (mData->mSession.mRemoteControls.size() == 1)
8381 {
8382 ErrorInfoKeeper eik;
8383 mData->mSession.mRemoteControls.front()->Uninitialize();
8384 }
8385
8386 mData->mSession.mRemoteControls.clear();
8387 mData->mSession.mState = SessionState_Unlocked;
8388
8389 /* finalize the progress after setting the state */
8390 if (!mData->mSession.mProgress.isNull())
8391 {
8392 mData->mSession.mProgress->notifyComplete(rc);
8393 mData->mSession.mProgress.setNull();
8394 }
8395
8396 mData->mSession.mPID = NIL_RTPROCESS;
8397
8398 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8399 return true;
8400 }
8401
8402 return false;
8403}
8404
8405/**
8406 * Checks whether the machine can be registered. If so, commits and saves
8407 * all settings.
8408 *
8409 * @note Must be called from mParent's write lock. Locks this object and
8410 * children for writing.
8411 */
8412HRESULT Machine::prepareRegister()
8413{
8414 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8415
8416 AutoLimitedCaller autoCaller(this);
8417 AssertComRCReturnRC(autoCaller.rc());
8418
8419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8420
8421 /* wait for state dependents to drop to zero */
8422 ensureNoStateDependencies();
8423
8424 if (!mData->mAccessible)
8425 return setError(VBOX_E_INVALID_OBJECT_STATE,
8426 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8427 mUserData->s.strName.c_str(),
8428 mData->mUuid.toString().c_str());
8429
8430 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8431
8432 if (mData->mRegistered)
8433 return setError(VBOX_E_INVALID_OBJECT_STATE,
8434 tr("The machine '%s' with UUID {%s} is already registered"),
8435 mUserData->s.strName.c_str(),
8436 mData->mUuid.toString().c_str());
8437
8438 HRESULT rc = S_OK;
8439
8440 // Ensure the settings are saved. If we are going to be registered and
8441 // no config file exists yet, create it by calling saveSettings() too.
8442 if ( (mData->flModifications)
8443 || (!mData->pMachineConfigFile->fileExists())
8444 )
8445 {
8446 rc = saveSettings(NULL);
8447 // no need to check whether VirtualBox.xml needs saving too since
8448 // we can't have a machine XML file rename pending
8449 if (FAILED(rc)) return rc;
8450 }
8451
8452 /* more config checking goes here */
8453
8454 if (SUCCEEDED(rc))
8455 {
8456 /* we may have had implicit modifications we want to fix on success */
8457 commit();
8458
8459 mData->mRegistered = true;
8460 }
8461 else
8462 {
8463 /* we may have had implicit modifications we want to cancel on failure*/
8464 rollback(false /* aNotify */);
8465 }
8466
8467 return rc;
8468}
8469
8470/**
8471 * Increases the number of objects dependent on the machine state or on the
8472 * registered state. Guarantees that these two states will not change at least
8473 * until #releaseStateDependency() is called.
8474 *
8475 * Depending on the @a aDepType value, additional state checks may be made.
8476 * These checks will set extended error info on failure. See
8477 * #checkStateDependency() for more info.
8478 *
8479 * If this method returns a failure, the dependency is not added and the caller
8480 * is not allowed to rely on any particular machine state or registration state
8481 * value and may return the failed result code to the upper level.
8482 *
8483 * @param aDepType Dependency type to add.
8484 * @param aState Current machine state (NULL if not interested).
8485 * @param aRegistered Current registered state (NULL if not interested).
8486 *
8487 * @note Locks this object for writing.
8488 */
8489HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8490 MachineState_T *aState /* = NULL */,
8491 BOOL *aRegistered /* = NULL */)
8492{
8493 AutoCaller autoCaller(this);
8494 AssertComRCReturnRC(autoCaller.rc());
8495
8496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8497
8498 HRESULT rc = checkStateDependency(aDepType);
8499 if (FAILED(rc)) return rc;
8500
8501 {
8502 if (mData->mMachineStateChangePending != 0)
8503 {
8504 /* ensureNoStateDependencies() is waiting for state dependencies to
8505 * drop to zero so don't add more. It may make sense to wait a bit
8506 * and retry before reporting an error (since the pending state
8507 * transition should be really quick) but let's just assert for
8508 * now to see if it ever happens on practice. */
8509
8510 AssertFailed();
8511
8512 return setError(E_ACCESSDENIED,
8513 tr("Machine state change is in progress. Please retry the operation later."));
8514 }
8515
8516 ++mData->mMachineStateDeps;
8517 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8518 }
8519
8520 if (aState)
8521 *aState = mData->mMachineState;
8522 if (aRegistered)
8523 *aRegistered = mData->mRegistered;
8524
8525 return S_OK;
8526}
8527
8528/**
8529 * Decreases the number of objects dependent on the machine state.
8530 * Must always complete the #addStateDependency() call after the state
8531 * dependency is no more necessary.
8532 */
8533void Machine::releaseStateDependency()
8534{
8535 AutoCaller autoCaller(this);
8536 AssertComRCReturnVoid(autoCaller.rc());
8537
8538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8539
8540 /* releaseStateDependency() w/o addStateDependency()? */
8541 AssertReturnVoid(mData->mMachineStateDeps != 0);
8542 -- mData->mMachineStateDeps;
8543
8544 if (mData->mMachineStateDeps == 0)
8545 {
8546 /* inform ensureNoStateDependencies() that there are no more deps */
8547 if (mData->mMachineStateChangePending != 0)
8548 {
8549 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8550 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8551 }
8552 }
8553}
8554
8555Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8556{
8557 /* start with nothing found */
8558 Utf8Str strResult("");
8559
8560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8561
8562 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8563 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8564 // found:
8565 strResult = it->second; // source is a Utf8Str
8566
8567 return strResult;
8568}
8569
8570// protected methods
8571/////////////////////////////////////////////////////////////////////////////
8572
8573/**
8574 * Performs machine state checks based on the @a aDepType value. If a check
8575 * fails, this method will set extended error info, otherwise it will return
8576 * S_OK. It is supposed, that on failure, the caller will immediately return
8577 * the return value of this method to the upper level.
8578 *
8579 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8580 *
8581 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8582 * current state of this machine object allows to change settings of the
8583 * machine (i.e. the machine is not registered, or registered but not running
8584 * and not saved). It is useful to call this method from Machine setters
8585 * before performing any change.
8586 *
8587 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8588 * as for MutableStateDep except that if the machine is saved, S_OK is also
8589 * returned. This is useful in setters which allow changing machine
8590 * properties when it is in the saved state.
8591 *
8592 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8593 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8594 * Aborted).
8595 *
8596 * @param aDepType Dependency type to check.
8597 *
8598 * @note Non Machine based classes should use #addStateDependency() and
8599 * #releaseStateDependency() methods or the smart AutoStateDependency
8600 * template.
8601 *
8602 * @note This method must be called from under this object's read or write
8603 * lock.
8604 */
8605HRESULT Machine::checkStateDependency(StateDependency aDepType)
8606{
8607 switch (aDepType)
8608 {
8609 case AnyStateDep:
8610 {
8611 break;
8612 }
8613 case MutableStateDep:
8614 {
8615 if ( mData->mRegistered
8616 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8617 || ( mData->mMachineState != MachineState_Paused
8618 && mData->mMachineState != MachineState_Running
8619 && mData->mMachineState != MachineState_Aborted
8620 && mData->mMachineState != MachineState_Teleported
8621 && mData->mMachineState != MachineState_PoweredOff
8622 )
8623 )
8624 )
8625 return setError(VBOX_E_INVALID_VM_STATE,
8626 tr("The machine is not mutable (state is %s)"),
8627 Global::stringifyMachineState(mData->mMachineState));
8628 break;
8629 }
8630 case MutableOrSavedStateDep:
8631 {
8632 if ( mData->mRegistered
8633 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8634 || ( mData->mMachineState != MachineState_Paused
8635 && mData->mMachineState != MachineState_Running
8636 && mData->mMachineState != MachineState_Aborted
8637 && mData->mMachineState != MachineState_Teleported
8638 && mData->mMachineState != MachineState_Saved
8639 && mData->mMachineState != MachineState_PoweredOff
8640 )
8641 )
8642 )
8643 return setError(VBOX_E_INVALID_VM_STATE,
8644 tr("The machine is not mutable (state is %s)"),
8645 Global::stringifyMachineState(mData->mMachineState));
8646 break;
8647 }
8648 case OfflineStateDep:
8649 {
8650 if ( mData->mRegistered
8651 && ( !isSessionMachine()
8652 || ( mData->mMachineState != MachineState_PoweredOff
8653 && mData->mMachineState != MachineState_Saved
8654 && mData->mMachineState != MachineState_Aborted
8655 && mData->mMachineState != MachineState_Teleported
8656 )
8657 )
8658 )
8659 return setError(VBOX_E_INVALID_VM_STATE,
8660 tr("The machine is not offline (state is %s)"),
8661 Global::stringifyMachineState(mData->mMachineState));
8662 break;
8663 }
8664 }
8665
8666 return S_OK;
8667}
8668
8669/**
8670 * Helper to initialize all associated child objects and allocate data
8671 * structures.
8672 *
8673 * This method must be called as a part of the object's initialization procedure
8674 * (usually done in the #init() method).
8675 *
8676 * @note Must be called only from #init() or from #registeredInit().
8677 */
8678HRESULT Machine::initDataAndChildObjects()
8679{
8680 AutoCaller autoCaller(this);
8681 AssertComRCReturnRC(autoCaller.rc());
8682 AssertComRCReturn(autoCaller.state() == InInit ||
8683 autoCaller.state() == Limited, E_FAIL);
8684
8685 AssertReturn(!mData->mAccessible, E_FAIL);
8686
8687 /* allocate data structures */
8688 mSSData.allocate();
8689 mUserData.allocate();
8690 mHWData.allocate();
8691 mMediaData.allocate();
8692 mStorageControllers.allocate();
8693 mUSBControllers.allocate();
8694
8695 /* initialize mOSTypeId */
8696 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8697
8698 /* create associated BIOS settings object */
8699 unconst(mBIOSSettings).createObject();
8700 mBIOSSettings->init(this);
8701
8702 /* create an associated VRDE object (default is disabled) */
8703 unconst(mVRDEServer).createObject();
8704 mVRDEServer->init(this);
8705
8706 /* create associated serial port objects */
8707 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8708 {
8709 unconst(mSerialPorts[slot]).createObject();
8710 mSerialPorts[slot]->init(this, slot);
8711 }
8712
8713 /* create associated parallel port objects */
8714 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8715 {
8716 unconst(mParallelPorts[slot]).createObject();
8717 mParallelPorts[slot]->init(this, slot);
8718 }
8719
8720 /* create the audio adapter object (always present, default is disabled) */
8721 unconst(mAudioAdapter).createObject();
8722 mAudioAdapter->init(this);
8723
8724 /* create the USB device filters object (always present) */
8725 unconst(mUSBDeviceFilters).createObject();
8726 mUSBDeviceFilters->init(this);
8727
8728 /* create associated network adapter objects */
8729 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8730 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8731 {
8732 unconst(mNetworkAdapters[slot]).createObject();
8733 mNetworkAdapters[slot]->init(this, slot);
8734 }
8735
8736 /* create the bandwidth control */
8737 unconst(mBandwidthControl).createObject();
8738 mBandwidthControl->init(this);
8739
8740 return S_OK;
8741}
8742
8743/**
8744 * Helper to uninitialize all associated child objects and to free all data
8745 * structures.
8746 *
8747 * This method must be called as a part of the object's uninitialization
8748 * procedure (usually done in the #uninit() method).
8749 *
8750 * @note Must be called only from #uninit() or from #registeredInit().
8751 */
8752void Machine::uninitDataAndChildObjects()
8753{
8754 AutoCaller autoCaller(this);
8755 AssertComRCReturnVoid(autoCaller.rc());
8756 AssertComRCReturnVoid( autoCaller.state() == InUninit
8757 || autoCaller.state() == Limited);
8758
8759 /* tell all our other child objects we've been uninitialized */
8760 if (mBandwidthControl)
8761 {
8762 mBandwidthControl->uninit();
8763 unconst(mBandwidthControl).setNull();
8764 }
8765
8766 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8767 {
8768 if (mNetworkAdapters[slot])
8769 {
8770 mNetworkAdapters[slot]->uninit();
8771 unconst(mNetworkAdapters[slot]).setNull();
8772 }
8773 }
8774
8775 if (mUSBDeviceFilters)
8776 {
8777 mUSBDeviceFilters->uninit();
8778 unconst(mUSBDeviceFilters).setNull();
8779 }
8780
8781 if (mAudioAdapter)
8782 {
8783 mAudioAdapter->uninit();
8784 unconst(mAudioAdapter).setNull();
8785 }
8786
8787 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8788 {
8789 if (mParallelPorts[slot])
8790 {
8791 mParallelPorts[slot]->uninit();
8792 unconst(mParallelPorts[slot]).setNull();
8793 }
8794 }
8795
8796 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8797 {
8798 if (mSerialPorts[slot])
8799 {
8800 mSerialPorts[slot]->uninit();
8801 unconst(mSerialPorts[slot]).setNull();
8802 }
8803 }
8804
8805 if (mVRDEServer)
8806 {
8807 mVRDEServer->uninit();
8808 unconst(mVRDEServer).setNull();
8809 }
8810
8811 if (mBIOSSettings)
8812 {
8813 mBIOSSettings->uninit();
8814 unconst(mBIOSSettings).setNull();
8815 }
8816
8817 /* Deassociate media (only when a real Machine or a SnapshotMachine
8818 * instance is uninitialized; SessionMachine instances refer to real
8819 * Machine media). This is necessary for a clean re-initialization of
8820 * the VM after successfully re-checking the accessibility state. Note
8821 * that in case of normal Machine or SnapshotMachine uninitialization (as
8822 * a result of unregistering or deleting the snapshot), outdated media
8823 * attachments will already be uninitialized and deleted, so this
8824 * code will not affect them. */
8825 if ( !!mMediaData
8826 && (!isSessionMachine())
8827 )
8828 {
8829 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8830 it != mMediaData->mAttachments.end();
8831 ++it)
8832 {
8833 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8834 if (pMedium.isNull())
8835 continue;
8836 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, getSnapshotId());
8837 AssertComRC(rc);
8838 }
8839 }
8840
8841 if (!isSessionMachine() && !isSnapshotMachine())
8842 {
8843 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8844 if (mData->mFirstSnapshot)
8845 {
8846 // snapshots tree is protected by machine write lock; strictly
8847 // this isn't necessary here since we're deleting the entire
8848 // machine, but otherwise we assert in Snapshot::uninit()
8849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8850 mData->mFirstSnapshot->uninit();
8851 mData->mFirstSnapshot.setNull();
8852 }
8853
8854 mData->mCurrentSnapshot.setNull();
8855 }
8856
8857 /* free data structures (the essential mData structure is not freed here
8858 * since it may be still in use) */
8859 mMediaData.free();
8860 mStorageControllers.free();
8861 mUSBControllers.free();
8862 mHWData.free();
8863 mUserData.free();
8864 mSSData.free();
8865}
8866
8867/**
8868 * Returns a pointer to the Machine object for this machine that acts like a
8869 * parent for complex machine data objects such as shared folders, etc.
8870 *
8871 * For primary Machine objects and for SnapshotMachine objects, returns this
8872 * object's pointer itself. For SessionMachine objects, returns the peer
8873 * (primary) machine pointer.
8874 */
8875Machine* Machine::getMachine()
8876{
8877 if (isSessionMachine())
8878 return (Machine*)mPeer;
8879 return this;
8880}
8881
8882/**
8883 * Makes sure that there are no machine state dependents. If necessary, waits
8884 * for the number of dependents to drop to zero.
8885 *
8886 * Make sure this method is called from under this object's write lock to
8887 * guarantee that no new dependents may be added when this method returns
8888 * control to the caller.
8889 *
8890 * @note Locks this object for writing. The lock will be released while waiting
8891 * (if necessary).
8892 *
8893 * @warning To be used only in methods that change the machine state!
8894 */
8895void Machine::ensureNoStateDependencies()
8896{
8897 AssertReturnVoid(isWriteLockOnCurrentThread());
8898
8899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8900
8901 /* Wait for all state dependents if necessary */
8902 if (mData->mMachineStateDeps != 0)
8903 {
8904 /* lazy semaphore creation */
8905 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8906 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8907
8908 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8909 mData->mMachineStateDeps));
8910
8911 ++mData->mMachineStateChangePending;
8912
8913 /* reset the semaphore before waiting, the last dependent will signal
8914 * it */
8915 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8916
8917 alock.release();
8918
8919 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8920
8921 alock.acquire();
8922
8923 -- mData->mMachineStateChangePending;
8924 }
8925}
8926
8927/**
8928 * Changes the machine state and informs callbacks.
8929 *
8930 * This method is not intended to fail so it either returns S_OK or asserts (and
8931 * returns a failure).
8932 *
8933 * @note Locks this object for writing.
8934 */
8935HRESULT Machine::setMachineState(MachineState_T aMachineState)
8936{
8937 LogFlowThisFuncEnter();
8938 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8939
8940 AutoCaller autoCaller(this);
8941 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8942
8943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8944
8945 /* wait for state dependents to drop to zero */
8946 ensureNoStateDependencies();
8947
8948 if (mData->mMachineState != aMachineState)
8949 {
8950 mData->mMachineState = aMachineState;
8951
8952 RTTimeNow(&mData->mLastStateChange);
8953
8954 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8955 }
8956
8957 LogFlowThisFuncLeave();
8958 return S_OK;
8959}
8960
8961/**
8962 * Searches for a shared folder with the given logical name
8963 * in the collection of shared folders.
8964 *
8965 * @param aName logical name of the shared folder
8966 * @param aSharedFolder where to return the found object
8967 * @param aSetError whether to set the error info if the folder is
8968 * not found
8969 * @return
8970 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8971 *
8972 * @note
8973 * must be called from under the object's lock!
8974 */
8975HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8976 ComObjPtr<SharedFolder> &aSharedFolder,
8977 bool aSetError /* = false */)
8978{
8979 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8980 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8981 it != mHWData->mSharedFolders.end();
8982 ++it)
8983 {
8984 SharedFolder *pSF = *it;
8985 AutoCaller autoCaller(pSF);
8986 if (pSF->getName() == aName)
8987 {
8988 aSharedFolder = pSF;
8989 rc = S_OK;
8990 break;
8991 }
8992 }
8993
8994 if (aSetError && FAILED(rc))
8995 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8996
8997 return rc;
8998}
8999
9000/**
9001 * Initializes all machine instance data from the given settings structures
9002 * from XML. The exception is the machine UUID which needs special handling
9003 * depending on the caller's use case, so the caller needs to set that herself.
9004 *
9005 * This gets called in several contexts during machine initialization:
9006 *
9007 * -- When machine XML exists on disk already and needs to be loaded into memory,
9008 * for example, from registeredInit() to load all registered machines on
9009 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
9010 * attached to the machine should be part of some media registry already.
9011 *
9012 * -- During OVF import, when a machine config has been constructed from an
9013 * OVF file. In this case, puuidRegistry is set to the machine UUID to
9014 * ensure that the media listed as attachments in the config (which have
9015 * been imported from the OVF) receive the correct registry ID.
9016 *
9017 * -- During VM cloning.
9018 *
9019 * @param config Machine settings from XML.
9020 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
9021 * @return
9022 */
9023HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9024 const Guid *puuidRegistry)
9025{
9026 // copy name, description, OS type, teleporter, UTC etc.
9027 mUserData->s = config.machineUserData;
9028
9029 // Decode the Icon overide data from config userdata and set onto Machine.
9030 #define DECODE_STR_MAX _1M
9031 const char* pszStr = config.machineUserData.ovIcon.c_str();
9032 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
9033 if (cbOut > DECODE_STR_MAX)
9034 return setError(E_FAIL,
9035 tr("Icon Data too long.'%d' > '%d'"),
9036 cbOut,
9037 DECODE_STR_MAX);
9038 com::SafeArray<BYTE> iconByte(cbOut);
9039 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
9040 if (FAILED(rc))
9041 return setError(E_FAIL,
9042 tr("Failure to Decode Icon Data. '%s' (%d)"),
9043 pszStr,
9044 rc);
9045 mUserData->mIcon.resize(iconByte.size());
9046 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
9047
9048 // look up the object by Id to check it is valid
9049 ComPtr<IGuestOSType> guestOSType;
9050 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
9051 guestOSType.asOutParam());
9052 if (FAILED(rc)) return rc;
9053
9054 // stateFile (optional)
9055 if (config.strStateFile.isEmpty())
9056 mSSData->strStateFilePath.setNull();
9057 else
9058 {
9059 Utf8Str stateFilePathFull(config.strStateFile);
9060 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
9061 if (RT_FAILURE(vrc))
9062 return setError(E_FAIL,
9063 tr("Invalid saved state file path '%s' (%Rrc)"),
9064 config.strStateFile.c_str(),
9065 vrc);
9066 mSSData->strStateFilePath = stateFilePathFull;
9067 }
9068
9069 // snapshot folder needs special processing so set it again
9070 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9071 if (FAILED(rc)) return rc;
9072
9073 /* Copy the extra data items (Not in any case config is already the same as
9074 * mData->pMachineConfigFile, like when the xml files are read from disk. So
9075 * make sure the extra data map is copied). */
9076 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9077
9078 /* currentStateModified (optional, default is true) */
9079 mData->mCurrentStateModified = config.fCurrentStateModified;
9080
9081 mData->mLastStateChange = config.timeLastStateChange;
9082
9083 /*
9084 * note: all mUserData members must be assigned prior this point because
9085 * we need to commit changes in order to let mUserData be shared by all
9086 * snapshot machine instances.
9087 */
9088 mUserData.commitCopy();
9089
9090 // machine registry, if present (must be loaded before snapshots)
9091 if (config.canHaveOwnMediaRegistry())
9092 {
9093 // determine machine folder
9094 Utf8Str strMachineFolder = getSettingsFileFull();
9095 strMachineFolder.stripFilename();
9096 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
9097 config.mediaRegistry,
9098 strMachineFolder);
9099 if (FAILED(rc)) return rc;
9100 }
9101
9102 /* Snapshot node (optional) */
9103 size_t cRootSnapshots;
9104 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9105 {
9106 // there must be only one root snapshot
9107 Assert(cRootSnapshots == 1);
9108
9109 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9110
9111 rc = loadSnapshot(snap,
9112 config.uuidCurrentSnapshot,
9113 NULL); // no parent == first snapshot
9114 if (FAILED(rc)) return rc;
9115 }
9116
9117 // hardware data
9118 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9119 if (FAILED(rc)) return rc;
9120
9121 // load storage controllers
9122 rc = loadStorageControllers(config.storageMachine,
9123 puuidRegistry,
9124 NULL /* puuidSnapshot */);
9125 if (FAILED(rc)) return rc;
9126
9127 /*
9128 * NOTE: the assignment below must be the last thing to do,
9129 * otherwise it will be not possible to change the settings
9130 * somewhere in the code above because all setters will be
9131 * blocked by checkStateDependency(MutableStateDep).
9132 */
9133
9134 /* set the machine state to Aborted or Saved when appropriate */
9135 if (config.fAborted)
9136 {
9137 mSSData->strStateFilePath.setNull();
9138
9139 /* no need to use setMachineState() during init() */
9140 mData->mMachineState = MachineState_Aborted;
9141 }
9142 else if (!mSSData->strStateFilePath.isEmpty())
9143 {
9144 /* no need to use setMachineState() during init() */
9145 mData->mMachineState = MachineState_Saved;
9146 }
9147
9148 // after loading settings, we are no longer different from the XML on disk
9149 mData->flModifications = 0;
9150
9151 return S_OK;
9152}
9153
9154/**
9155 * Recursively loads all snapshots starting from the given.
9156 *
9157 * @param aNode <Snapshot> node.
9158 * @param aCurSnapshotId Current snapshot ID from the settings file.
9159 * @param aParentSnapshot Parent snapshot.
9160 */
9161HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9162 const Guid &aCurSnapshotId,
9163 Snapshot *aParentSnapshot)
9164{
9165 AssertReturn(!isSnapshotMachine(), E_FAIL);
9166 AssertReturn(!isSessionMachine(), E_FAIL);
9167
9168 HRESULT rc = S_OK;
9169
9170 Utf8Str strStateFile;
9171 if (!data.strStateFile.isEmpty())
9172 {
9173 /* optional */
9174 strStateFile = data.strStateFile;
9175 int vrc = calculateFullPath(strStateFile, strStateFile);
9176 if (RT_FAILURE(vrc))
9177 return setError(E_FAIL,
9178 tr("Invalid saved state file path '%s' (%Rrc)"),
9179 strStateFile.c_str(),
9180 vrc);
9181 }
9182
9183 /* create a snapshot machine object */
9184 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9185 pSnapshotMachine.createObject();
9186 rc = pSnapshotMachine->initFromSettings(this,
9187 data.hardware,
9188 &data.debugging,
9189 &data.autostart,
9190 data.storage,
9191 data.uuid.ref(),
9192 strStateFile);
9193 if (FAILED(rc)) return rc;
9194
9195 /* create a snapshot object */
9196 ComObjPtr<Snapshot> pSnapshot;
9197 pSnapshot.createObject();
9198 /* initialize the snapshot */
9199 rc = pSnapshot->init(mParent, // VirtualBox object
9200 data.uuid,
9201 data.strName,
9202 data.strDescription,
9203 data.timestamp,
9204 pSnapshotMachine,
9205 aParentSnapshot);
9206 if (FAILED(rc)) return rc;
9207
9208 /* memorize the first snapshot if necessary */
9209 if (!mData->mFirstSnapshot)
9210 mData->mFirstSnapshot = pSnapshot;
9211
9212 /* memorize the current snapshot when appropriate */
9213 if ( !mData->mCurrentSnapshot
9214 && pSnapshot->i_getId() == aCurSnapshotId
9215 )
9216 mData->mCurrentSnapshot = pSnapshot;
9217
9218 // now create the children
9219 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9220 it != data.llChildSnapshots.end();
9221 ++it)
9222 {
9223 const settings::Snapshot &childData = *it;
9224 // recurse
9225 rc = loadSnapshot(childData,
9226 aCurSnapshotId,
9227 pSnapshot); // parent = the one we created above
9228 if (FAILED(rc)) return rc;
9229 }
9230
9231 return rc;
9232}
9233
9234/**
9235 * Loads settings into mHWData.
9236 *
9237 * @param data Reference to the hardware settings.
9238 * @param pDbg Pointer to the debugging settings.
9239 * @param pAutostart Pointer to the autostart settings.
9240 */
9241HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9242 const settings::Autostart *pAutostart)
9243{
9244 AssertReturn(!isSessionMachine(), E_FAIL);
9245
9246 HRESULT rc = S_OK;
9247
9248 try
9249 {
9250 /* The hardware version attribute (optional). */
9251 mHWData->mHWVersion = data.strVersion;
9252 mHWData->mHardwareUUID = data.uuid;
9253
9254 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9255 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9256 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9257 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9258 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9259 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9260 mHWData->mPAEEnabled = data.fPAE;
9261 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9262 mHWData->mLongMode = data.enmLongMode;
9263 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9264 mHWData->mCPUCount = data.cCPUs;
9265 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9266 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9267
9268 // cpu
9269 if (mHWData->mCPUHotPlugEnabled)
9270 {
9271 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9272 it != data.llCpus.end();
9273 ++it)
9274 {
9275 const settings::Cpu &cpu = *it;
9276
9277 mHWData->mCPUAttached[cpu.ulId] = true;
9278 }
9279 }
9280
9281 // cpuid leafs
9282 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9283 it != data.llCpuIdLeafs.end();
9284 ++it)
9285 {
9286 const settings::CpuIdLeaf &leaf = *it;
9287
9288 switch (leaf.ulId)
9289 {
9290 case 0x0:
9291 case 0x1:
9292 case 0x2:
9293 case 0x3:
9294 case 0x4:
9295 case 0x5:
9296 case 0x6:
9297 case 0x7:
9298 case 0x8:
9299 case 0x9:
9300 case 0xA:
9301 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9302 break;
9303
9304 case 0x80000000:
9305 case 0x80000001:
9306 case 0x80000002:
9307 case 0x80000003:
9308 case 0x80000004:
9309 case 0x80000005:
9310 case 0x80000006:
9311 case 0x80000007:
9312 case 0x80000008:
9313 case 0x80000009:
9314 case 0x8000000A:
9315 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9316 break;
9317
9318 default:
9319 /* just ignore */
9320 break;
9321 }
9322 }
9323
9324 mHWData->mMemorySize = data.ulMemorySizeMB;
9325 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9326
9327 // boot order
9328 for (size_t i = 0;
9329 i < RT_ELEMENTS(mHWData->mBootOrder);
9330 i++)
9331 {
9332 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9333 if (it == data.mapBootOrder.end())
9334 mHWData->mBootOrder[i] = DeviceType_Null;
9335 else
9336 mHWData->mBootOrder[i] = it->second;
9337 }
9338
9339 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9340 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9341 mHWData->mMonitorCount = data.cMonitors;
9342 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9343 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9344 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9345 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9346 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9347 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9348 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9349 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9350 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9351 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9352 if (!data.strVideoCaptureFile.isEmpty())
9353 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9354 else
9355 mHWData->mVideoCaptureFile.setNull();
9356 mHWData->mFirmwareType = data.firmwareType;
9357 mHWData->mPointingHIDType = data.pointingHIDType;
9358 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9359 mHWData->mChipsetType = data.chipsetType;
9360 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9361 mHWData->mHPETEnabled = data.fHPETEnabled;
9362
9363 /* VRDEServer */
9364 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9365 if (FAILED(rc)) return rc;
9366
9367 /* BIOS */
9368 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9369 if (FAILED(rc)) return rc;
9370
9371 // Bandwidth control (must come before network adapters)
9372 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9373 if (FAILED(rc)) return rc;
9374
9375 /* Shared folders */
9376 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9377 it != data.usbSettings.llUSBControllers.end();
9378 ++it)
9379 {
9380 const settings::USBController &settingsCtrl = *it;
9381 ComObjPtr<USBController> newCtrl;
9382
9383 newCtrl.createObject();
9384 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9385 mUSBControllers->push_back(newCtrl);
9386 }
9387
9388 /* USB device filters */
9389 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9390 if (FAILED(rc)) return rc;
9391
9392 // network adapters
9393 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9394 uint32_t oldCount = mNetworkAdapters.size();
9395 if (newCount > oldCount)
9396 {
9397 mNetworkAdapters.resize(newCount);
9398 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9399 {
9400 unconst(mNetworkAdapters[slot]).createObject();
9401 mNetworkAdapters[slot]->init(this, slot);
9402 }
9403 }
9404 else if (newCount < oldCount)
9405 mNetworkAdapters.resize(newCount);
9406 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9407 it != data.llNetworkAdapters.end();
9408 ++it)
9409 {
9410 const settings::NetworkAdapter &nic = *it;
9411
9412 /* slot unicity is guaranteed by XML Schema */
9413 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9414 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9415 if (FAILED(rc)) return rc;
9416 }
9417
9418 // serial ports
9419 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9420 it != data.llSerialPorts.end();
9421 ++it)
9422 {
9423 const settings::SerialPort &s = *it;
9424
9425 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9426 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9427 if (FAILED(rc)) return rc;
9428 }
9429
9430 // parallel ports (optional)
9431 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9432 it != data.llParallelPorts.end();
9433 ++it)
9434 {
9435 const settings::ParallelPort &p = *it;
9436
9437 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9438 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9439 if (FAILED(rc)) return rc;
9440 }
9441
9442 /* AudioAdapter */
9443 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9444 if (FAILED(rc)) return rc;
9445
9446 /* Shared folders */
9447 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9448 it != data.llSharedFolders.end();
9449 ++it)
9450 {
9451 const settings::SharedFolder &sf = *it;
9452
9453 ComObjPtr<SharedFolder> sharedFolder;
9454 /* Check for double entries. Not allowed! */
9455 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9456 if (SUCCEEDED(rc))
9457 return setError(VBOX_E_OBJECT_IN_USE,
9458 tr("Shared folder named '%s' already exists"),
9459 sf.strName.c_str());
9460
9461 /* Create the new shared folder. Don't break on error. This will be
9462 * reported when the machine starts. */
9463 sharedFolder.createObject();
9464 rc = sharedFolder->init(getMachine(),
9465 sf.strName,
9466 sf.strHostPath,
9467 RT_BOOL(sf.fWritable),
9468 RT_BOOL(sf.fAutoMount),
9469 false /* fFailOnError */);
9470 if (FAILED(rc)) return rc;
9471 mHWData->mSharedFolders.push_back(sharedFolder);
9472 }
9473
9474 // Clipboard
9475 mHWData->mClipboardMode = data.clipboardMode;
9476
9477 // drag'n'drop
9478 mHWData->mDragAndDropMode = data.dragAndDropMode;
9479
9480 // guest settings
9481 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9482
9483 // IO settings
9484 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9485 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9486
9487 // Host PCI devices
9488 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9489 it != data.pciAttachments.end();
9490 ++it)
9491 {
9492 const settings::HostPCIDeviceAttachment &hpda = *it;
9493 ComObjPtr<PCIDeviceAttachment> pda;
9494
9495 pda.createObject();
9496 pda->loadSettings(this, hpda);
9497 mHWData->mPCIDeviceAssignments.push_back(pda);
9498 }
9499
9500 /*
9501 * (The following isn't really real hardware, but it lives in HWData
9502 * for reasons of convenience.)
9503 */
9504
9505#ifdef VBOX_WITH_GUEST_PROPS
9506 /* Guest properties (optional) */
9507 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9508 it != data.llGuestProperties.end();
9509 ++it)
9510 {
9511 const settings::GuestProperty &prop = *it;
9512 uint32_t fFlags = guestProp::NILFLAG;
9513 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9514 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9515 mHWData->mGuestProperties[prop.strName] = property;
9516 }
9517
9518 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9519#endif /* VBOX_WITH_GUEST_PROPS defined */
9520
9521 rc = loadDebugging(pDbg);
9522 if (FAILED(rc))
9523 return rc;
9524
9525 mHWData->mAutostart = *pAutostart;
9526
9527 /* default frontend */
9528 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9529 }
9530 catch(std::bad_alloc &)
9531 {
9532 return E_OUTOFMEMORY;
9533 }
9534
9535 AssertComRC(rc);
9536 return rc;
9537}
9538
9539/**
9540 * Called from Machine::loadHardware() to load the debugging settings of the
9541 * machine.
9542 *
9543 * @param pDbg Pointer to the settings.
9544 */
9545HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9546{
9547 mHWData->mDebugging = *pDbg;
9548 /* no more processing currently required, this will probably change. */
9549 return S_OK;
9550}
9551
9552/**
9553 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9554 *
9555 * @param data
9556 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9557 * @param puuidSnapshot
9558 * @return
9559 */
9560HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9561 const Guid *puuidRegistry,
9562 const Guid *puuidSnapshot)
9563{
9564 AssertReturn(!isSessionMachine(), E_FAIL);
9565
9566 HRESULT rc = S_OK;
9567
9568 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9569 it != data.llStorageControllers.end();
9570 ++it)
9571 {
9572 const settings::StorageController &ctlData = *it;
9573
9574 ComObjPtr<StorageController> pCtl;
9575 /* Try to find one with the name first. */
9576 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9577 if (SUCCEEDED(rc))
9578 return setError(VBOX_E_OBJECT_IN_USE,
9579 tr("Storage controller named '%s' already exists"),
9580 ctlData.strName.c_str());
9581
9582 pCtl.createObject();
9583 rc = pCtl->init(this,
9584 ctlData.strName,
9585 ctlData.storageBus,
9586 ctlData.ulInstance,
9587 ctlData.fBootable);
9588 if (FAILED(rc)) return rc;
9589
9590 mStorageControllers->push_back(pCtl);
9591
9592 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9593 if (FAILED(rc)) return rc;
9594
9595 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9596 if (FAILED(rc)) return rc;
9597
9598 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9599 if (FAILED(rc)) return rc;
9600
9601 /* Set IDE emulation settings (only for AHCI controller). */
9602 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9603 {
9604 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9605 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9606 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9607 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9608 )
9609 return rc;
9610 }
9611
9612 /* Load the attached devices now. */
9613 rc = loadStorageDevices(pCtl,
9614 ctlData,
9615 puuidRegistry,
9616 puuidSnapshot);
9617 if (FAILED(rc)) return rc;
9618 }
9619
9620 return S_OK;
9621}
9622
9623/**
9624 * Called from loadStorageControllers for a controller's devices.
9625 *
9626 * @param aStorageController
9627 * @param data
9628 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9629 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9630 * @return
9631 */
9632HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9633 const settings::StorageController &data,
9634 const Guid *puuidRegistry,
9635 const Guid *puuidSnapshot)
9636{
9637 HRESULT rc = S_OK;
9638
9639 /* paranoia: detect duplicate attachments */
9640 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9641 it != data.llAttachedDevices.end();
9642 ++it)
9643 {
9644 const settings::AttachedDevice &ad = *it;
9645
9646 for (settings::AttachedDevicesList::const_iterator it2 = it;
9647 it2 != data.llAttachedDevices.end();
9648 ++it2)
9649 {
9650 if (it == it2)
9651 continue;
9652
9653 const settings::AttachedDevice &ad2 = *it2;
9654
9655 if ( ad.lPort == ad2.lPort
9656 && ad.lDevice == ad2.lDevice)
9657 {
9658 return setError(E_FAIL,
9659 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9660 aStorageController->i_getName().c_str(),
9661 ad.lPort,
9662 ad.lDevice,
9663 mUserData->s.strName.c_str());
9664 }
9665 }
9666 }
9667
9668 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9669 it != data.llAttachedDevices.end();
9670 ++it)
9671 {
9672 const settings::AttachedDevice &dev = *it;
9673 ComObjPtr<Medium> medium;
9674
9675 switch (dev.deviceType)
9676 {
9677 case DeviceType_Floppy:
9678 case DeviceType_DVD:
9679 if (dev.strHostDriveSrc.isNotEmpty())
9680 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9681 else
9682 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9683 dev.uuid,
9684 false /* fRefresh */,
9685 false /* aSetError */,
9686 medium);
9687 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9688 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9689 rc = S_OK;
9690 break;
9691
9692 case DeviceType_HardDisk:
9693 {
9694 /* find a hard disk by UUID */
9695 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9696 if (FAILED(rc))
9697 {
9698 if (isSnapshotMachine())
9699 {
9700 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9701 // so the user knows that the bad disk is in a snapshot somewhere
9702 com::ErrorInfo info;
9703 return setError(E_FAIL,
9704 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9705 puuidSnapshot->raw(),
9706 info.getText().raw());
9707 }
9708 else
9709 return rc;
9710 }
9711
9712 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9713
9714 if (medium->i_getType() == MediumType_Immutable)
9715 {
9716 if (isSnapshotMachine())
9717 return setError(E_FAIL,
9718 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9719 "of the virtual machine '%s' ('%s')"),
9720 medium->i_getLocationFull().c_str(),
9721 dev.uuid.raw(),
9722 puuidSnapshot->raw(),
9723 mUserData->s.strName.c_str(),
9724 mData->m_strConfigFileFull.c_str());
9725
9726 return setError(E_FAIL,
9727 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9728 medium->i_getLocationFull().c_str(),
9729 dev.uuid.raw(),
9730 mUserData->s.strName.c_str(),
9731 mData->m_strConfigFileFull.c_str());
9732 }
9733
9734 if (medium->i_getType() == MediumType_MultiAttach)
9735 {
9736 if (isSnapshotMachine())
9737 return setError(E_FAIL,
9738 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9739 "of the virtual machine '%s' ('%s')"),
9740 medium->i_getLocationFull().c_str(),
9741 dev.uuid.raw(),
9742 puuidSnapshot->raw(),
9743 mUserData->s.strName.c_str(),
9744 mData->m_strConfigFileFull.c_str());
9745
9746 return setError(E_FAIL,
9747 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9748 medium->i_getLocationFull().c_str(),
9749 dev.uuid.raw(),
9750 mUserData->s.strName.c_str(),
9751 mData->m_strConfigFileFull.c_str());
9752 }
9753
9754 if ( !isSnapshotMachine()
9755 && medium->i_getChildren().size() != 0
9756 )
9757 return setError(E_FAIL,
9758 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9759 "because it has %d differencing child hard disks"),
9760 medium->i_getLocationFull().c_str(),
9761 dev.uuid.raw(),
9762 mUserData->s.strName.c_str(),
9763 mData->m_strConfigFileFull.c_str(),
9764 medium->i_getChildren().size());
9765
9766 if (findAttachment(mMediaData->mAttachments,
9767 medium))
9768 return setError(E_FAIL,
9769 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9770 medium->i_getLocationFull().c_str(),
9771 dev.uuid.raw(),
9772 mUserData->s.strName.c_str(),
9773 mData->m_strConfigFileFull.c_str());
9774
9775 break;
9776 }
9777
9778 default:
9779 return setError(E_FAIL,
9780 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9781 medium->i_getLocationFull().c_str(),
9782 mUserData->s.strName.c_str(),
9783 mData->m_strConfigFileFull.c_str());
9784 }
9785
9786 if (FAILED(rc))
9787 break;
9788
9789 /* Bandwidth groups are loaded at this point. */
9790 ComObjPtr<BandwidthGroup> pBwGroup;
9791
9792 if (!dev.strBwGroup.isEmpty())
9793 {
9794 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9795 if (FAILED(rc))
9796 return setError(E_FAIL,
9797 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9798 medium->i_getLocationFull().c_str(),
9799 dev.strBwGroup.c_str(),
9800 mUserData->s.strName.c_str(),
9801 mData->m_strConfigFileFull.c_str());
9802 pBwGroup->i_reference();
9803 }
9804
9805 const Bstr controllerName = aStorageController->i_getName();
9806 ComObjPtr<MediumAttachment> pAttachment;
9807 pAttachment.createObject();
9808 rc = pAttachment->init(this,
9809 medium,
9810 controllerName,
9811 dev.lPort,
9812 dev.lDevice,
9813 dev.deviceType,
9814 false,
9815 dev.fPassThrough,
9816 dev.fTempEject,
9817 dev.fNonRotational,
9818 dev.fDiscard,
9819 dev.fHotPluggable,
9820 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9821 if (FAILED(rc)) break;
9822
9823 /* associate the medium with this machine and snapshot */
9824 if (!medium.isNull())
9825 {
9826 AutoCaller medCaller(medium);
9827 if (FAILED(medCaller.rc())) return medCaller.rc();
9828 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9829
9830 if (isSnapshotMachine())
9831 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9832 else
9833 rc = medium->i_addBackReference(mData->mUuid);
9834 /* If the medium->addBackReference fails it sets an appropriate
9835 * error message, so no need to do any guesswork here. */
9836
9837 if (puuidRegistry)
9838 // caller wants registry ID to be set on all attached media (OVF import case)
9839 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9840 }
9841
9842 if (FAILED(rc))
9843 break;
9844
9845 /* back up mMediaData to let registeredInit() properly rollback on failure
9846 * (= limited accessibility) */
9847 setModified(IsModified_Storage);
9848 mMediaData.backup();
9849 mMediaData->mAttachments.push_back(pAttachment);
9850 }
9851
9852 return rc;
9853}
9854
9855/**
9856 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9857 *
9858 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9859 * @param aSnapshot where to return the found snapshot
9860 * @param aSetError true to set extended error info on failure
9861 */
9862HRESULT Machine::findSnapshotById(const Guid &aId,
9863 ComObjPtr<Snapshot> &aSnapshot,
9864 bool aSetError /* = false */)
9865{
9866 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9867
9868 if (!mData->mFirstSnapshot)
9869 {
9870 if (aSetError)
9871 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9872 return E_FAIL;
9873 }
9874
9875 if (aId.isZero())
9876 aSnapshot = mData->mFirstSnapshot;
9877 else
9878 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9879
9880 if (!aSnapshot)
9881 {
9882 if (aSetError)
9883 return setError(E_FAIL,
9884 tr("Could not find a snapshot with UUID {%s}"),
9885 aId.toString().c_str());
9886 return E_FAIL;
9887 }
9888
9889 return S_OK;
9890}
9891
9892/**
9893 * Returns the snapshot with the given name or fails of no such snapshot.
9894 *
9895 * @param aName snapshot name to find
9896 * @param aSnapshot where to return the found snapshot
9897 * @param aSetError true to set extended error info on failure
9898 */
9899HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9900 ComObjPtr<Snapshot> &aSnapshot,
9901 bool aSetError /* = false */)
9902{
9903 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9904
9905 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9906
9907 if (!mData->mFirstSnapshot)
9908 {
9909 if (aSetError)
9910 return setError(VBOX_E_OBJECT_NOT_FOUND,
9911 tr("This machine does not have any snapshots"));
9912 return VBOX_E_OBJECT_NOT_FOUND;
9913 }
9914
9915 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9916
9917 if (!aSnapshot)
9918 {
9919 if (aSetError)
9920 return setError(VBOX_E_OBJECT_NOT_FOUND,
9921 tr("Could not find a snapshot named '%s'"), strName.c_str());
9922 return VBOX_E_OBJECT_NOT_FOUND;
9923 }
9924
9925 return S_OK;
9926}
9927
9928/**
9929 * Returns a storage controller object with the given name.
9930 *
9931 * @param aName storage controller name to find
9932 * @param aStorageController where to return the found storage controller
9933 * @param aSetError true to set extended error info on failure
9934 */
9935HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9936 ComObjPtr<StorageController> &aStorageController,
9937 bool aSetError /* = false */)
9938{
9939 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9940
9941 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9942 it != mStorageControllers->end();
9943 ++it)
9944 {
9945 if ((*it)->i_getName() == aName)
9946 {
9947 aStorageController = (*it);
9948 return S_OK;
9949 }
9950 }
9951
9952 if (aSetError)
9953 return setError(VBOX_E_OBJECT_NOT_FOUND,
9954 tr("Could not find a storage controller named '%s'"),
9955 aName.c_str());
9956 return VBOX_E_OBJECT_NOT_FOUND;
9957}
9958
9959/**
9960 * Returns a USB controller object with the given name.
9961 *
9962 * @param aName USB controller name to find
9963 * @param aUSBController where to return the found USB controller
9964 * @param aSetError true to set extended error info on failure
9965 */
9966HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9967 ComObjPtr<USBController> &aUSBController,
9968 bool aSetError /* = false */)
9969{
9970 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9971
9972 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9973 it != mUSBControllers->end();
9974 ++it)
9975 {
9976 if ((*it)->i_getName() == aName)
9977 {
9978 aUSBController = (*it);
9979 return S_OK;
9980 }
9981 }
9982
9983 if (aSetError)
9984 return setError(VBOX_E_OBJECT_NOT_FOUND,
9985 tr("Could not find a storage controller named '%s'"),
9986 aName.c_str());
9987 return VBOX_E_OBJECT_NOT_FOUND;
9988}
9989
9990/**
9991 * Returns the number of USB controller instance of the given type.
9992 *
9993 * @param enmType USB controller type.
9994 */
9995ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9996{
9997 ULONG cCtrls = 0;
9998
9999 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10000 it != mUSBControllers->end();
10001 ++it)
10002 {
10003 if ((*it)->i_getControllerType() == enmType)
10004 cCtrls++;
10005 }
10006
10007 return cCtrls;
10008}
10009
10010HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
10011 MediaData::AttachmentList &atts)
10012{
10013 AutoCaller autoCaller(this);
10014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10015
10016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10017
10018 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
10019 it != mMediaData->mAttachments.end();
10020 ++it)
10021 {
10022 const ComObjPtr<MediumAttachment> &pAtt = *it;
10023
10024 // should never happen, but deal with NULL pointers in the list.
10025 AssertStmt(!pAtt.isNull(), continue);
10026
10027 // getControllerName() needs caller+read lock
10028 AutoCaller autoAttCaller(pAtt);
10029 if (FAILED(autoAttCaller.rc()))
10030 {
10031 atts.clear();
10032 return autoAttCaller.rc();
10033 }
10034 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10035
10036 if (pAtt->i_getControllerName() == aName)
10037 atts.push_back(pAtt);
10038 }
10039
10040 return S_OK;
10041}
10042
10043/**
10044 * Helper for #saveSettings. Cares about renaming the settings directory and
10045 * file if the machine name was changed and about creating a new settings file
10046 * if this is a new machine.
10047 *
10048 * @note Must be never called directly but only from #saveSettings().
10049 */
10050HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
10051{
10052 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10053
10054 HRESULT rc = S_OK;
10055
10056 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10057
10058 /// @todo need to handle primary group change, too
10059
10060 /* attempt to rename the settings file if machine name is changed */
10061 if ( mUserData->s.fNameSync
10062 && mUserData.isBackedUp()
10063 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10064 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10065 )
10066 {
10067 bool dirRenamed = false;
10068 bool fileRenamed = false;
10069
10070 Utf8Str configFile, newConfigFile;
10071 Utf8Str configFilePrev, newConfigFilePrev;
10072 Utf8Str configDir, newConfigDir;
10073
10074 do
10075 {
10076 int vrc = VINF_SUCCESS;
10077
10078 Utf8Str name = mUserData.backedUpData()->s.strName;
10079 Utf8Str newName = mUserData->s.strName;
10080 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10081 if (group == "/")
10082 group.setNull();
10083 Utf8Str newGroup = mUserData->s.llGroups.front();
10084 if (newGroup == "/")
10085 newGroup.setNull();
10086
10087 configFile = mData->m_strConfigFileFull;
10088
10089 /* first, rename the directory if it matches the group and machine name */
10090 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
10091 group.c_str(), RTPATH_DELIMITER, name.c_str());
10092 /** @todo hack, make somehow use of ComposeMachineFilename */
10093 if (mUserData->s.fDirectoryIncludesUUID)
10094 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10095 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
10096 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10097 /** @todo hack, make somehow use of ComposeMachineFilename */
10098 if (mUserData->s.fDirectoryIncludesUUID)
10099 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10100 configDir = configFile;
10101 configDir.stripFilename();
10102 newConfigDir = configDir;
10103 if ( configDir.length() >= groupPlusName.length()
10104 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
10105 {
10106 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10107 Utf8Str newConfigBaseDir(newConfigDir);
10108 newConfigDir.append(newGroupPlusName);
10109 /* consistency: use \ if appropriate on the platform */
10110 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10111 /* new dir and old dir cannot be equal here because of 'if'
10112 * above and because name != newName */
10113 Assert(configDir != newConfigDir);
10114 if (!fSettingsFileIsNew)
10115 {
10116 /* perform real rename only if the machine is not new */
10117 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10118 if ( vrc == VERR_FILE_NOT_FOUND
10119 || vrc == VERR_PATH_NOT_FOUND)
10120 {
10121 /* create the parent directory, then retry renaming */
10122 Utf8Str parent(newConfigDir);
10123 parent.stripFilename();
10124 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10125 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10126 }
10127 if (RT_FAILURE(vrc))
10128 {
10129 rc = setError(E_FAIL,
10130 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10131 configDir.c_str(),
10132 newConfigDir.c_str(),
10133 vrc);
10134 break;
10135 }
10136 /* delete subdirectories which are no longer needed */
10137 Utf8Str dir(configDir);
10138 dir.stripFilename();
10139 while (dir != newConfigBaseDir && dir != ".")
10140 {
10141 vrc = RTDirRemove(dir.c_str());
10142 if (RT_FAILURE(vrc))
10143 break;
10144 dir.stripFilename();
10145 }
10146 dirRenamed = true;
10147 }
10148 }
10149
10150 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10151 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10152
10153 /* then try to rename the settings file itself */
10154 if (newConfigFile != configFile)
10155 {
10156 /* get the path to old settings file in renamed directory */
10157 configFile = Utf8StrFmt("%s%c%s",
10158 newConfigDir.c_str(),
10159 RTPATH_DELIMITER,
10160 RTPathFilename(configFile.c_str()));
10161 if (!fSettingsFileIsNew)
10162 {
10163 /* perform real rename only if the machine is not new */
10164 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10165 if (RT_FAILURE(vrc))
10166 {
10167 rc = setError(E_FAIL,
10168 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10169 configFile.c_str(),
10170 newConfigFile.c_str(),
10171 vrc);
10172 break;
10173 }
10174 fileRenamed = true;
10175 configFilePrev = configFile;
10176 configFilePrev += "-prev";
10177 newConfigFilePrev = newConfigFile;
10178 newConfigFilePrev += "-prev";
10179 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10180 }
10181 }
10182
10183 // update m_strConfigFileFull amd mConfigFile
10184 mData->m_strConfigFileFull = newConfigFile;
10185 // compute the relative path too
10186 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10187
10188 // store the old and new so that VirtualBox::saveSettings() can update
10189 // the media registry
10190 if ( mData->mRegistered
10191 && configDir != newConfigDir)
10192 {
10193 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10194
10195 if (pfNeedsGlobalSaveSettings)
10196 *pfNeedsGlobalSaveSettings = true;
10197 }
10198
10199 // in the saved state file path, replace the old directory with the new directory
10200 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10201 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10202
10203 // and do the same thing for the saved state file paths of all the online snapshots
10204 if (mData->mFirstSnapshot)
10205 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10206 newConfigDir.c_str());
10207 }
10208 while (0);
10209
10210 if (FAILED(rc))
10211 {
10212 /* silently try to rename everything back */
10213 if (fileRenamed)
10214 {
10215 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10216 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10217 }
10218 if (dirRenamed)
10219 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10220 }
10221
10222 if (FAILED(rc)) return rc;
10223 }
10224
10225 if (fSettingsFileIsNew)
10226 {
10227 /* create a virgin config file */
10228 int vrc = VINF_SUCCESS;
10229
10230 /* ensure the settings directory exists */
10231 Utf8Str path(mData->m_strConfigFileFull);
10232 path.stripFilename();
10233 if (!RTDirExists(path.c_str()))
10234 {
10235 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10236 if (RT_FAILURE(vrc))
10237 {
10238 return setError(E_FAIL,
10239 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10240 path.c_str(),
10241 vrc);
10242 }
10243 }
10244
10245 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10246 path = Utf8Str(mData->m_strConfigFileFull);
10247 RTFILE f = NIL_RTFILE;
10248 vrc = RTFileOpen(&f, path.c_str(),
10249 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10250 if (RT_FAILURE(vrc))
10251 return setError(E_FAIL,
10252 tr("Could not create the settings file '%s' (%Rrc)"),
10253 path.c_str(),
10254 vrc);
10255 RTFileClose(f);
10256 }
10257
10258 return rc;
10259}
10260
10261/**
10262 * Saves and commits machine data, user data and hardware data.
10263 *
10264 * Note that on failure, the data remains uncommitted.
10265 *
10266 * @a aFlags may combine the following flags:
10267 *
10268 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10269 * Used when saving settings after an operation that makes them 100%
10270 * correspond to the settings from the current snapshot.
10271 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10272 * #isReallyModified() returns false. This is necessary for cases when we
10273 * change machine data directly, not through the backup()/commit() mechanism.
10274 * - SaveS_Force: settings will be saved without doing a deep compare of the
10275 * settings structures. This is used when this is called because snapshots
10276 * have changed to avoid the overhead of the deep compare.
10277 *
10278 * @note Must be called from under this object's write lock. Locks children for
10279 * writing.
10280 *
10281 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10282 * initialized to false and that will be set to true by this function if
10283 * the caller must invoke VirtualBox::saveSettings() because the global
10284 * settings have changed. This will happen if a machine rename has been
10285 * saved and the global machine and media registries will therefore need
10286 * updating.
10287 */
10288HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10289 int aFlags /*= 0*/)
10290{
10291 LogFlowThisFuncEnter();
10292
10293 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10294
10295 /* make sure child objects are unable to modify the settings while we are
10296 * saving them */
10297 ensureNoStateDependencies();
10298
10299 AssertReturn(!isSnapshotMachine(),
10300 E_FAIL);
10301
10302 HRESULT rc = S_OK;
10303 bool fNeedsWrite = false;
10304
10305 /* First, prepare to save settings. It will care about renaming the
10306 * settings directory and file if the machine name was changed and about
10307 * creating a new settings file if this is a new machine. */
10308 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10309 if (FAILED(rc)) return rc;
10310
10311 // keep a pointer to the current settings structures
10312 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10313 settings::MachineConfigFile *pNewConfig = NULL;
10314
10315 try
10316 {
10317 // make a fresh one to have everyone write stuff into
10318 pNewConfig = new settings::MachineConfigFile(NULL);
10319 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10320
10321 // now go and copy all the settings data from COM to the settings structures
10322 // (this calles saveSettings() on all the COM objects in the machine)
10323 copyMachineDataToSettings(*pNewConfig);
10324
10325 if (aFlags & SaveS_ResetCurStateModified)
10326 {
10327 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10328 mData->mCurrentStateModified = FALSE;
10329 fNeedsWrite = true; // always, no need to compare
10330 }
10331 else if (aFlags & SaveS_Force)
10332 {
10333 fNeedsWrite = true; // always, no need to compare
10334 }
10335 else
10336 {
10337 if (!mData->mCurrentStateModified)
10338 {
10339 // do a deep compare of the settings that we just saved with the settings
10340 // previously stored in the config file; this invokes MachineConfigFile::operator==
10341 // which does a deep compare of all the settings, which is expensive but less expensive
10342 // than writing out XML in vain
10343 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10344
10345 // could still be modified if any settings changed
10346 mData->mCurrentStateModified = fAnySettingsChanged;
10347
10348 fNeedsWrite = fAnySettingsChanged;
10349 }
10350 else
10351 fNeedsWrite = true;
10352 }
10353
10354 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10355
10356 if (fNeedsWrite)
10357 // now spit it all out!
10358 pNewConfig->write(mData->m_strConfigFileFull);
10359
10360 mData->pMachineConfigFile = pNewConfig;
10361 delete pOldConfig;
10362 commit();
10363
10364 // after saving settings, we are no longer different from the XML on disk
10365 mData->flModifications = 0;
10366 }
10367 catch (HRESULT err)
10368 {
10369 // we assume that error info is set by the thrower
10370 rc = err;
10371
10372 // restore old config
10373 delete pNewConfig;
10374 mData->pMachineConfigFile = pOldConfig;
10375 }
10376 catch (...)
10377 {
10378 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10379 }
10380
10381 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10382 {
10383 /* Fire the data change event, even on failure (since we've already
10384 * committed all data). This is done only for SessionMachines because
10385 * mutable Machine instances are always not registered (i.e. private
10386 * to the client process that creates them) and thus don't need to
10387 * inform callbacks. */
10388 if (isSessionMachine())
10389 mParent->i_onMachineDataChange(mData->mUuid);
10390 }
10391
10392 LogFlowThisFunc(("rc=%08X\n", rc));
10393 LogFlowThisFuncLeave();
10394 return rc;
10395}
10396
10397/**
10398 * Implementation for saving the machine settings into the given
10399 * settings::MachineConfigFile instance. This copies machine extradata
10400 * from the previous machine config file in the instance data, if any.
10401 *
10402 * This gets called from two locations:
10403 *
10404 * -- Machine::saveSettings(), during the regular XML writing;
10405 *
10406 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10407 * exported to OVF and we write the VirtualBox proprietary XML
10408 * into a <vbox:Machine> tag.
10409 *
10410 * This routine fills all the fields in there, including snapshots, *except*
10411 * for the following:
10412 *
10413 * -- fCurrentStateModified. There is some special logic associated with that.
10414 *
10415 * The caller can then call MachineConfigFile::write() or do something else
10416 * with it.
10417 *
10418 * Caller must hold the machine lock!
10419 *
10420 * This throws XML errors and HRESULT, so the caller must have a catch block!
10421 */
10422void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10423{
10424 // deep copy extradata
10425 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10426
10427 config.uuid = mData->mUuid;
10428
10429 // copy name, description, OS type, teleport, UTC etc.
10430 config.machineUserData = mUserData->s;
10431
10432 // Encode the Icon Override data from Machine and store on config userdata.
10433 com::SafeArray<BYTE> iconByte;
10434 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10435 ssize_t cbData = iconByte.size();
10436 if (cbData > 0)
10437 {
10438 ssize_t cchOut = RTBase64EncodedLength(cbData);
10439 Utf8Str strIconData;
10440 strIconData.reserve(cchOut+1);
10441 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10442 strIconData.mutableRaw(), strIconData.capacity(),
10443 NULL);
10444 if (RT_FAILURE(vrc))
10445 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10446 strIconData.jolt();
10447 config.machineUserData.ovIcon = strIconData;
10448 }
10449 else
10450 config.machineUserData.ovIcon.setNull();
10451
10452 if ( mData->mMachineState == MachineState_Saved
10453 || mData->mMachineState == MachineState_Restoring
10454 // when deleting a snapshot we may or may not have a saved state in the current state,
10455 // so let's not assert here please
10456 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10457 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10458 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10459 && (!mSSData->strStateFilePath.isEmpty())
10460 )
10461 )
10462 {
10463 Assert(!mSSData->strStateFilePath.isEmpty());
10464 /* try to make the file name relative to the settings file dir */
10465 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10466 }
10467 else
10468 {
10469 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10470 config.strStateFile.setNull();
10471 }
10472
10473 if (mData->mCurrentSnapshot)
10474 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10475 else
10476 config.uuidCurrentSnapshot.clear();
10477
10478 config.timeLastStateChange = mData->mLastStateChange;
10479 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10480 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10481
10482 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10483 if (FAILED(rc)) throw rc;
10484
10485 rc = saveStorageControllers(config.storageMachine);
10486 if (FAILED(rc)) throw rc;
10487
10488 // save machine's media registry if this is VirtualBox 4.0 or later
10489 if (config.canHaveOwnMediaRegistry())
10490 {
10491 // determine machine folder
10492 Utf8Str strMachineFolder = getSettingsFileFull();
10493 strMachineFolder.stripFilename();
10494 mParent->i_saveMediaRegistry(config.mediaRegistry,
10495 getId(), // only media with registry ID == machine UUID
10496 strMachineFolder);
10497 // this throws HRESULT
10498 }
10499
10500 // save snapshots
10501 rc = saveAllSnapshots(config);
10502 if (FAILED(rc)) throw rc;
10503}
10504
10505/**
10506 * Saves all snapshots of the machine into the given machine config file. Called
10507 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10508 * @param config
10509 * @return
10510 */
10511HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10512{
10513 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10514
10515 HRESULT rc = S_OK;
10516
10517 try
10518 {
10519 config.llFirstSnapshot.clear();
10520
10521 if (mData->mFirstSnapshot)
10522 {
10523 settings::Snapshot snapNew;
10524 config.llFirstSnapshot.push_back(snapNew);
10525
10526 // get reference to the fresh copy of the snapshot on the list and
10527 // work on that copy directly to avoid excessive copying later
10528 settings::Snapshot &snap = config.llFirstSnapshot.front();
10529
10530 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
10531 if (FAILED(rc)) throw rc;
10532 }
10533
10534// if (mType == IsSessionMachine)
10535// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10536
10537 }
10538 catch (HRESULT err)
10539 {
10540 /* we assume that error info is set by the thrower */
10541 rc = err;
10542 }
10543 catch (...)
10544 {
10545 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10546 }
10547
10548 return rc;
10549}
10550
10551/**
10552 * Saves the VM hardware configuration. It is assumed that the
10553 * given node is empty.
10554 *
10555 * @param data Reference to the settings object for the hardware config.
10556 * @param pDbg Pointer to the settings object for the debugging config
10557 * which happens to live in mHWData.
10558 * @param pAutostart Pointer to the settings object for the autostart config
10559 * which happens to live in mHWData.
10560 */
10561HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10562 settings::Autostart *pAutostart)
10563{
10564 HRESULT rc = S_OK;
10565
10566 try
10567 {
10568 /* The hardware version attribute (optional).
10569 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10570 if ( mHWData->mHWVersion == "1"
10571 && mSSData->strStateFilePath.isEmpty()
10572 )
10573 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
10574
10575 data.strVersion = mHWData->mHWVersion;
10576 data.uuid = mHWData->mHardwareUUID;
10577
10578 // CPU
10579 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10580 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10581 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10582 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10583 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10584 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10585 data.fPAE = !!mHWData->mPAEEnabled;
10586 data.enmLongMode = mHWData->mLongMode;
10587 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10588 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10589
10590 /* Standard and Extended CPUID leafs. */
10591 data.llCpuIdLeafs.clear();
10592 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10593 {
10594 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10595 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10596 }
10597 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10598 {
10599 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10600 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10601 }
10602
10603 data.cCPUs = mHWData->mCPUCount;
10604 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10605 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10606
10607 data.llCpus.clear();
10608 if (data.fCpuHotPlug)
10609 {
10610 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10611 {
10612 if (mHWData->mCPUAttached[idx])
10613 {
10614 settings::Cpu cpu;
10615 cpu.ulId = idx;
10616 data.llCpus.push_back(cpu);
10617 }
10618 }
10619 }
10620
10621 // memory
10622 data.ulMemorySizeMB = mHWData->mMemorySize;
10623 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10624
10625 // firmware
10626 data.firmwareType = mHWData->mFirmwareType;
10627
10628 // HID
10629 data.pointingHIDType = mHWData->mPointingHIDType;
10630 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10631
10632 // chipset
10633 data.chipsetType = mHWData->mChipsetType;
10634
10635 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10636
10637 // HPET
10638 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10639
10640 // boot order
10641 data.mapBootOrder.clear();
10642 for (size_t i = 0;
10643 i < RT_ELEMENTS(mHWData->mBootOrder);
10644 ++i)
10645 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10646
10647 // display
10648 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10649 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10650 data.cMonitors = mHWData->mMonitorCount;
10651 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10652 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10653 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10654 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10655 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10656 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10657 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10658 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10659 {
10660 if (mHWData->maVideoCaptureScreens[i])
10661 ASMBitSet(&data.u64VideoCaptureScreens, i);
10662 else
10663 ASMBitClear(&data.u64VideoCaptureScreens, i);
10664 }
10665 /* store relative video capture file if possible */
10666 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10667
10668 /* VRDEServer settings (optional) */
10669 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10670 if (FAILED(rc)) throw rc;
10671
10672 /* BIOS (required) */
10673 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10674 if (FAILED(rc)) throw rc;
10675
10676 /* USB Controller (required) */
10677 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10678 it != mUSBControllers->end();
10679 ++it)
10680 {
10681 ComObjPtr<USBController> ctrl = *it;
10682 settings::USBController settingsCtrl;
10683
10684 settingsCtrl.strName = ctrl->i_getName();
10685 settingsCtrl.enmType = ctrl->i_getControllerType();
10686
10687 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10688 }
10689
10690 /* USB device filters (required) */
10691 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10692 if (FAILED(rc)) throw rc;
10693
10694 /* Network adapters (required) */
10695 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10696 data.llNetworkAdapters.clear();
10697 /* Write out only the nominal number of network adapters for this
10698 * chipset type. Since Machine::commit() hasn't been called there
10699 * may be extra NIC settings in the vector. */
10700 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10701 {
10702 settings::NetworkAdapter nic;
10703 nic.ulSlot = slot;
10704 /* paranoia check... must not be NULL, but must not crash either. */
10705 if (mNetworkAdapters[slot])
10706 {
10707 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10708 if (FAILED(rc)) throw rc;
10709
10710 data.llNetworkAdapters.push_back(nic);
10711 }
10712 }
10713
10714 /* Serial ports */
10715 data.llSerialPorts.clear();
10716 for (ULONG slot = 0;
10717 slot < RT_ELEMENTS(mSerialPorts);
10718 ++slot)
10719 {
10720 settings::SerialPort s;
10721 s.ulSlot = slot;
10722 rc = mSerialPorts[slot]->i_saveSettings(s);
10723 if (FAILED(rc)) return rc;
10724
10725 data.llSerialPorts.push_back(s);
10726 }
10727
10728 /* Parallel ports */
10729 data.llParallelPorts.clear();
10730 for (ULONG slot = 0;
10731 slot < RT_ELEMENTS(mParallelPorts);
10732 ++slot)
10733 {
10734 settings::ParallelPort p;
10735 p.ulSlot = slot;
10736 rc = mParallelPorts[slot]->i_saveSettings(p);
10737 if (FAILED(rc)) return rc;
10738
10739 data.llParallelPorts.push_back(p);
10740 }
10741
10742 /* Audio adapter */
10743 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10744 if (FAILED(rc)) return rc;
10745
10746 /* Shared folders */
10747 data.llSharedFolders.clear();
10748 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10749 it != mHWData->mSharedFolders.end();
10750 ++it)
10751 {
10752 SharedFolder *pSF = *it;
10753 AutoCaller sfCaller(pSF);
10754 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10755 settings::SharedFolder sf;
10756 sf.strName = pSF->getName();
10757 sf.strHostPath = pSF->getHostPath();
10758 sf.fWritable = !!pSF->isWritable();
10759 sf.fAutoMount = !!pSF->isAutoMounted();
10760
10761 data.llSharedFolders.push_back(sf);
10762 }
10763
10764 // clipboard
10765 data.clipboardMode = mHWData->mClipboardMode;
10766
10767 // drag'n'drop
10768 data.dragAndDropMode = mHWData->mDragAndDropMode;
10769
10770 /* Guest */
10771 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10772
10773 // IO settings
10774 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10775 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10776
10777 /* BandwidthControl (required) */
10778 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10779 if (FAILED(rc)) throw rc;
10780
10781 /* Host PCI devices */
10782 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10783 it != mHWData->mPCIDeviceAssignments.end();
10784 ++it)
10785 {
10786 ComObjPtr<PCIDeviceAttachment> pda = *it;
10787 settings::HostPCIDeviceAttachment hpda;
10788
10789 rc = pda->saveSettings(hpda);
10790 if (FAILED(rc)) throw rc;
10791
10792 data.pciAttachments.push_back(hpda);
10793 }
10794
10795
10796 // guest properties
10797 data.llGuestProperties.clear();
10798#ifdef VBOX_WITH_GUEST_PROPS
10799 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10800 it != mHWData->mGuestProperties.end();
10801 ++it)
10802 {
10803 HWData::GuestProperty property = it->second;
10804
10805 /* Remove transient guest properties at shutdown unless we
10806 * are saving state */
10807 if ( ( mData->mMachineState == MachineState_PoweredOff
10808 || mData->mMachineState == MachineState_Aborted
10809 || mData->mMachineState == MachineState_Teleported)
10810 && ( property.mFlags & guestProp::TRANSIENT
10811 || property.mFlags & guestProp::TRANSRESET))
10812 continue;
10813 settings::GuestProperty prop;
10814 prop.strName = it->first;
10815 prop.strValue = property.strValue;
10816 prop.timestamp = property.mTimestamp;
10817 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10818 guestProp::writeFlags(property.mFlags, szFlags);
10819 prop.strFlags = szFlags;
10820
10821 data.llGuestProperties.push_back(prop);
10822 }
10823
10824 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10825 /* I presume this doesn't require a backup(). */
10826 mData->mGuestPropertiesModified = FALSE;
10827#endif /* VBOX_WITH_GUEST_PROPS defined */
10828
10829 *pDbg = mHWData->mDebugging;
10830 *pAutostart = mHWData->mAutostart;
10831
10832 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10833 }
10834 catch(std::bad_alloc &)
10835 {
10836 return E_OUTOFMEMORY;
10837 }
10838
10839 AssertComRC(rc);
10840 return rc;
10841}
10842
10843/**
10844 * Saves the storage controller configuration.
10845 *
10846 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10847 */
10848HRESULT Machine::saveStorageControllers(settings::Storage &data)
10849{
10850 data.llStorageControllers.clear();
10851
10852 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10853 it != mStorageControllers->end();
10854 ++it)
10855 {
10856 HRESULT rc;
10857 ComObjPtr<StorageController> pCtl = *it;
10858
10859 settings::StorageController ctl;
10860 ctl.strName = pCtl->i_getName();
10861 ctl.controllerType = pCtl->i_getControllerType();
10862 ctl.storageBus = pCtl->i_getStorageBus();
10863 ctl.ulInstance = pCtl->i_getInstance();
10864 ctl.fBootable = pCtl->i_getBootable();
10865
10866 /* Save the port count. */
10867 ULONG portCount;
10868 rc = pCtl->COMGETTER(PortCount)(&portCount);
10869 ComAssertComRCRet(rc, rc);
10870 ctl.ulPortCount = portCount;
10871
10872 /* Save fUseHostIOCache */
10873 BOOL fUseHostIOCache;
10874 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10875 ComAssertComRCRet(rc, rc);
10876 ctl.fUseHostIOCache = !!fUseHostIOCache;
10877
10878 /* Save IDE emulation settings. */
10879 if (ctl.controllerType == StorageControllerType_IntelAhci)
10880 {
10881 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10882 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10883 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10884 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10885 )
10886 ComAssertComRCRet(rc, rc);
10887 }
10888
10889 /* save the devices now. */
10890 rc = saveStorageDevices(pCtl, ctl);
10891 ComAssertComRCRet(rc, rc);
10892
10893 data.llStorageControllers.push_back(ctl);
10894 }
10895
10896 return S_OK;
10897}
10898
10899/**
10900 * Saves the hard disk configuration.
10901 */
10902HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10903 settings::StorageController &data)
10904{
10905 MediaData::AttachmentList atts;
10906
10907 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->i_getName()).raw(), atts);
10908 if (FAILED(rc)) return rc;
10909
10910 data.llAttachedDevices.clear();
10911 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10912 it != atts.end();
10913 ++it)
10914 {
10915 settings::AttachedDevice dev;
10916
10917 MediumAttachment *pAttach = *it;
10918 Medium *pMedium = pAttach->i_getMedium();
10919
10920 dev.deviceType = pAttach->i_getType();
10921 dev.lPort = pAttach->i_getPort();
10922 dev.lDevice = pAttach->i_getDevice();
10923 dev.fPassThrough = pAttach->i_getPassthrough();
10924 dev.fHotPluggable = pAttach->i_getHotPluggable();
10925 if (pMedium)
10926 {
10927 if (pMedium->i_isHostDrive())
10928 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10929 else
10930 dev.uuid = pMedium->i_getId();
10931 dev.fTempEject = pAttach->i_getTempEject();
10932 dev.fNonRotational = pAttach->i_getNonRotational();
10933 dev.fDiscard = pAttach->i_getDiscard();
10934 }
10935
10936 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10937
10938 data.llAttachedDevices.push_back(dev);
10939 }
10940
10941 return S_OK;
10942}
10943
10944/**
10945 * Saves machine state settings as defined by aFlags
10946 * (SaveSTS_* values).
10947 *
10948 * @param aFlags Combination of SaveSTS_* flags.
10949 *
10950 * @note Locks objects for writing.
10951 */
10952HRESULT Machine::saveStateSettings(int aFlags)
10953{
10954 if (aFlags == 0)
10955 return S_OK;
10956
10957 AutoCaller autoCaller(this);
10958 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10959
10960 /* This object's write lock is also necessary to serialize file access
10961 * (prevent concurrent reads and writes) */
10962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10963
10964 HRESULT rc = S_OK;
10965
10966 Assert(mData->pMachineConfigFile);
10967
10968 try
10969 {
10970 if (aFlags & SaveSTS_CurStateModified)
10971 mData->pMachineConfigFile->fCurrentStateModified = true;
10972
10973 if (aFlags & SaveSTS_StateFilePath)
10974 {
10975 if (!mSSData->strStateFilePath.isEmpty())
10976 /* try to make the file name relative to the settings file dir */
10977 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10978 else
10979 mData->pMachineConfigFile->strStateFile.setNull();
10980 }
10981
10982 if (aFlags & SaveSTS_StateTimeStamp)
10983 {
10984 Assert( mData->mMachineState != MachineState_Aborted
10985 || mSSData->strStateFilePath.isEmpty());
10986
10987 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10988
10989 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10990//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10991 }
10992
10993 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10994 }
10995 catch (...)
10996 {
10997 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10998 }
10999
11000 return rc;
11001}
11002
11003/**
11004 * Ensures that the given medium is added to a media registry. If this machine
11005 * was created with 4.0 or later, then the machine registry is used. Otherwise
11006 * the global VirtualBox media registry is used.
11007 *
11008 * Caller must NOT hold machine lock, media tree or any medium locks!
11009 *
11010 * @param pMedium
11011 */
11012void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11013{
11014 /* Paranoia checks: do not hold machine or media tree locks. */
11015 AssertReturnVoid(!isWriteLockOnCurrentThread());
11016 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11017
11018 ComObjPtr<Medium> pBase;
11019 {
11020 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11021 pBase = pMedium->i_getBase();
11022 }
11023
11024 /* Paranoia checks: do not hold medium locks. */
11025 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11026 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11027
11028 // decide which medium registry to use now that the medium is attached:
11029 Guid uuid;
11030 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
11031 // machine XML is VirtualBox 4.0 or higher:
11032 uuid = getId(); // machine UUID
11033 else
11034 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
11035
11036 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
11037 mParent->i_markRegistryModified(uuid);
11038
11039 /* For more complex hard disk structures it can happen that the base
11040 * medium isn't yet associated with any medium registry. Do that now. */
11041 if (pMedium != pBase)
11042 {
11043 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
11044 mParent->i_markRegistryModified(uuid);
11045 }
11046}
11047
11048/**
11049 * Creates differencing hard disks for all normal hard disks attached to this
11050 * machine and a new set of attachments to refer to created disks.
11051 *
11052 * Used when taking a snapshot or when deleting the current state. Gets called
11053 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11054 *
11055 * This method assumes that mMediaData contains the original hard disk attachments
11056 * it needs to create diffs for. On success, these attachments will be replaced
11057 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
11058 * called to delete created diffs which will also rollback mMediaData and restore
11059 * whatever was backed up before calling this method.
11060 *
11061 * Attachments with non-normal hard disks are left as is.
11062 *
11063 * If @a aOnline is @c false then the original hard disks that require implicit
11064 * diffs will be locked for reading. Otherwise it is assumed that they are
11065 * already locked for writing (when the VM was started). Note that in the latter
11066 * case it is responsibility of the caller to lock the newly created diffs for
11067 * writing if this method succeeds.
11068 *
11069 * @param aProgress Progress object to run (must contain at least as
11070 * many operations left as the number of hard disks
11071 * attached).
11072 * @param aOnline Whether the VM was online prior to this operation.
11073 *
11074 * @note The progress object is not marked as completed, neither on success nor
11075 * on failure. This is a responsibility of the caller.
11076 *
11077 * @note Locks this object and the media tree for writing.
11078 */
11079HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
11080 ULONG aWeight,
11081 bool aOnline)
11082{
11083 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11084
11085 AutoCaller autoCaller(this);
11086 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11087
11088 AutoMultiWriteLock2 alock(this->lockHandle(),
11089 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11090
11091 /* must be in a protective state because we release the lock below */
11092 AssertReturn( mData->mMachineState == MachineState_Saving
11093 || mData->mMachineState == MachineState_LiveSnapshotting
11094 || mData->mMachineState == MachineState_RestoringSnapshot
11095 || mData->mMachineState == MachineState_DeletingSnapshot
11096 , E_FAIL);
11097
11098 HRESULT rc = S_OK;
11099
11100 // use appropriate locked media map (online or offline)
11101 MediumLockListMap lockedMediaOffline;
11102 MediumLockListMap *lockedMediaMap;
11103 if (aOnline)
11104 lockedMediaMap = &mData->mSession.mLockedMedia;
11105 else
11106 lockedMediaMap = &lockedMediaOffline;
11107
11108 try
11109 {
11110 if (!aOnline)
11111 {
11112 /* lock all attached hard disks early to detect "in use"
11113 * situations before creating actual diffs */
11114 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11115 it != mMediaData->mAttachments.end();
11116 ++it)
11117 {
11118 MediumAttachment* pAtt = *it;
11119 if (pAtt->i_getType() == DeviceType_HardDisk)
11120 {
11121 Medium* pMedium = pAtt->i_getMedium();
11122 Assert(pMedium);
11123
11124 MediumLockList *pMediumLockList(new MediumLockList());
11125 alock.release();
11126 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11127 false /* fMediumLockWrite */,
11128 NULL,
11129 *pMediumLockList);
11130 alock.acquire();
11131 if (FAILED(rc))
11132 {
11133 delete pMediumLockList;
11134 throw rc;
11135 }
11136 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11137 if (FAILED(rc))
11138 {
11139 throw setError(rc,
11140 tr("Collecting locking information for all attached media failed"));
11141 }
11142 }
11143 }
11144
11145 /* Now lock all media. If this fails, nothing is locked. */
11146 alock.release();
11147 rc = lockedMediaMap->Lock();
11148 alock.acquire();
11149 if (FAILED(rc))
11150 {
11151 throw setError(rc,
11152 tr("Locking of attached media failed"));
11153 }
11154 }
11155
11156 /* remember the current list (note that we don't use backup() since
11157 * mMediaData may be already backed up) */
11158 MediaData::AttachmentList atts = mMediaData->mAttachments;
11159
11160 /* start from scratch */
11161 mMediaData->mAttachments.clear();
11162
11163 /* go through remembered attachments and create diffs for normal hard
11164 * disks and attach them */
11165 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11166 it != atts.end();
11167 ++it)
11168 {
11169 MediumAttachment* pAtt = *it;
11170
11171 DeviceType_T devType = pAtt->i_getType();
11172 Medium* pMedium = pAtt->i_getMedium();
11173
11174 if ( devType != DeviceType_HardDisk
11175 || pMedium == NULL
11176 || pMedium->i_getType() != MediumType_Normal)
11177 {
11178 /* copy the attachment as is */
11179
11180 /** @todo the progress object created in Console::TakeSnaphot
11181 * only expects operations for hard disks. Later other
11182 * device types need to show up in the progress as well. */
11183 if (devType == DeviceType_HardDisk)
11184 {
11185 if (pMedium == NULL)
11186 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11187 aWeight); // weight
11188 else
11189 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11190 pMedium->i_getBase()->i_getName().c_str()).raw(),
11191 aWeight); // weight
11192 }
11193
11194 mMediaData->mAttachments.push_back(pAtt);
11195 continue;
11196 }
11197
11198 /* need a diff */
11199 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11200 pMedium->i_getBase()->i_getName().c_str()).raw(),
11201 aWeight); // weight
11202
11203 Utf8Str strFullSnapshotFolder;
11204 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11205
11206 ComObjPtr<Medium> diff;
11207 diff.createObject();
11208 // store the diff in the same registry as the parent
11209 // (this cannot fail here because we can't create implicit diffs for
11210 // unregistered images)
11211 Guid uuidRegistryParent;
11212 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11213 Assert(fInRegistry); NOREF(fInRegistry);
11214 rc = diff->init(mParent,
11215 pMedium->i_getPreferredDiffFormat(),
11216 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11217 uuidRegistryParent);
11218 if (FAILED(rc)) throw rc;
11219
11220 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11221 * the push_back? Looks like we're going to release medium with the
11222 * wrong kind of lock (general issue with if we fail anywhere at all)
11223 * and an orphaned VDI in the snapshots folder. */
11224
11225 /* update the appropriate lock list */
11226 MediumLockList *pMediumLockList;
11227 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11228 AssertComRCThrowRC(rc);
11229 if (aOnline)
11230 {
11231 alock.release();
11232 /* The currently attached medium will be read-only, change
11233 * the lock type to read. */
11234 rc = pMediumLockList->Update(pMedium, false);
11235 alock.acquire();
11236 AssertComRCThrowRC(rc);
11237 }
11238
11239 /* release the locks before the potentially lengthy operation */
11240 alock.release();
11241 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
11242 pMediumLockList,
11243 NULL /* aProgress */,
11244 true /* aWait */);
11245 alock.acquire();
11246 if (FAILED(rc)) throw rc;
11247
11248 /* actual lock list update is done in Medium::commitMedia */
11249
11250 rc = diff->i_addBackReference(mData->mUuid);
11251 AssertComRCThrowRC(rc);
11252
11253 /* add a new attachment */
11254 ComObjPtr<MediumAttachment> attachment;
11255 attachment.createObject();
11256 rc = attachment->init(this,
11257 diff,
11258 pAtt->i_getControllerName(),
11259 pAtt->i_getPort(),
11260 pAtt->i_getDevice(),
11261 DeviceType_HardDisk,
11262 true /* aImplicit */,
11263 false /* aPassthrough */,
11264 false /* aTempEject */,
11265 pAtt->i_getNonRotational(),
11266 pAtt->i_getDiscard(),
11267 pAtt->i_getHotPluggable(),
11268 pAtt->i_getBandwidthGroup());
11269 if (FAILED(rc)) throw rc;
11270
11271 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11272 AssertComRCThrowRC(rc);
11273 mMediaData->mAttachments.push_back(attachment);
11274 }
11275 }
11276 catch (HRESULT aRC) { rc = aRC; }
11277
11278 /* unlock all hard disks we locked when there is no VM */
11279 if (!aOnline)
11280 {
11281 ErrorInfoKeeper eik;
11282
11283 HRESULT rc1 = lockedMediaMap->Clear();
11284 AssertComRC(rc1);
11285 }
11286
11287 return rc;
11288}
11289
11290/**
11291 * Deletes implicit differencing hard disks created either by
11292 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11293 *
11294 * Note that to delete hard disks created by #AttachDevice() this method is
11295 * called from #fixupMedia() when the changes are rolled back.
11296 *
11297 * @note Locks this object and the media tree for writing.
11298 */
11299HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11300{
11301 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11302
11303 AutoCaller autoCaller(this);
11304 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11305
11306 AutoMultiWriteLock2 alock(this->lockHandle(),
11307 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11308
11309 /* We absolutely must have backed up state. */
11310 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11311
11312 /* Check if there are any implicitly created diff images. */
11313 bool fImplicitDiffs = false;
11314 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11315 it != mMediaData->mAttachments.end();
11316 ++it)
11317 {
11318 const ComObjPtr<MediumAttachment> &pAtt = *it;
11319 if (pAtt->i_isImplicit())
11320 {
11321 fImplicitDiffs = true;
11322 break;
11323 }
11324 }
11325 /* If there is nothing to do, leave early. This saves lots of image locking
11326 * effort. It also avoids a MachineStateChanged event without real reason.
11327 * This is important e.g. when loading a VM config, because there should be
11328 * no events. Otherwise API clients can become thoroughly confused for
11329 * inaccessible VMs (the code for loading VM configs uses this method for
11330 * cleanup if the config makes no sense), as they take such events as an
11331 * indication that the VM is alive, and they would force the VM config to
11332 * be reread, leading to an endless loop. */
11333 if (!fImplicitDiffs)
11334 return S_OK;
11335
11336 HRESULT rc = S_OK;
11337 MachineState_T oldState = mData->mMachineState;
11338
11339 /* will release the lock before the potentially lengthy operation,
11340 * so protect with the special state (unless already protected) */
11341 if ( oldState != MachineState_Saving
11342 && oldState != MachineState_LiveSnapshotting
11343 && oldState != MachineState_RestoringSnapshot
11344 && oldState != MachineState_DeletingSnapshot
11345 && oldState != MachineState_DeletingSnapshotOnline
11346 && oldState != MachineState_DeletingSnapshotPaused
11347 )
11348 setMachineState(MachineState_SettingUp);
11349
11350 // use appropriate locked media map (online or offline)
11351 MediumLockListMap lockedMediaOffline;
11352 MediumLockListMap *lockedMediaMap;
11353 if (aOnline)
11354 lockedMediaMap = &mData->mSession.mLockedMedia;
11355 else
11356 lockedMediaMap = &lockedMediaOffline;
11357
11358 try
11359 {
11360 if (!aOnline)
11361 {
11362 /* lock all attached hard disks early to detect "in use"
11363 * situations before deleting actual diffs */
11364 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11365 it != mMediaData->mAttachments.end();
11366 ++it)
11367 {
11368 MediumAttachment* pAtt = *it;
11369 if (pAtt->i_getType() == DeviceType_HardDisk)
11370 {
11371 Medium* pMedium = pAtt->i_getMedium();
11372 Assert(pMedium);
11373
11374 MediumLockList *pMediumLockList(new MediumLockList());
11375 alock.release();
11376 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11377 false /* fMediumLockWrite */,
11378 NULL,
11379 *pMediumLockList);
11380 alock.acquire();
11381
11382 if (FAILED(rc))
11383 {
11384 delete pMediumLockList;
11385 throw rc;
11386 }
11387
11388 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11389 if (FAILED(rc))
11390 throw rc;
11391 }
11392 }
11393
11394 if (FAILED(rc))
11395 throw rc;
11396 } // end of offline
11397
11398 /* Lock lists are now up to date and include implicitly created media */
11399
11400 /* Go through remembered attachments and delete all implicitly created
11401 * diffs and fix up the attachment information */
11402 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11403 MediaData::AttachmentList implicitAtts;
11404 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11405 it != mMediaData->mAttachments.end();
11406 ++it)
11407 {
11408 ComObjPtr<MediumAttachment> pAtt = *it;
11409 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11410 if (pMedium.isNull())
11411 continue;
11412
11413 // Implicit attachments go on the list for deletion and back references are removed.
11414 if (pAtt->i_isImplicit())
11415 {
11416 /* Deassociate and mark for deletion */
11417 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11418 rc = pMedium->i_removeBackReference(mData->mUuid);
11419 if (FAILED(rc))
11420 throw rc;
11421 implicitAtts.push_back(pAtt);
11422 continue;
11423 }
11424
11425 /* Was this medium attached before? */
11426 if (!findAttachment(oldAtts, pMedium))
11427 {
11428 /* no: de-associate */
11429 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11430 rc = pMedium->i_removeBackReference(mData->mUuid);
11431 if (FAILED(rc))
11432 throw rc;
11433 continue;
11434 }
11435 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11436 }
11437
11438 /* If there are implicit attachments to delete, throw away the lock
11439 * map contents (which will unlock all media) since the medium
11440 * attachments will be rolled back. Below we need to completely
11441 * recreate the lock map anyway since it is infinitely complex to
11442 * do this incrementally (would need reconstructing each attachment
11443 * change, which would be extremely hairy). */
11444 if (implicitAtts.size() != 0)
11445 {
11446 ErrorInfoKeeper eik;
11447
11448 HRESULT rc1 = lockedMediaMap->Clear();
11449 AssertComRC(rc1);
11450 }
11451
11452 /* rollback hard disk changes */
11453 mMediaData.rollback();
11454
11455 MultiResult mrc(S_OK);
11456
11457 // Delete unused implicit diffs.
11458 if (implicitAtts.size() != 0)
11459 {
11460 alock.release();
11461
11462 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11463 it != implicitAtts.end();
11464 ++it)
11465 {
11466 // Remove medium associated with this attachment.
11467 ComObjPtr<MediumAttachment> pAtt = *it;
11468 Assert(pAtt);
11469 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11470 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11471 Assert(pMedium);
11472
11473 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11474 // continue on delete failure, just collect error messages
11475 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(), pMedium->i_getLocationFull().c_str() ));
11476 mrc = rc;
11477 }
11478
11479 alock.acquire();
11480
11481 /* if there is a VM recreate media lock map as mentioned above,
11482 * otherwise it is a waste of time and we leave things unlocked */
11483 if (aOnline)
11484 {
11485 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11486 /* must never be NULL, but better safe than sorry */
11487 if (!pMachine.isNull())
11488 {
11489 alock.release();
11490 rc = mData->mSession.mMachine->lockMedia();
11491 alock.acquire();
11492 if (FAILED(rc))
11493 throw rc;
11494 }
11495 }
11496 }
11497 }
11498 catch (HRESULT aRC) {rc = aRC;}
11499
11500 if (mData->mMachineState == MachineState_SettingUp)
11501 setMachineState(oldState);
11502
11503 /* unlock all hard disks we locked when there is no VM */
11504 if (!aOnline)
11505 {
11506 ErrorInfoKeeper eik;
11507
11508 HRESULT rc1 = lockedMediaMap->Clear();
11509 AssertComRC(rc1);
11510 }
11511
11512 return rc;
11513}
11514
11515
11516/**
11517 * Looks through the given list of media attachments for one with the given parameters
11518 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11519 * can be searched as well if needed.
11520 *
11521 * @param list
11522 * @param aControllerName
11523 * @param aControllerPort
11524 * @param aDevice
11525 * @return
11526 */
11527MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11528 IN_BSTR aControllerName,
11529 LONG aControllerPort,
11530 LONG aDevice)
11531{
11532 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11533 it != ll.end();
11534 ++it)
11535 {
11536 MediumAttachment *pAttach = *it;
11537 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11538 return pAttach;
11539 }
11540
11541 return NULL;
11542}
11543
11544/**
11545 * Looks through the given list of media attachments for one with the given parameters
11546 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11547 * can be searched as well if needed.
11548 *
11549 * @param list
11550 * @param aControllerName
11551 * @param aControllerPort
11552 * @param aDevice
11553 * @return
11554 */
11555MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11556 ComObjPtr<Medium> pMedium)
11557{
11558 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11559 it != ll.end();
11560 ++it)
11561 {
11562 MediumAttachment *pAttach = *it;
11563 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11564 if (pMediumThis == pMedium)
11565 return pAttach;
11566 }
11567
11568 return NULL;
11569}
11570
11571/**
11572 * Looks through the given list of media attachments for one with the given parameters
11573 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11574 * can be searched as well if needed.
11575 *
11576 * @param list
11577 * @param aControllerName
11578 * @param aControllerPort
11579 * @param aDevice
11580 * @return
11581 */
11582MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11583 Guid &id)
11584{
11585 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11586 it != ll.end();
11587 ++it)
11588 {
11589 MediumAttachment *pAttach = *it;
11590 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11591 if (pMediumThis->i_getId() == id)
11592 return pAttach;
11593 }
11594
11595 return NULL;
11596}
11597
11598/**
11599 * Main implementation for Machine::DetachDevice. This also gets called
11600 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11601 *
11602 * @param pAttach Medium attachment to detach.
11603 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11604 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11605 * @return
11606 */
11607HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11608 AutoWriteLock &writeLock,
11609 Snapshot *pSnapshot)
11610{
11611 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11612 DeviceType_T mediumType = pAttach->i_getType();
11613
11614 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11615
11616 if (pAttach->i_isImplicit())
11617 {
11618 /* attempt to implicitly delete the implicitly created diff */
11619
11620 /// @todo move the implicit flag from MediumAttachment to Medium
11621 /// and forbid any hard disk operation when it is implicit. Or maybe
11622 /// a special media state for it to make it even more simple.
11623
11624 Assert(mMediaData.isBackedUp());
11625
11626 /* will release the lock before the potentially lengthy operation, so
11627 * protect with the special state */
11628 MachineState_T oldState = mData->mMachineState;
11629 setMachineState(MachineState_SettingUp);
11630
11631 writeLock.release();
11632
11633 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11634 true /*aWait*/);
11635
11636 writeLock.acquire();
11637
11638 setMachineState(oldState);
11639
11640 if (FAILED(rc)) return rc;
11641 }
11642
11643 setModified(IsModified_Storage);
11644 mMediaData.backup();
11645 mMediaData->mAttachments.remove(pAttach);
11646
11647 if (!oldmedium.isNull())
11648 {
11649 // if this is from a snapshot, do not defer detachment to commitMedia()
11650 if (pSnapshot)
11651 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11652 // else if non-hard disk media, do not defer detachment to commitMedia() either
11653 else if (mediumType != DeviceType_HardDisk)
11654 oldmedium->i_removeBackReference(mData->mUuid);
11655 }
11656
11657 return S_OK;
11658}
11659
11660/**
11661 * Goes thru all media of the given list and
11662 *
11663 * 1) calls detachDevice() on each of them for this machine and
11664 * 2) adds all Medium objects found in the process to the given list,
11665 * depending on cleanupMode.
11666 *
11667 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11668 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11669 * media to the list.
11670 *
11671 * This gets called from Machine::Unregister, both for the actual Machine and
11672 * the SnapshotMachine objects that might be found in the snapshots.
11673 *
11674 * Requires caller and locking. The machine lock must be passed in because it
11675 * will be passed on to detachDevice which needs it for temporary unlocking.
11676 *
11677 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11678 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11679 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11680 * otherwise no media get added.
11681 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11682 * @return
11683 */
11684HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11685 Snapshot *pSnapshot,
11686 CleanupMode_T cleanupMode,
11687 MediaList &llMedia)
11688{
11689 Assert(isWriteLockOnCurrentThread());
11690
11691 HRESULT rc;
11692
11693 // make a temporary list because detachDevice invalidates iterators into
11694 // mMediaData->mAttachments
11695 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11696
11697 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11698 it != llAttachments2.end();
11699 ++it)
11700 {
11701 ComObjPtr<MediumAttachment> &pAttach = *it;
11702 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11703
11704 if (!pMedium.isNull())
11705 {
11706 AutoCaller mac(pMedium);
11707 if (FAILED(mac.rc())) return mac.rc();
11708 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11709 DeviceType_T devType = pMedium->i_getDeviceType();
11710 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11711 && devType == DeviceType_HardDisk)
11712 || (cleanupMode == CleanupMode_Full)
11713 )
11714 {
11715 llMedia.push_back(pMedium);
11716 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11717 /*
11718 * Search for medias which are not attached to any machine, but
11719 * in the chain to an attached disk. Mediums are only consided
11720 * if they are:
11721 * - have only one child
11722 * - no references to any machines
11723 * - are of normal medium type
11724 */
11725 while (!pParent.isNull())
11726 {
11727 AutoCaller mac1(pParent);
11728 if (FAILED(mac1.rc())) return mac1.rc();
11729 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11730 if (pParent->i_getChildren().size() == 1)
11731 {
11732 if ( pParent->i_getMachineBackRefCount() == 0
11733 && pParent->i_getType() == MediumType_Normal
11734 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11735 llMedia.push_back(pParent);
11736 }
11737 else
11738 break;
11739 pParent = pParent->i_getParent();
11740 }
11741 }
11742 }
11743
11744 // real machine: then we need to use the proper method
11745 rc = detachDevice(pAttach, writeLock, pSnapshot);
11746
11747 if (FAILED(rc))
11748 return rc;
11749 }
11750
11751 return S_OK;
11752}
11753
11754/**
11755 * Perform deferred hard disk detachments.
11756 *
11757 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11758 * backed up).
11759 *
11760 * If @a aOnline is @c true then this method will also unlock the old hard disks
11761 * for which the new implicit diffs were created and will lock these new diffs for
11762 * writing.
11763 *
11764 * @param aOnline Whether the VM was online prior to this operation.
11765 *
11766 * @note Locks this object for writing!
11767 */
11768void Machine::commitMedia(bool aOnline /*= false*/)
11769{
11770 AutoCaller autoCaller(this);
11771 AssertComRCReturnVoid(autoCaller.rc());
11772
11773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11774
11775 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11776
11777 HRESULT rc = S_OK;
11778
11779 /* no attach/detach operations -- nothing to do */
11780 if (!mMediaData.isBackedUp())
11781 return;
11782
11783 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11784 bool fMediaNeedsLocking = false;
11785
11786 /* enumerate new attachments */
11787 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11788 it != mMediaData->mAttachments.end();
11789 ++it)
11790 {
11791 MediumAttachment *pAttach = *it;
11792
11793 pAttach->i_commit();
11794
11795 Medium* pMedium = pAttach->i_getMedium();
11796 bool fImplicit = pAttach->i_isImplicit();
11797
11798 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11799 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11800 fImplicit));
11801
11802 /** @todo convert all this Machine-based voodoo to MediumAttachment
11803 * based commit logic. */
11804 if (fImplicit)
11805 {
11806 /* convert implicit attachment to normal */
11807 pAttach->i_setImplicit(false);
11808
11809 if ( aOnline
11810 && pMedium
11811 && pAttach->i_getType() == DeviceType_HardDisk
11812 )
11813 {
11814 ComObjPtr<Medium> parent = pMedium->i_getParent();
11815 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11816
11817 /* update the appropriate lock list */
11818 MediumLockList *pMediumLockList;
11819 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11820 AssertComRC(rc);
11821 if (pMediumLockList)
11822 {
11823 /* unlock if there's a need to change the locking */
11824 if (!fMediaNeedsLocking)
11825 {
11826 rc = mData->mSession.mLockedMedia.Unlock();
11827 AssertComRC(rc);
11828 fMediaNeedsLocking = true;
11829 }
11830 rc = pMediumLockList->Update(parent, false);
11831 AssertComRC(rc);
11832 rc = pMediumLockList->Append(pMedium, true);
11833 AssertComRC(rc);
11834 }
11835 }
11836
11837 continue;
11838 }
11839
11840 if (pMedium)
11841 {
11842 /* was this medium attached before? */
11843 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11844 oldIt != oldAtts.end();
11845 ++oldIt)
11846 {
11847 MediumAttachment *pOldAttach = *oldIt;
11848 if (pOldAttach->i_getMedium() == pMedium)
11849 {
11850 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11851
11852 /* yes: remove from old to avoid de-association */
11853 oldAtts.erase(oldIt);
11854 break;
11855 }
11856 }
11857 }
11858 }
11859
11860 /* enumerate remaining old attachments and de-associate from the
11861 * current machine state */
11862 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11863 it != oldAtts.end();
11864 ++it)
11865 {
11866 MediumAttachment *pAttach = *it;
11867 Medium* pMedium = pAttach->i_getMedium();
11868
11869 /* Detach only hard disks, since DVD/floppy media is detached
11870 * instantly in MountMedium. */
11871 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11872 {
11873 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11874
11875 /* now de-associate from the current machine state */
11876 rc = pMedium->i_removeBackReference(mData->mUuid);
11877 AssertComRC(rc);
11878
11879 if (aOnline)
11880 {
11881 /* unlock since medium is not used anymore */
11882 MediumLockList *pMediumLockList;
11883 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11884 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11885 {
11886 /* this happens for online snapshots, there the attachment
11887 * is changing, but only to a diff image created under
11888 * the old one, so there is no separate lock list */
11889 Assert(!pMediumLockList);
11890 }
11891 else
11892 {
11893 AssertComRC(rc);
11894 if (pMediumLockList)
11895 {
11896 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11897 AssertComRC(rc);
11898 }
11899 }
11900 }
11901 }
11902 }
11903
11904 /* take media locks again so that the locking state is consistent */
11905 if (fMediaNeedsLocking)
11906 {
11907 Assert(aOnline);
11908 rc = mData->mSession.mLockedMedia.Lock();
11909 AssertComRC(rc);
11910 }
11911
11912 /* commit the hard disk changes */
11913 mMediaData.commit();
11914
11915 if (isSessionMachine())
11916 {
11917 /*
11918 * Update the parent machine to point to the new owner.
11919 * This is necessary because the stored parent will point to the
11920 * session machine otherwise and cause crashes or errors later
11921 * when the session machine gets invalid.
11922 */
11923 /** @todo Change the MediumAttachment class to behave like any other
11924 * class in this regard by creating peer MediumAttachment
11925 * objects for session machines and share the data with the peer
11926 * machine.
11927 */
11928 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11929 it != mMediaData->mAttachments.end();
11930 ++it)
11931 {
11932 (*it)->i_updateParentMachine(mPeer);
11933 }
11934
11935 /* attach new data to the primary machine and reshare it */
11936 mPeer->mMediaData.attach(mMediaData);
11937 }
11938
11939 return;
11940}
11941
11942/**
11943 * Perform deferred deletion of implicitly created diffs.
11944 *
11945 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11946 * backed up).
11947 *
11948 * @note Locks this object for writing!
11949 */
11950void Machine::rollbackMedia()
11951{
11952 AutoCaller autoCaller(this);
11953 AssertComRCReturnVoid(autoCaller.rc());
11954
11955 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11956 LogFlowThisFunc(("Entering rollbackMedia\n"));
11957
11958 HRESULT rc = S_OK;
11959
11960 /* no attach/detach operations -- nothing to do */
11961 if (!mMediaData.isBackedUp())
11962 return;
11963
11964 /* enumerate new attachments */
11965 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11966 it != mMediaData->mAttachments.end();
11967 ++it)
11968 {
11969 MediumAttachment *pAttach = *it;
11970 /* Fix up the backrefs for DVD/floppy media. */
11971 if (pAttach->i_getType() != DeviceType_HardDisk)
11972 {
11973 Medium* pMedium = pAttach->i_getMedium();
11974 if (pMedium)
11975 {
11976 rc = pMedium->i_removeBackReference(mData->mUuid);
11977 AssertComRC(rc);
11978 }
11979 }
11980
11981 (*it)->i_rollback();
11982
11983 pAttach = *it;
11984 /* Fix up the backrefs for DVD/floppy media. */
11985 if (pAttach->i_getType() != DeviceType_HardDisk)
11986 {
11987 Medium* pMedium = pAttach->i_getMedium();
11988 if (pMedium)
11989 {
11990 rc = pMedium->i_addBackReference(mData->mUuid);
11991 AssertComRC(rc);
11992 }
11993 }
11994 }
11995
11996 /** @todo convert all this Machine-based voodoo to MediumAttachment
11997 * based rollback logic. */
11998 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11999
12000 return;
12001}
12002
12003/**
12004 * Returns true if the settings file is located in the directory named exactly
12005 * as the machine; this means, among other things, that the machine directory
12006 * should be auto-renamed.
12007 *
12008 * @param aSettingsDir if not NULL, the full machine settings file directory
12009 * name will be assigned there.
12010 *
12011 * @note Doesn't lock anything.
12012 * @note Not thread safe (must be called from this object's lock).
12013 */
12014bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12015{
12016 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12017 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12018 if (aSettingsDir)
12019 *aSettingsDir = strMachineDirName;
12020 strMachineDirName.stripPath(); // vmname
12021 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12022 strConfigFileOnly.stripPath() // vmname.vbox
12023 .stripSuffix(); // vmname
12024 /** @todo hack, make somehow use of ComposeMachineFilename */
12025 if (mUserData->s.fDirectoryIncludesUUID)
12026 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
12027
12028 AssertReturn(!strMachineDirName.isEmpty(), false);
12029 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12030
12031 return strMachineDirName == strConfigFileOnly;
12032}
12033
12034/**
12035 * Discards all changes to machine settings.
12036 *
12037 * @param aNotify Whether to notify the direct session about changes or not.
12038 *
12039 * @note Locks objects for writing!
12040 */
12041void Machine::rollback(bool aNotify)
12042{
12043 AutoCaller autoCaller(this);
12044 AssertComRCReturn(autoCaller.rc(), (void)0);
12045
12046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12047
12048 if (!mStorageControllers.isNull())
12049 {
12050 if (mStorageControllers.isBackedUp())
12051 {
12052 /* unitialize all new devices (absent in the backed up list). */
12053 StorageControllerList::const_iterator it = mStorageControllers->begin();
12054 StorageControllerList *backedList = mStorageControllers.backedUpData();
12055 while (it != mStorageControllers->end())
12056 {
12057 if ( std::find(backedList->begin(), backedList->end(), *it)
12058 == backedList->end()
12059 )
12060 {
12061 (*it)->uninit();
12062 }
12063 ++it;
12064 }
12065
12066 /* restore the list */
12067 mStorageControllers.rollback();
12068 }
12069
12070 /* rollback any changes to devices after restoring the list */
12071 if (mData->flModifications & IsModified_Storage)
12072 {
12073 StorageControllerList::const_iterator it = mStorageControllers->begin();
12074 while (it != mStorageControllers->end())
12075 {
12076 (*it)->i_rollback();
12077 ++it;
12078 }
12079 }
12080 }
12081
12082 if (!mUSBControllers.isNull())
12083 {
12084 if (mUSBControllers.isBackedUp())
12085 {
12086 /* unitialize all new devices (absent in the backed up list). */
12087 USBControllerList::const_iterator it = mUSBControllers->begin();
12088 USBControllerList *backedList = mUSBControllers.backedUpData();
12089 while (it != mUSBControllers->end())
12090 {
12091 if ( std::find(backedList->begin(), backedList->end(), *it)
12092 == backedList->end()
12093 )
12094 {
12095 (*it)->uninit();
12096 }
12097 ++it;
12098 }
12099
12100 /* restore the list */
12101 mUSBControllers.rollback();
12102 }
12103
12104 /* rollback any changes to devices after restoring the list */
12105 if (mData->flModifications & IsModified_USB)
12106 {
12107 USBControllerList::const_iterator it = mUSBControllers->begin();
12108 while (it != mUSBControllers->end())
12109 {
12110 (*it)->i_rollback();
12111 ++it;
12112 }
12113 }
12114 }
12115
12116 mUserData.rollback();
12117
12118 mHWData.rollback();
12119
12120 if (mData->flModifications & IsModified_Storage)
12121 rollbackMedia();
12122
12123 if (mBIOSSettings)
12124 mBIOSSettings->i_rollback();
12125
12126 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12127 mVRDEServer->i_rollback();
12128
12129 if (mAudioAdapter)
12130 mAudioAdapter->i_rollback();
12131
12132 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12133 mUSBDeviceFilters->i_rollback();
12134
12135 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12136 mBandwidthControl->i_rollback();
12137
12138 if (!mHWData.isNull())
12139 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12140 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12141 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12142 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12143
12144 if (mData->flModifications & IsModified_NetworkAdapters)
12145 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12146 if ( mNetworkAdapters[slot]
12147 && mNetworkAdapters[slot]->i_isModified())
12148 {
12149 mNetworkAdapters[slot]->i_rollback();
12150 networkAdapters[slot] = mNetworkAdapters[slot];
12151 }
12152
12153 if (mData->flModifications & IsModified_SerialPorts)
12154 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12155 if ( mSerialPorts[slot]
12156 && mSerialPorts[slot]->i_isModified())
12157 {
12158 mSerialPorts[slot]->i_rollback();
12159 serialPorts[slot] = mSerialPorts[slot];
12160 }
12161
12162 if (mData->flModifications & IsModified_ParallelPorts)
12163 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12164 if ( mParallelPorts[slot]
12165 && mParallelPorts[slot]->i_isModified())
12166 {
12167 mParallelPorts[slot]->i_rollback();
12168 parallelPorts[slot] = mParallelPorts[slot];
12169 }
12170
12171 if (aNotify)
12172 {
12173 /* inform the direct session about changes */
12174
12175 ComObjPtr<Machine> that = this;
12176 uint32_t flModifications = mData->flModifications;
12177 alock.release();
12178
12179 if (flModifications & IsModified_SharedFolders)
12180 that->onSharedFolderChange();
12181
12182 if (flModifications & IsModified_VRDEServer)
12183 that->onVRDEServerChange(/* aRestart */ TRUE);
12184 if (flModifications & IsModified_USB)
12185 that->onUSBControllerChange();
12186
12187 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12188 if (networkAdapters[slot])
12189 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12190 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12191 if (serialPorts[slot])
12192 that->onSerialPortChange(serialPorts[slot]);
12193 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12194 if (parallelPorts[slot])
12195 that->onParallelPortChange(parallelPorts[slot]);
12196
12197 if (flModifications & IsModified_Storage)
12198 that->onStorageControllerChange();
12199
12200#if 0
12201 if (flModifications & IsModified_BandwidthControl)
12202 that->onBandwidthControlChange();
12203#endif
12204 }
12205}
12206
12207/**
12208 * Commits all the changes to machine settings.
12209 *
12210 * Note that this operation is supposed to never fail.
12211 *
12212 * @note Locks this object and children for writing.
12213 */
12214void Machine::commit()
12215{
12216 AutoCaller autoCaller(this);
12217 AssertComRCReturnVoid(autoCaller.rc());
12218
12219 AutoCaller peerCaller(mPeer);
12220 AssertComRCReturnVoid(peerCaller.rc());
12221
12222 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12223
12224 /*
12225 * use safe commit to ensure Snapshot machines (that share mUserData)
12226 * will still refer to a valid memory location
12227 */
12228 mUserData.commitCopy();
12229
12230 mHWData.commit();
12231
12232 if (mMediaData.isBackedUp())
12233 commitMedia(Global::IsOnline(mData->mMachineState));
12234
12235 mBIOSSettings->i_commit();
12236 mVRDEServer->i_commit();
12237 mAudioAdapter->i_commit();
12238 mUSBDeviceFilters->i_commit();
12239 mBandwidthControl->i_commit();
12240
12241 /* Since mNetworkAdapters is a list which might have been changed (resized)
12242 * without using the Backupable<> template we need to handle the copying
12243 * of the list entries manually, including the creation of peers for the
12244 * new objects. */
12245 bool commitNetworkAdapters = false;
12246 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12247 if (mPeer)
12248 {
12249 /* commit everything, even the ones which will go away */
12250 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12251 mNetworkAdapters[slot]->i_commit();
12252 /* copy over the new entries, creating a peer and uninit the original */
12253 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12254 for (size_t slot = 0; slot < newSize; slot++)
12255 {
12256 /* look if this adapter has a peer device */
12257 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12258 if (!peer)
12259 {
12260 /* no peer means the adapter is a newly created one;
12261 * create a peer owning data this data share it with */
12262 peer.createObject();
12263 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12264 }
12265 mPeer->mNetworkAdapters[slot] = peer;
12266 }
12267 /* uninit any no longer needed network adapters */
12268 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12269 mNetworkAdapters[slot]->uninit();
12270 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12271 {
12272 if (mPeer->mNetworkAdapters[slot])
12273 mPeer->mNetworkAdapters[slot]->uninit();
12274 }
12275 /* Keep the original network adapter count until this point, so that
12276 * discarding a chipset type change will not lose settings. */
12277 mNetworkAdapters.resize(newSize);
12278 mPeer->mNetworkAdapters.resize(newSize);
12279 }
12280 else
12281 {
12282 /* we have no peer (our parent is the newly created machine);
12283 * just commit changes to the network adapters */
12284 commitNetworkAdapters = true;
12285 }
12286 if (commitNetworkAdapters)
12287 {
12288 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12289 mNetworkAdapters[slot]->i_commit();
12290 }
12291
12292 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12293 mSerialPorts[slot]->i_commit();
12294 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12295 mParallelPorts[slot]->i_commit();
12296
12297 bool commitStorageControllers = false;
12298
12299 if (mStorageControllers.isBackedUp())
12300 {
12301 mStorageControllers.commit();
12302
12303 if (mPeer)
12304 {
12305 /* Commit all changes to new controllers (this will reshare data with
12306 * peers for those who have peers) */
12307 StorageControllerList *newList = new StorageControllerList();
12308 StorageControllerList::const_iterator it = mStorageControllers->begin();
12309 while (it != mStorageControllers->end())
12310 {
12311 (*it)->i_commit();
12312
12313 /* look if this controller has a peer device */
12314 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12315 if (!peer)
12316 {
12317 /* no peer means the device is a newly created one;
12318 * create a peer owning data this device share it with */
12319 peer.createObject();
12320 peer->init(mPeer, *it, true /* aReshare */);
12321 }
12322 else
12323 {
12324 /* remove peer from the old list */
12325 mPeer->mStorageControllers->remove(peer);
12326 }
12327 /* and add it to the new list */
12328 newList->push_back(peer);
12329
12330 ++it;
12331 }
12332
12333 /* uninit old peer's controllers that are left */
12334 it = mPeer->mStorageControllers->begin();
12335 while (it != mPeer->mStorageControllers->end())
12336 {
12337 (*it)->uninit();
12338 ++it;
12339 }
12340
12341 /* attach new list of controllers to our peer */
12342 mPeer->mStorageControllers.attach(newList);
12343 }
12344 else
12345 {
12346 /* we have no peer (our parent is the newly created machine);
12347 * just commit changes to devices */
12348 commitStorageControllers = true;
12349 }
12350 }
12351 else
12352 {
12353 /* the list of controllers itself is not changed,
12354 * just commit changes to controllers themselves */
12355 commitStorageControllers = true;
12356 }
12357
12358 if (commitStorageControllers)
12359 {
12360 StorageControllerList::const_iterator it = mStorageControllers->begin();
12361 while (it != mStorageControllers->end())
12362 {
12363 (*it)->i_commit();
12364 ++it;
12365 }
12366 }
12367
12368 bool commitUSBControllers = false;
12369
12370 if (mUSBControllers.isBackedUp())
12371 {
12372 mUSBControllers.commit();
12373
12374 if (mPeer)
12375 {
12376 /* Commit all changes to new controllers (this will reshare data with
12377 * peers for those who have peers) */
12378 USBControllerList *newList = new USBControllerList();
12379 USBControllerList::const_iterator it = mUSBControllers->begin();
12380 while (it != mUSBControllers->end())
12381 {
12382 (*it)->i_commit();
12383
12384 /* look if this controller has a peer device */
12385 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12386 if (!peer)
12387 {
12388 /* no peer means the device is a newly created one;
12389 * create a peer owning data this device share it with */
12390 peer.createObject();
12391 peer->init(mPeer, *it, true /* aReshare */);
12392 }
12393 else
12394 {
12395 /* remove peer from the old list */
12396 mPeer->mUSBControllers->remove(peer);
12397 }
12398 /* and add it to the new list */
12399 newList->push_back(peer);
12400
12401 ++it;
12402 }
12403
12404 /* uninit old peer's controllers that are left */
12405 it = mPeer->mUSBControllers->begin();
12406 while (it != mPeer->mUSBControllers->end())
12407 {
12408 (*it)->uninit();
12409 ++it;
12410 }
12411
12412 /* attach new list of controllers to our peer */
12413 mPeer->mUSBControllers.attach(newList);
12414 }
12415 else
12416 {
12417 /* we have no peer (our parent is the newly created machine);
12418 * just commit changes to devices */
12419 commitUSBControllers = true;
12420 }
12421 }
12422 else
12423 {
12424 /* the list of controllers itself is not changed,
12425 * just commit changes to controllers themselves */
12426 commitUSBControllers = true;
12427 }
12428
12429 if (commitUSBControllers)
12430 {
12431 USBControllerList::const_iterator it = mUSBControllers->begin();
12432 while (it != mUSBControllers->end())
12433 {
12434 (*it)->i_commit();
12435 ++it;
12436 }
12437 }
12438
12439 if (isSessionMachine())
12440 {
12441 /* attach new data to the primary machine and reshare it */
12442 mPeer->mUserData.attach(mUserData);
12443 mPeer->mHWData.attach(mHWData);
12444 /* mMediaData is reshared by fixupMedia */
12445 // mPeer->mMediaData.attach(mMediaData);
12446 Assert(mPeer->mMediaData.data() == mMediaData.data());
12447 }
12448}
12449
12450/**
12451 * Copies all the hardware data from the given machine.
12452 *
12453 * Currently, only called when the VM is being restored from a snapshot. In
12454 * particular, this implies that the VM is not running during this method's
12455 * call.
12456 *
12457 * @note This method must be called from under this object's lock.
12458 *
12459 * @note This method doesn't call #commit(), so all data remains backed up and
12460 * unsaved.
12461 */
12462void Machine::copyFrom(Machine *aThat)
12463{
12464 AssertReturnVoid(!isSnapshotMachine());
12465 AssertReturnVoid(aThat->isSnapshotMachine());
12466
12467 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12468
12469 mHWData.assignCopy(aThat->mHWData);
12470
12471 // create copies of all shared folders (mHWData after attaching a copy
12472 // contains just references to original objects)
12473 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12474 it != mHWData->mSharedFolders.end();
12475 ++it)
12476 {
12477 ComObjPtr<SharedFolder> folder;
12478 folder.createObject();
12479 HRESULT rc = folder->initCopy(getMachine(), *it);
12480 AssertComRC(rc);
12481 *it = folder;
12482 }
12483
12484 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12485 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12486 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12487 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12488 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12489
12490 /* create private copies of all controllers */
12491 mStorageControllers.backup();
12492 mStorageControllers->clear();
12493 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12494 it != aThat->mStorageControllers->end();
12495 ++it)
12496 {
12497 ComObjPtr<StorageController> ctrl;
12498 ctrl.createObject();
12499 ctrl->initCopy(this, *it);
12500 mStorageControllers->push_back(ctrl);
12501 }
12502
12503 /* create private copies of all USB controllers */
12504 mUSBControllers.backup();
12505 mUSBControllers->clear();
12506 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12507 it != aThat->mUSBControllers->end();
12508 ++it)
12509 {
12510 ComObjPtr<USBController> ctrl;
12511 ctrl.createObject();
12512 ctrl->initCopy(this, *it);
12513 mUSBControllers->push_back(ctrl);
12514 }
12515
12516 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12517 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12518 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12519 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12520 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12521 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12522 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12523}
12524
12525/**
12526 * Returns whether the given storage controller is hotplug capable.
12527 *
12528 * @returns true if the controller supports hotplugging
12529 * false otherwise.
12530 * @param enmCtrlType The controller type to check for.
12531 */
12532bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12533{
12534 ComPtr<ISystemProperties> systemProperties;
12535 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12536 if (FAILED(rc))
12537 return false;
12538
12539 BOOL aHotplugCapable = FALSE;
12540 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12541
12542 return RT_BOOL(aHotplugCapable);
12543}
12544
12545#ifdef VBOX_WITH_RESOURCE_USAGE_API
12546
12547void Machine::getDiskList(MediaList &list)
12548{
12549 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12550 it != mMediaData->mAttachments.end();
12551 ++it)
12552 {
12553 MediumAttachment* pAttach = *it;
12554 /* just in case */
12555 AssertStmt(pAttach, continue);
12556
12557 AutoCaller localAutoCallerA(pAttach);
12558 if (FAILED(localAutoCallerA.rc())) continue;
12559
12560 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12561
12562 if (pAttach->i_getType() == DeviceType_HardDisk)
12563 list.push_back(pAttach->i_getMedium());
12564 }
12565}
12566
12567void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12568{
12569 AssertReturnVoid(isWriteLockOnCurrentThread());
12570 AssertPtrReturnVoid(aCollector);
12571
12572 pm::CollectorHAL *hal = aCollector->getHAL();
12573 /* Create sub metrics */
12574 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12575 "Percentage of processor time spent in user mode by the VM process.");
12576 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12577 "Percentage of processor time spent in kernel mode by the VM process.");
12578 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12579 "Size of resident portion of VM process in memory.");
12580 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12581 "Actual size of all VM disks combined.");
12582 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12583 "Network receive rate.");
12584 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12585 "Network transmit rate.");
12586 /* Create and register base metrics */
12587 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12588 cpuLoadUser, cpuLoadKernel);
12589 aCollector->registerBaseMetric(cpuLoad);
12590 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12591 ramUsageUsed);
12592 aCollector->registerBaseMetric(ramUsage);
12593 MediaList disks;
12594 getDiskList(disks);
12595 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12596 diskUsageUsed);
12597 aCollector->registerBaseMetric(diskUsage);
12598
12599 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12600 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12601 new pm::AggregateAvg()));
12602 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12603 new pm::AggregateMin()));
12604 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12605 new pm::AggregateMax()));
12606 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12607 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12608 new pm::AggregateAvg()));
12609 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12610 new pm::AggregateMin()));
12611 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12612 new pm::AggregateMax()));
12613
12614 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12615 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12616 new pm::AggregateAvg()));
12617 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12618 new pm::AggregateMin()));
12619 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12620 new pm::AggregateMax()));
12621
12622 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12623 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12624 new pm::AggregateAvg()));
12625 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12626 new pm::AggregateMin()));
12627 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12628 new pm::AggregateMax()));
12629
12630
12631 /* Guest metrics collector */
12632 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12633 aCollector->registerGuest(mCollectorGuest);
12634 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12635 this, __PRETTY_FUNCTION__, mCollectorGuest));
12636
12637 /* Create sub metrics */
12638 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12639 "Percentage of processor time spent in user mode as seen by the guest.");
12640 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12641 "Percentage of processor time spent in kernel mode as seen by the guest.");
12642 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12643 "Percentage of processor time spent idling as seen by the guest.");
12644
12645 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12646 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12647 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12648 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12649 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12650 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12651
12652 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12653
12654 /* Create and register base metrics */
12655 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12656 machineNetRx, machineNetTx);
12657 aCollector->registerBaseMetric(machineNetRate);
12658
12659 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12660 guestLoadUser, guestLoadKernel, guestLoadIdle);
12661 aCollector->registerBaseMetric(guestCpuLoad);
12662
12663 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12664 guestMemTotal, guestMemFree,
12665 guestMemBalloon, guestMemShared,
12666 guestMemCache, guestPagedTotal);
12667 aCollector->registerBaseMetric(guestCpuMem);
12668
12669 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12670 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12671 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12672 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12673
12674 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12675 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12676 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12677 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12678
12679 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12680 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12681 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12682 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12683
12684 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12685 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12686 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12687 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12688
12689 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12690 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12691 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12692 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12693
12694 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12695 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12696 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12697 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12698
12699 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12700 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12701 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12702 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12703
12704 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12705 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12706 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12707 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12708
12709 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12710 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12711 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12712 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12713
12714 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12715 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12716 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12717 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12718
12719 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12720 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12721 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12722 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12723}
12724
12725void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12726{
12727 AssertReturnVoid(isWriteLockOnCurrentThread());
12728
12729 if (aCollector)
12730 {
12731 aCollector->unregisterMetricsFor(aMachine);
12732 aCollector->unregisterBaseMetricsFor(aMachine);
12733 }
12734}
12735
12736#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12737
12738
12739////////////////////////////////////////////////////////////////////////////////
12740
12741DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12742
12743HRESULT SessionMachine::FinalConstruct()
12744{
12745 LogFlowThisFunc(("\n"));
12746
12747 mClientToken = NULL;
12748
12749 return BaseFinalConstruct();
12750}
12751
12752void SessionMachine::FinalRelease()
12753{
12754 LogFlowThisFunc(("\n"));
12755
12756 Assert(!mClientToken);
12757 /* paranoia, should not hang around any more */
12758 if (mClientToken)
12759 {
12760 delete mClientToken;
12761 mClientToken = NULL;
12762 }
12763
12764 uninit(Uninit::Unexpected);
12765
12766 BaseFinalRelease();
12767}
12768
12769/**
12770 * @note Must be called only by Machine::LockMachine() from its own write lock.
12771 */
12772HRESULT SessionMachine::init(Machine *aMachine)
12773{
12774 LogFlowThisFuncEnter();
12775 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12776
12777 AssertReturn(aMachine, E_INVALIDARG);
12778
12779 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12780
12781 /* Enclose the state transition NotReady->InInit->Ready */
12782 AutoInitSpan autoInitSpan(this);
12783 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12784
12785 HRESULT rc = S_OK;
12786
12787 /* create the machine client token */
12788 try
12789 {
12790 mClientToken = new ClientToken(aMachine, this);
12791 if (!mClientToken->isReady())
12792 {
12793 delete mClientToken;
12794 mClientToken = NULL;
12795 rc = E_FAIL;
12796 }
12797 }
12798 catch (std::bad_alloc &)
12799 {
12800 rc = E_OUTOFMEMORY;
12801 }
12802 if (FAILED(rc))
12803 return rc;
12804
12805 /* memorize the peer Machine */
12806 unconst(mPeer) = aMachine;
12807 /* share the parent pointer */
12808 unconst(mParent) = aMachine->mParent;
12809
12810 /* take the pointers to data to share */
12811 mData.share(aMachine->mData);
12812 mSSData.share(aMachine->mSSData);
12813
12814 mUserData.share(aMachine->mUserData);
12815 mHWData.share(aMachine->mHWData);
12816 mMediaData.share(aMachine->mMediaData);
12817
12818 mStorageControllers.allocate();
12819 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12820 it != aMachine->mStorageControllers->end();
12821 ++it)
12822 {
12823 ComObjPtr<StorageController> ctl;
12824 ctl.createObject();
12825 ctl->init(this, *it);
12826 mStorageControllers->push_back(ctl);
12827 }
12828
12829 mUSBControllers.allocate();
12830 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12831 it != aMachine->mUSBControllers->end();
12832 ++it)
12833 {
12834 ComObjPtr<USBController> ctl;
12835 ctl.createObject();
12836 ctl->init(this, *it);
12837 mUSBControllers->push_back(ctl);
12838 }
12839
12840 unconst(mBIOSSettings).createObject();
12841 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12842 /* create another VRDEServer object that will be mutable */
12843 unconst(mVRDEServer).createObject();
12844 mVRDEServer->init(this, aMachine->mVRDEServer);
12845 /* create another audio adapter object that will be mutable */
12846 unconst(mAudioAdapter).createObject();
12847 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12848 /* create a list of serial ports that will be mutable */
12849 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12850 {
12851 unconst(mSerialPorts[slot]).createObject();
12852 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12853 }
12854 /* create a list of parallel ports that will be mutable */
12855 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12856 {
12857 unconst(mParallelPorts[slot]).createObject();
12858 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12859 }
12860
12861 /* create another USB device filters object that will be mutable */
12862 unconst(mUSBDeviceFilters).createObject();
12863 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12864
12865 /* create a list of network adapters that will be mutable */
12866 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12867 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12868 {
12869 unconst(mNetworkAdapters[slot]).createObject();
12870 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12871
12872 NetworkAttachmentType_T type;
12873 HRESULT hrc;
12874 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12875 if ( SUCCEEDED(hrc)
12876 && type == NetworkAttachmentType_NATNetwork)
12877 {
12878 Bstr name;
12879 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12880 if (SUCCEEDED(hrc))
12881 {
12882 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12883 mUserData->s.strName.c_str(), name.raw()));
12884 aMachine->lockHandle()->unlockWrite();
12885 mParent->i_natNetworkRefInc(name.raw());
12886#ifdef RT_LOCK_STRICT
12887 aMachine->lockHandle()->lockWrite(RT_SRC_POS);
12888#else
12889 aMachine->lockHandle()->lockWrite();
12890#endif
12891 }
12892 }
12893 }
12894
12895 /* create another bandwidth control object that will be mutable */
12896 unconst(mBandwidthControl).createObject();
12897 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12898
12899 /* default is to delete saved state on Saved -> PoweredOff transition */
12900 mRemoveSavedState = true;
12901
12902 /* Confirm a successful initialization when it's the case */
12903 autoInitSpan.setSucceeded();
12904
12905 LogFlowThisFuncLeave();
12906 return rc;
12907}
12908
12909/**
12910 * Uninitializes this session object. If the reason is other than
12911 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12912 * or the client watcher code.
12913 *
12914 * @param aReason uninitialization reason
12915 *
12916 * @note Locks mParent + this object for writing.
12917 */
12918void SessionMachine::uninit(Uninit::Reason aReason)
12919{
12920 LogFlowThisFuncEnter();
12921 LogFlowThisFunc(("reason=%d\n", aReason));
12922
12923 /*
12924 * Strongly reference ourselves to prevent this object deletion after
12925 * mData->mSession.mMachine.setNull() below (which can release the last
12926 * reference and call the destructor). Important: this must be done before
12927 * accessing any members (and before AutoUninitSpan that does it as well).
12928 * This self reference will be released as the very last step on return.
12929 */
12930 ComObjPtr<SessionMachine> selfRef = this;
12931
12932 /* Enclose the state transition Ready->InUninit->NotReady */
12933 AutoUninitSpan autoUninitSpan(this);
12934 if (autoUninitSpan.uninitDone())
12935 {
12936 LogFlowThisFunc(("Already uninitialized\n"));
12937 LogFlowThisFuncLeave();
12938 return;
12939 }
12940
12941 if (autoUninitSpan.initFailed())
12942 {
12943 /* We've been called by init() because it's failed. It's not really
12944 * necessary (nor it's safe) to perform the regular uninit sequence
12945 * below, the following is enough.
12946 */
12947 LogFlowThisFunc(("Initialization failed.\n"));
12948 /* destroy the machine client token */
12949 if (mClientToken)
12950 {
12951 delete mClientToken;
12952 mClientToken = NULL;
12953 }
12954 uninitDataAndChildObjects();
12955 mData.free();
12956 unconst(mParent) = NULL;
12957 unconst(mPeer) = NULL;
12958 LogFlowThisFuncLeave();
12959 return;
12960 }
12961
12962 MachineState_T lastState;
12963 {
12964 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12965 lastState = mData->mMachineState;
12966 }
12967 NOREF(lastState);
12968
12969#ifdef VBOX_WITH_USB
12970 // release all captured USB devices, but do this before requesting the locks below
12971 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12972 {
12973 /* Console::captureUSBDevices() is called in the VM process only after
12974 * setting the machine state to Starting or Restoring.
12975 * Console::detachAllUSBDevices() will be called upon successful
12976 * termination. So, we need to release USB devices only if there was
12977 * an abnormal termination of a running VM.
12978 *
12979 * This is identical to SessionMachine::DetachAllUSBDevices except
12980 * for the aAbnormal argument. */
12981 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12982 AssertComRC(rc);
12983 NOREF(rc);
12984
12985 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12986 if (service)
12987 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12988 }
12989#endif /* VBOX_WITH_USB */
12990
12991 // we need to lock this object in uninit() because the lock is shared
12992 // with mPeer (as well as data we modify below). mParent lock is needed
12993 // by several calls to it, and USB needs host lock.
12994 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12995
12996#ifdef VBOX_WITH_RESOURCE_USAGE_API
12997 /*
12998 * It is safe to call Machine::unregisterMetrics() here because
12999 * PerformanceCollector::samplerCallback no longer accesses guest methods
13000 * holding the lock.
13001 */
13002 unregisterMetrics(mParent->i_performanceCollector(), mPeer);
13003 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13004 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
13005 this, __PRETTY_FUNCTION__, mCollectorGuest));
13006 if (mCollectorGuest)
13007 {
13008 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
13009 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13010 mCollectorGuest = NULL;
13011 }
13012#endif
13013
13014 if (aReason == Uninit::Abnormal)
13015 {
13016 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
13017 Global::IsOnlineOrTransient(lastState)));
13018
13019 /* reset the state to Aborted */
13020 if (mData->mMachineState != MachineState_Aborted)
13021 setMachineState(MachineState_Aborted);
13022 }
13023
13024 // any machine settings modified?
13025 if (mData->flModifications)
13026 {
13027 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
13028 rollback(false /* aNotify */);
13029 }
13030
13031 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
13032 || !mConsoleTaskData.mSnapshot);
13033 if (!mConsoleTaskData.strStateFilePath.isEmpty())
13034 {
13035 LogWarningThisFunc(("canceling failed save state request!\n"));
13036 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
13037 }
13038 else if (!mConsoleTaskData.mSnapshot.isNull())
13039 {
13040 LogWarningThisFunc(("canceling untaken snapshot!\n"));
13041
13042 /* delete all differencing hard disks created (this will also attach
13043 * their parents back by rolling back mMediaData) */
13044 rollbackMedia();
13045
13046 // delete the saved state file (it might have been already created)
13047 // AFTER killing the snapshot so that releaseSavedStateFile() won't
13048 // think it's still in use
13049 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->i_getStateFilePath();
13050 mConsoleTaskData.mSnapshot->uninit();
13051 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
13052 }
13053
13054 mData->mSession.mPID = NIL_RTPROCESS;
13055
13056 if (aReason == Uninit::Unexpected)
13057 {
13058 /* Uninitialization didn't come from #checkForDeath(), so tell the
13059 * client watcher thread to update the set of machines that have open
13060 * sessions. */
13061 mParent->i_updateClientWatcher();
13062 }
13063
13064 /* uninitialize all remote controls */
13065 if (mData->mSession.mRemoteControls.size())
13066 {
13067 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13068 mData->mSession.mRemoteControls.size()));
13069
13070 Data::Session::RemoteControlList::iterator it =
13071 mData->mSession.mRemoteControls.begin();
13072 while (it != mData->mSession.mRemoteControls.end())
13073 {
13074 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13075 HRESULT rc = (*it)->Uninitialize();
13076 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13077 if (FAILED(rc))
13078 LogWarningThisFunc(("Forgot to close the remote session?\n"));
13079 ++it;
13080 }
13081 mData->mSession.mRemoteControls.clear();
13082 }
13083
13084 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
13085 {
13086 NetworkAttachmentType_T type;
13087 HRESULT hrc;
13088
13089 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13090 if ( SUCCEEDED(hrc)
13091 && type == NetworkAttachmentType_NATNetwork)
13092 {
13093 Bstr name;
13094 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13095 if (SUCCEEDED(hrc))
13096 {
13097 multilock.release();
13098 LogRel(("VM '%s' stops using NAT network '%ls'\n",
13099 mUserData->s.strName.c_str(), name.raw()));
13100 mParent->i_natNetworkRefDec(name.raw());
13101 multilock.acquire();
13102 }
13103 }
13104 }
13105
13106 /*
13107 * An expected uninitialization can come only from #checkForDeath().
13108 * Otherwise it means that something's gone really wrong (for example,
13109 * the Session implementation has released the VirtualBox reference
13110 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13111 * etc). However, it's also possible, that the client releases the IPC
13112 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13113 * but the VirtualBox release event comes first to the server process.
13114 * This case is practically possible, so we should not assert on an
13115 * unexpected uninit, just log a warning.
13116 */
13117
13118 if ((aReason == Uninit::Unexpected))
13119 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13120
13121 if (aReason != Uninit::Normal)
13122 {
13123 mData->mSession.mDirectControl.setNull();
13124 }
13125 else
13126 {
13127 /* this must be null here (see #OnSessionEnd()) */
13128 Assert(mData->mSession.mDirectControl.isNull());
13129 Assert(mData->mSession.mState == SessionState_Unlocking);
13130 Assert(!mData->mSession.mProgress.isNull());
13131 }
13132 if (mData->mSession.mProgress)
13133 {
13134 if (aReason == Uninit::Normal)
13135 mData->mSession.mProgress->notifyComplete(S_OK);
13136 else
13137 mData->mSession.mProgress->notifyComplete(E_FAIL,
13138 COM_IIDOF(ISession),
13139 getComponentName(),
13140 tr("The VM session was aborted"));
13141 mData->mSession.mProgress.setNull();
13142 }
13143
13144 /* remove the association between the peer machine and this session machine */
13145 Assert( (SessionMachine*)mData->mSession.mMachine == this
13146 || aReason == Uninit::Unexpected);
13147
13148 /* reset the rest of session data */
13149 mData->mSession.mMachine.setNull();
13150 mData->mSession.mState = SessionState_Unlocked;
13151 mData->mSession.mType.setNull();
13152
13153 /* destroy the machine client token before leaving the exclusive lock */
13154 if (mClientToken)
13155 {
13156 delete mClientToken;
13157 mClientToken = NULL;
13158 }
13159
13160 /* fire an event */
13161 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13162
13163 uninitDataAndChildObjects();
13164
13165 /* free the essential data structure last */
13166 mData.free();
13167
13168 /* release the exclusive lock before setting the below two to NULL */
13169 multilock.release();
13170
13171 unconst(mParent) = NULL;
13172 unconst(mPeer) = NULL;
13173
13174 LogFlowThisFuncLeave();
13175}
13176
13177// util::Lockable interface
13178////////////////////////////////////////////////////////////////////////////////
13179
13180/**
13181 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13182 * with the primary Machine instance (mPeer).
13183 */
13184RWLockHandle *SessionMachine::lockHandle() const
13185{
13186 AssertReturn(mPeer != NULL, NULL);
13187 return mPeer->lockHandle();
13188}
13189
13190// IInternalMachineControl methods
13191////////////////////////////////////////////////////////////////////////////////
13192
13193/**
13194 * Passes collected guest statistics to performance collector object
13195 */
13196STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13197 ULONG aCpuKernel, ULONG aCpuIdle,
13198 ULONG aMemTotal, ULONG aMemFree,
13199 ULONG aMemBalloon, ULONG aMemShared,
13200 ULONG aMemCache, ULONG aPageTotal,
13201 ULONG aAllocVMM, ULONG aFreeVMM,
13202 ULONG aBalloonedVMM, ULONG aSharedVMM,
13203 ULONG aVmNetRx, ULONG aVmNetTx)
13204{
13205#ifdef VBOX_WITH_RESOURCE_USAGE_API
13206 if (mCollectorGuest)
13207 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13208 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13209 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13210 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13211
13212 return S_OK;
13213#else
13214 NOREF(aValidStats);
13215 NOREF(aCpuUser);
13216 NOREF(aCpuKernel);
13217 NOREF(aCpuIdle);
13218 NOREF(aMemTotal);
13219 NOREF(aMemFree);
13220 NOREF(aMemBalloon);
13221 NOREF(aMemShared);
13222 NOREF(aMemCache);
13223 NOREF(aPageTotal);
13224 NOREF(aAllocVMM);
13225 NOREF(aFreeVMM);
13226 NOREF(aBalloonedVMM);
13227 NOREF(aSharedVMM);
13228 NOREF(aVmNetRx);
13229 NOREF(aVmNetTx);
13230 return E_NOTIMPL;
13231#endif
13232}
13233
13234/**
13235 * @note Locks this object for writing.
13236 */
13237STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13238{
13239 AutoCaller autoCaller(this);
13240 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13241
13242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13243
13244 mRemoveSavedState = aRemove;
13245
13246 return S_OK;
13247}
13248
13249/**
13250 * @note Locks the same as #setMachineState() does.
13251 */
13252STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13253{
13254 return setMachineState(aMachineState);
13255}
13256
13257/**
13258 * @note Locks this object for writing.
13259 */
13260STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13261{
13262 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13263 AutoCaller autoCaller(this);
13264 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13265
13266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13267
13268 if (mData->mSession.mState != SessionState_Locked)
13269 return VBOX_E_INVALID_OBJECT_STATE;
13270
13271 if (!mData->mSession.mProgress.isNull())
13272 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13273
13274 LogFlowThisFunc(("returns S_OK.\n"));
13275 return S_OK;
13276}
13277
13278/**
13279 * @note Locks this object for writing.
13280 */
13281STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13282{
13283 AutoCaller autoCaller(this);
13284 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13285
13286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13287
13288 if (mData->mSession.mState != SessionState_Locked)
13289 return VBOX_E_INVALID_OBJECT_STATE;
13290
13291 /* Finalize the LaunchVMProcess progress object. */
13292 if (mData->mSession.mProgress)
13293 {
13294 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13295 mData->mSession.mProgress.setNull();
13296 }
13297
13298 if (SUCCEEDED((HRESULT)iResult))
13299 {
13300#ifdef VBOX_WITH_RESOURCE_USAGE_API
13301 /* The VM has been powered up successfully, so it makes sense
13302 * now to offer the performance metrics for a running machine
13303 * object. Doing it earlier wouldn't be safe. */
13304 registerMetrics(mParent->i_performanceCollector(), mPeer,
13305 mData->mSession.mPID);
13306#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13307 }
13308
13309 return S_OK;
13310}
13311
13312/**
13313 * @note Locks this object for writing.
13314 */
13315STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13316{
13317 LogFlowThisFuncEnter();
13318
13319 CheckComArgOutPointerValid(aProgress);
13320
13321 AutoCaller autoCaller(this);
13322 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13323
13324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13325
13326 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13327 E_FAIL);
13328
13329 /* create a progress object to track operation completion */
13330 ComObjPtr<Progress> pProgress;
13331 pProgress.createObject();
13332 pProgress->init(getVirtualBox(),
13333 static_cast<IMachine *>(this) /* aInitiator */,
13334 Bstr(tr("Stopping the virtual machine")).raw(),
13335 FALSE /* aCancelable */);
13336
13337 /* fill in the console task data */
13338 mConsoleTaskData.mLastState = mData->mMachineState;
13339 mConsoleTaskData.mProgress = pProgress;
13340
13341 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13342 setMachineState(MachineState_Stopping);
13343
13344 pProgress.queryInterfaceTo(aProgress);
13345
13346 return S_OK;
13347}
13348
13349/**
13350 * @note Locks this object for writing.
13351 */
13352STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13353{
13354 LogFlowThisFuncEnter();
13355
13356 AutoCaller autoCaller(this);
13357 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13358
13359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13360
13361 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13362 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13363 && mConsoleTaskData.mLastState != MachineState_Null,
13364 E_FAIL);
13365
13366 /*
13367 * On failure, set the state to the state we had when BeginPoweringDown()
13368 * was called (this is expected by Console::PowerDown() and the associated
13369 * task). On success the VM process already changed the state to
13370 * MachineState_PoweredOff, so no need to do anything.
13371 */
13372 if (FAILED(iResult))
13373 setMachineState(mConsoleTaskData.mLastState);
13374
13375 /* notify the progress object about operation completion */
13376 Assert(mConsoleTaskData.mProgress);
13377 if (SUCCEEDED(iResult))
13378 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13379 else
13380 {
13381 Utf8Str strErrMsg(aErrMsg);
13382 if (strErrMsg.length())
13383 mConsoleTaskData.mProgress->notifyComplete(iResult,
13384 COM_IIDOF(ISession),
13385 getComponentName(),
13386 strErrMsg.c_str());
13387 else
13388 mConsoleTaskData.mProgress->notifyComplete(iResult);
13389 }
13390
13391 /* clear out the temporary saved state data */
13392 mConsoleTaskData.mLastState = MachineState_Null;
13393 mConsoleTaskData.mProgress.setNull();
13394
13395 LogFlowThisFuncLeave();
13396 return S_OK;
13397}
13398
13399
13400/**
13401 * Goes through the USB filters of the given machine to see if the given
13402 * device matches any filter or not.
13403 *
13404 * @note Locks the same as USBController::hasMatchingFilter() does.
13405 */
13406STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13407 BOOL *aMatched,
13408 ULONG *aMaskedIfs)
13409{
13410 LogFlowThisFunc(("\n"));
13411
13412 CheckComArgNotNull(aUSBDevice);
13413 CheckComArgOutPointerValid(aMatched);
13414
13415 AutoCaller autoCaller(this);
13416 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13417
13418#ifdef VBOX_WITH_USB
13419 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aUSBDevice, aMaskedIfs);
13420#else
13421 NOREF(aUSBDevice);
13422 NOREF(aMaskedIfs);
13423 *aMatched = FALSE;
13424#endif
13425
13426 return S_OK;
13427}
13428
13429/**
13430 * @note Locks the same as Host::captureUSBDevice() does.
13431 */
13432STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13433{
13434 LogFlowThisFunc(("\n"));
13435
13436 AutoCaller autoCaller(this);
13437 AssertComRCReturnRC(autoCaller.rc());
13438
13439#ifdef VBOX_WITH_USB
13440 /* if captureDeviceForVM() fails, it must have set extended error info */
13441 clearError();
13442 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13443 if (FAILED(rc)) return rc;
13444
13445 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13446 AssertReturn(service, E_FAIL);
13447 return service->captureDeviceForVM(this, Guid(aId).ref());
13448#else
13449 NOREF(aId);
13450 return E_NOTIMPL;
13451#endif
13452}
13453
13454/**
13455 * @note Locks the same as Host::detachUSBDevice() does.
13456 */
13457STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13458{
13459 LogFlowThisFunc(("\n"));
13460
13461 AutoCaller autoCaller(this);
13462 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13463
13464#ifdef VBOX_WITH_USB
13465 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13466 AssertReturn(service, E_FAIL);
13467 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13468#else
13469 NOREF(aId);
13470 NOREF(aDone);
13471 return E_NOTIMPL;
13472#endif
13473}
13474
13475/**
13476 * Inserts all machine filters to the USB proxy service and then calls
13477 * Host::autoCaptureUSBDevices().
13478 *
13479 * Called by Console from the VM process upon VM startup.
13480 *
13481 * @note Locks what called methods lock.
13482 */
13483STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13484{
13485 LogFlowThisFunc(("\n"));
13486
13487 AutoCaller autoCaller(this);
13488 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13489
13490#ifdef VBOX_WITH_USB
13491 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13492 AssertComRC(rc);
13493 NOREF(rc);
13494
13495 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13496 AssertReturn(service, E_FAIL);
13497 return service->autoCaptureDevicesForVM(this);
13498#else
13499 return S_OK;
13500#endif
13501}
13502
13503/**
13504 * Removes all machine filters from the USB proxy service and then calls
13505 * Host::detachAllUSBDevices().
13506 *
13507 * Called by Console from the VM process upon normal VM termination or by
13508 * SessionMachine::uninit() upon abnormal VM termination (from under the
13509 * Machine/SessionMachine lock).
13510 *
13511 * @note Locks what called methods lock.
13512 */
13513STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13514{
13515 LogFlowThisFunc(("\n"));
13516
13517 AutoCaller autoCaller(this);
13518 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13519
13520#ifdef VBOX_WITH_USB
13521 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13522 AssertComRC(rc);
13523 NOREF(rc);
13524
13525 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13526 AssertReturn(service, E_FAIL);
13527 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13528#else
13529 NOREF(aDone);
13530 return S_OK;
13531#endif
13532}
13533
13534/**
13535 * @note Locks this object for writing.
13536 */
13537STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13538 IProgress **aProgress)
13539{
13540 LogFlowThisFuncEnter();
13541
13542 AssertReturn(aSession, E_INVALIDARG);
13543 AssertReturn(aProgress, E_INVALIDARG);
13544
13545 AutoCaller autoCaller(this);
13546
13547 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13548 /*
13549 * We don't assert below because it might happen that a non-direct session
13550 * informs us it is closed right after we've been uninitialized -- it's ok.
13551 */
13552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13553
13554 /* get IInternalSessionControl interface */
13555 ComPtr<IInternalSessionControl> control(aSession);
13556
13557 ComAssertRet(!control.isNull(), E_INVALIDARG);
13558
13559 /* Creating a Progress object requires the VirtualBox lock, and
13560 * thus locking it here is required by the lock order rules. */
13561 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13562
13563 if (control == mData->mSession.mDirectControl)
13564 {
13565 ComAssertRet(aProgress, E_POINTER);
13566
13567 /* The direct session is being normally closed by the client process
13568 * ----------------------------------------------------------------- */
13569
13570 /* go to the closing state (essential for all open*Session() calls and
13571 * for #checkForDeath()) */
13572 Assert(mData->mSession.mState == SessionState_Locked);
13573 mData->mSession.mState = SessionState_Unlocking;
13574
13575 /* set direct control to NULL to release the remote instance */
13576 mData->mSession.mDirectControl.setNull();
13577 LogFlowThisFunc(("Direct control is set to NULL\n"));
13578
13579 if (mData->mSession.mProgress)
13580 {
13581 /* finalize the progress, someone might wait if a frontend
13582 * closes the session before powering on the VM. */
13583 mData->mSession.mProgress->notifyComplete(E_FAIL,
13584 COM_IIDOF(ISession),
13585 getComponentName(),
13586 tr("The VM session was closed before any attempt to power it on"));
13587 mData->mSession.mProgress.setNull();
13588 }
13589
13590 /* Create the progress object the client will use to wait until
13591 * #checkForDeath() is called to uninitialize this session object after
13592 * it releases the IPC semaphore.
13593 * Note! Because we're "reusing" mProgress here, this must be a proxy
13594 * object just like for LaunchVMProcess. */
13595 Assert(mData->mSession.mProgress.isNull());
13596 ComObjPtr<ProgressProxy> progress;
13597 progress.createObject();
13598 ComPtr<IUnknown> pPeer(mPeer);
13599 progress->init(mParent, pPeer,
13600 Bstr(tr("Closing session")).raw(),
13601 FALSE /* aCancelable */);
13602 progress.queryInterfaceTo(aProgress);
13603 mData->mSession.mProgress = progress;
13604 }
13605 else
13606 {
13607 /* the remote session is being normally closed */
13608 Data::Session::RemoteControlList::iterator it =
13609 mData->mSession.mRemoteControls.begin();
13610 while (it != mData->mSession.mRemoteControls.end())
13611 {
13612 if (control == *it)
13613 break;
13614 ++it;
13615 }
13616 BOOL found = it != mData->mSession.mRemoteControls.end();
13617 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13618 E_INVALIDARG);
13619 // This MUST be erase(it), not remove(*it) as the latter triggers a
13620 // very nasty use after free due to the place where the value "lives".
13621 mData->mSession.mRemoteControls.erase(it);
13622 }
13623
13624 /* signal the client watcher thread, because the client is going away */
13625 mParent->i_updateClientWatcher();
13626
13627 LogFlowThisFuncLeave();
13628 return S_OK;
13629}
13630
13631/**
13632 * @note Locks this object for writing.
13633 */
13634STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13635{
13636 LogFlowThisFuncEnter();
13637
13638 CheckComArgOutPointerValid(aProgress);
13639 CheckComArgOutPointerValid(aStateFilePath);
13640
13641 AutoCaller autoCaller(this);
13642 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13643
13644 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13645
13646 AssertReturn( mData->mMachineState == MachineState_Paused
13647 && mConsoleTaskData.mLastState == MachineState_Null
13648 && mConsoleTaskData.strStateFilePath.isEmpty(),
13649 E_FAIL);
13650
13651 /* create a progress object to track operation completion */
13652 ComObjPtr<Progress> pProgress;
13653 pProgress.createObject();
13654 pProgress->init(getVirtualBox(),
13655 static_cast<IMachine *>(this) /* aInitiator */,
13656 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13657 FALSE /* aCancelable */);
13658
13659 Utf8Str strStateFilePath;
13660 /* stateFilePath is null when the machine is not running */
13661 if (mData->mMachineState == MachineState_Paused)
13662 composeSavedStateFilename(strStateFilePath);
13663
13664 /* fill in the console task data */
13665 mConsoleTaskData.mLastState = mData->mMachineState;
13666 mConsoleTaskData.strStateFilePath = strStateFilePath;
13667 mConsoleTaskData.mProgress = pProgress;
13668
13669 /* set the state to Saving (this is expected by Console::SaveState()) */
13670 setMachineState(MachineState_Saving);
13671
13672 strStateFilePath.cloneTo(aStateFilePath);
13673 pProgress.queryInterfaceTo(aProgress);
13674
13675 return S_OK;
13676}
13677
13678/**
13679 * @note Locks mParent + this object for writing.
13680 */
13681STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13682{
13683 LogFlowThisFunc(("\n"));
13684
13685 AutoCaller autoCaller(this);
13686 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13687
13688 /* endSavingState() need mParent lock */
13689 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13690
13691 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13692 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13693 && mConsoleTaskData.mLastState != MachineState_Null
13694 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13695 E_FAIL);
13696
13697 /*
13698 * On failure, set the state to the state we had when BeginSavingState()
13699 * was called (this is expected by Console::SaveState() and the associated
13700 * task). On success the VM process already changed the state to
13701 * MachineState_Saved, so no need to do anything.
13702 */
13703 if (FAILED(iResult))
13704 setMachineState(mConsoleTaskData.mLastState);
13705
13706 return endSavingState(iResult, aErrMsg);
13707}
13708
13709/**
13710 * @note Locks this object for writing.
13711 */
13712STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13713{
13714 LogFlowThisFunc(("\n"));
13715
13716 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13717
13718 AutoCaller autoCaller(this);
13719 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13720
13721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13722
13723 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13724 || mData->mMachineState == MachineState_Teleported
13725 || mData->mMachineState == MachineState_Aborted
13726 , E_FAIL); /** @todo setError. */
13727
13728 Utf8Str stateFilePathFull = aSavedStateFile;
13729 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13730 if (RT_FAILURE(vrc))
13731 return setError(VBOX_E_FILE_ERROR,
13732 tr("Invalid saved state file path '%ls' (%Rrc)"),
13733 aSavedStateFile,
13734 vrc);
13735
13736 mSSData->strStateFilePath = stateFilePathFull;
13737
13738 /* The below setMachineState() will detect the state transition and will
13739 * update the settings file */
13740
13741 return setMachineState(MachineState_Saved);
13742}
13743
13744STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13745 ComSafeArrayOut(BSTR, aValues),
13746 ComSafeArrayOut(LONG64, aTimestamps),
13747 ComSafeArrayOut(BSTR, aFlags))
13748{
13749 LogFlowThisFunc(("\n"));
13750
13751#ifdef VBOX_WITH_GUEST_PROPS
13752 using namespace guestProp;
13753
13754 AutoCaller autoCaller(this);
13755 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13756
13757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13758
13759 CheckComArgOutSafeArrayPointerValid(aNames);
13760 CheckComArgOutSafeArrayPointerValid(aValues);
13761 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13762 CheckComArgOutSafeArrayPointerValid(aFlags);
13763
13764 size_t cEntries = mHWData->mGuestProperties.size();
13765 com::SafeArray<BSTR> names(cEntries);
13766 com::SafeArray<BSTR> values(cEntries);
13767 com::SafeArray<LONG64> timestamps(cEntries);
13768 com::SafeArray<BSTR> flags(cEntries);
13769 unsigned i = 0;
13770 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13771 it != mHWData->mGuestProperties.end();
13772 ++it)
13773 {
13774 char szFlags[MAX_FLAGS_LEN + 1];
13775 it->first.cloneTo(&names[i]);
13776 it->second.strValue.cloneTo(&values[i]);
13777 timestamps[i] = it->second.mTimestamp;
13778 /* If it is NULL, keep it NULL. */
13779 if (it->second.mFlags)
13780 {
13781 writeFlags(it->second.mFlags, szFlags);
13782 Bstr(szFlags).cloneTo(&flags[i]);
13783 }
13784 else
13785 flags[i] = NULL;
13786 ++i;
13787 }
13788 names.detachTo(ComSafeArrayOutArg(aNames));
13789 values.detachTo(ComSafeArrayOutArg(aValues));
13790 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13791 flags.detachTo(ComSafeArrayOutArg(aFlags));
13792 return S_OK;
13793#else
13794 ReturnComNotImplemented();
13795#endif
13796}
13797
13798STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13799 IN_BSTR aValue,
13800 LONG64 aTimestamp,
13801 IN_BSTR aFlags)
13802{
13803 LogFlowThisFunc(("\n"));
13804
13805#ifdef VBOX_WITH_GUEST_PROPS
13806 using namespace guestProp;
13807
13808 CheckComArgStrNotEmptyOrNull(aName);
13809 CheckComArgNotNull(aValue);
13810 CheckComArgNotNull(aFlags);
13811
13812 try
13813 {
13814 /*
13815 * Convert input up front.
13816 */
13817 Utf8Str utf8Name(aName);
13818 uint32_t fFlags = NILFLAG;
13819 if (aFlags)
13820 {
13821 Utf8Str utf8Flags(aFlags);
13822 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13823 AssertRCReturn(vrc, E_INVALIDARG);
13824 }
13825
13826 /*
13827 * Now grab the object lock, validate the state and do the update.
13828 */
13829 AutoCaller autoCaller(this);
13830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13831
13832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13833
13834 switch (mData->mMachineState)
13835 {
13836 case MachineState_Paused:
13837 case MachineState_Running:
13838 case MachineState_Teleporting:
13839 case MachineState_TeleportingPausedVM:
13840 case MachineState_LiveSnapshotting:
13841 case MachineState_DeletingSnapshotOnline:
13842 case MachineState_DeletingSnapshotPaused:
13843 case MachineState_Saving:
13844 case MachineState_Stopping:
13845 break;
13846
13847 default:
13848 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13849 VBOX_E_INVALID_VM_STATE);
13850 }
13851
13852 setModified(IsModified_MachineData);
13853 mHWData.backup();
13854
13855 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13856 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13857 if (it != mHWData->mGuestProperties.end())
13858 {
13859 if (!fDelete)
13860 {
13861 it->second.strValue = aValue;
13862 it->second.mTimestamp = aTimestamp;
13863 it->second.mFlags = fFlags;
13864 }
13865 else
13866 mHWData->mGuestProperties.erase(it);
13867
13868 mData->mGuestPropertiesModified = TRUE;
13869 }
13870 else if (!fDelete)
13871 {
13872 HWData::GuestProperty prop;
13873 prop.strValue = aValue;
13874 prop.mTimestamp = aTimestamp;
13875 prop.mFlags = fFlags;
13876
13877 mHWData->mGuestProperties[utf8Name] = prop;
13878 mData->mGuestPropertiesModified = TRUE;
13879 }
13880
13881 /*
13882 * Send a callback notification if appropriate
13883 */
13884 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13885 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13886 RTSTR_MAX,
13887 utf8Name.c_str(),
13888 RTSTR_MAX, NULL)
13889 )
13890 {
13891 alock.release();
13892
13893 mParent->i_onGuestPropertyChange(mData->mUuid,
13894 aName,
13895 aValue,
13896 aFlags);
13897 }
13898 }
13899 catch (...)
13900 {
13901 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13902 }
13903 return S_OK;
13904#else
13905 ReturnComNotImplemented();
13906#endif
13907}
13908
13909STDMETHODIMP SessionMachine::LockMedia()
13910{
13911 AutoCaller autoCaller(this);
13912 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13913
13914 AutoMultiWriteLock2 alock(this->lockHandle(),
13915 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13916
13917 AssertReturn( mData->mMachineState == MachineState_Starting
13918 || mData->mMachineState == MachineState_Restoring
13919 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13920
13921 clearError();
13922 alock.release();
13923 return lockMedia();
13924}
13925
13926STDMETHODIMP SessionMachine::UnlockMedia()
13927{
13928 unlockMedia();
13929 return S_OK;
13930}
13931
13932STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13933 IMediumAttachment **aNewAttachment)
13934{
13935 CheckComArgNotNull(aAttachment);
13936 CheckComArgOutPointerValid(aNewAttachment);
13937
13938 AutoCaller autoCaller(this);
13939 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13940
13941 // request the host lock first, since might be calling Host methods for getting host drives;
13942 // next, protect the media tree all the while we're in here, as well as our member variables
13943 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13944 this->lockHandle(),
13945 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13946
13947 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13948
13949 Bstr ctrlName;
13950 LONG lPort;
13951 LONG lDevice;
13952 bool fTempEject;
13953 {
13954 AutoCaller autoAttachCaller(this);
13955 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13956
13957 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13958
13959 /* Need to query the details first, as the IMediumAttachment reference
13960 * might be to the original settings, which we are going to change. */
13961 ctrlName = pAttach->i_getControllerName();
13962 lPort = pAttach->i_getPort();
13963 lDevice = pAttach->i_getDevice();
13964 fTempEject = pAttach->i_getTempEject();
13965 }
13966
13967 if (!fTempEject)
13968 {
13969 /* Remember previously mounted medium. The medium before taking the
13970 * backup is not necessarily the same thing. */
13971 ComObjPtr<Medium> oldmedium;
13972 oldmedium = pAttach->i_getMedium();
13973
13974 setModified(IsModified_Storage);
13975 mMediaData.backup();
13976
13977 // The backup operation makes the pAttach reference point to the
13978 // old settings. Re-get the correct reference.
13979 pAttach = findAttachment(mMediaData->mAttachments,
13980 ctrlName.raw(),
13981 lPort,
13982 lDevice);
13983
13984 {
13985 AutoCaller autoAttachCaller(this);
13986 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13987
13988 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13989 if (!oldmedium.isNull())
13990 oldmedium->i_removeBackReference(mData->mUuid);
13991
13992 pAttach->i_updateMedium(NULL);
13993 pAttach->i_updateEjected();
13994 }
13995
13996 setModified(IsModified_Storage);
13997 }
13998 else
13999 {
14000 {
14001 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14002 pAttach->i_updateEjected();
14003 }
14004 }
14005
14006 pAttach.queryInterfaceTo(aNewAttachment);
14007
14008 return S_OK;
14009}
14010
14011// public methods only for internal purposes
14012/////////////////////////////////////////////////////////////////////////////
14013
14014#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14015/**
14016 * Called from the client watcher thread to check for expected or unexpected
14017 * death of the client process that has a direct session to this machine.
14018 *
14019 * On Win32 and on OS/2, this method is called only when we've got the
14020 * mutex (i.e. the client has either died or terminated normally) so it always
14021 * returns @c true (the client is terminated, the session machine is
14022 * uninitialized).
14023 *
14024 * On other platforms, the method returns @c true if the client process has
14025 * terminated normally or abnormally and the session machine was uninitialized,
14026 * and @c false if the client process is still alive.
14027 *
14028 * @note Locks this object for writing.
14029 */
14030bool SessionMachine::checkForDeath()
14031{
14032 Uninit::Reason reason;
14033 bool terminated = false;
14034
14035 /* Enclose autoCaller with a block because calling uninit() from under it
14036 * will deadlock. */
14037 {
14038 AutoCaller autoCaller(this);
14039 if (!autoCaller.isOk())
14040 {
14041 /* return true if not ready, to cause the client watcher to exclude
14042 * the corresponding session from watching */
14043 LogFlowThisFunc(("Already uninitialized!\n"));
14044 return true;
14045 }
14046
14047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14048
14049 /* Determine the reason of death: if the session state is Closing here,
14050 * everything is fine. Otherwise it means that the client did not call
14051 * OnSessionEnd() before it released the IPC semaphore. This may happen
14052 * either because the client process has abnormally terminated, or
14053 * because it simply forgot to call ISession::Close() before exiting. We
14054 * threat the latter also as an abnormal termination (see
14055 * Session::uninit() for details). */
14056 reason = mData->mSession.mState == SessionState_Unlocking ?
14057 Uninit::Normal :
14058 Uninit::Abnormal;
14059
14060 if (mClientToken)
14061 terminated = mClientToken->release();
14062 } /* AutoCaller block */
14063
14064 if (terminated)
14065 uninit(reason);
14066
14067 return terminated;
14068}
14069
14070void SessionMachine::getTokenId(Utf8Str &strTokenId)
14071{
14072 LogFlowThisFunc(("\n"));
14073
14074 strTokenId.setNull();
14075
14076 AutoCaller autoCaller(this);
14077 AssertComRCReturnVoid(autoCaller.rc());
14078
14079 Assert(mClientToken);
14080 if (mClientToken)
14081 mClientToken->getId(strTokenId);
14082}
14083#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14084IToken *SessionMachine::getToken()
14085{
14086 LogFlowThisFunc(("\n"));
14087
14088 AutoCaller autoCaller(this);
14089 AssertComRCReturn(autoCaller.rc(), NULL);
14090
14091 Assert(mClientToken);
14092 if (mClientToken)
14093 return mClientToken->getToken();
14094 else
14095 return NULL;
14096}
14097#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14098
14099Machine::ClientToken *SessionMachine::getClientToken()
14100{
14101 LogFlowThisFunc(("\n"));
14102
14103 AutoCaller autoCaller(this);
14104 AssertComRCReturn(autoCaller.rc(), NULL);
14105
14106 return mClientToken;
14107}
14108
14109
14110/**
14111 * @note Locks this object for reading.
14112 */
14113HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14114{
14115 LogFlowThisFunc(("\n"));
14116
14117 AutoCaller autoCaller(this);
14118 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14119
14120 ComPtr<IInternalSessionControl> directControl;
14121 {
14122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14123 directControl = mData->mSession.mDirectControl;
14124 }
14125
14126 /* ignore notifications sent after #OnSessionEnd() is called */
14127 if (!directControl)
14128 return S_OK;
14129
14130 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14131}
14132
14133/**
14134 * @note Locks this object for reading.
14135 */
14136HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14137 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
14138{
14139 LogFlowThisFunc(("\n"));
14140
14141 AutoCaller autoCaller(this);
14142 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14143
14144 ComPtr<IInternalSessionControl> directControl;
14145 {
14146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14147 directControl = mData->mSession.mDirectControl;
14148 }
14149
14150 /* ignore notifications sent after #OnSessionEnd() is called */
14151 if (!directControl)
14152 return S_OK;
14153 /*
14154 * instead acting like callback we ask IVirtualBox deliver corresponding event
14155 */
14156
14157 mParent->i_onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14158 return S_OK;
14159}
14160
14161/**
14162 * @note Locks this object for reading.
14163 */
14164HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14165{
14166 LogFlowThisFunc(("\n"));
14167
14168 AutoCaller autoCaller(this);
14169 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14170
14171 ComPtr<IInternalSessionControl> directControl;
14172 {
14173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14174 directControl = mData->mSession.mDirectControl;
14175 }
14176
14177 /* ignore notifications sent after #OnSessionEnd() is called */
14178 if (!directControl)
14179 return S_OK;
14180
14181 return directControl->OnSerialPortChange(serialPort);
14182}
14183
14184/**
14185 * @note Locks this object for reading.
14186 */
14187HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14188{
14189 LogFlowThisFunc(("\n"));
14190
14191 AutoCaller autoCaller(this);
14192 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14193
14194 ComPtr<IInternalSessionControl> directControl;
14195 {
14196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14197 directControl = mData->mSession.mDirectControl;
14198 }
14199
14200 /* ignore notifications sent after #OnSessionEnd() is called */
14201 if (!directControl)
14202 return S_OK;
14203
14204 return directControl->OnParallelPortChange(parallelPort);
14205}
14206
14207/**
14208 * @note Locks this object for reading.
14209 */
14210HRESULT SessionMachine::onStorageControllerChange()
14211{
14212 LogFlowThisFunc(("\n"));
14213
14214 AutoCaller autoCaller(this);
14215 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14216
14217 ComPtr<IInternalSessionControl> directControl;
14218 {
14219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14220 directControl = mData->mSession.mDirectControl;
14221 }
14222
14223 /* ignore notifications sent after #OnSessionEnd() is called */
14224 if (!directControl)
14225 return S_OK;
14226
14227 return directControl->OnStorageControllerChange();
14228}
14229
14230/**
14231 * @note Locks this object for reading.
14232 */
14233HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14234{
14235 LogFlowThisFunc(("\n"));
14236
14237 AutoCaller autoCaller(this);
14238 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14239
14240 ComPtr<IInternalSessionControl> directControl;
14241 {
14242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14243 directControl = mData->mSession.mDirectControl;
14244 }
14245
14246 /* ignore notifications sent after #OnSessionEnd() is called */
14247 if (!directControl)
14248 return S_OK;
14249
14250 return directControl->OnMediumChange(aAttachment, aForce);
14251}
14252
14253/**
14254 * @note Locks this object for reading.
14255 */
14256HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14257{
14258 LogFlowThisFunc(("\n"));
14259
14260 AutoCaller autoCaller(this);
14261 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14262
14263 ComPtr<IInternalSessionControl> directControl;
14264 {
14265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14266 directControl = mData->mSession.mDirectControl;
14267 }
14268
14269 /* ignore notifications sent after #OnSessionEnd() is called */
14270 if (!directControl)
14271 return S_OK;
14272
14273 return directControl->OnCPUChange(aCPU, aRemove);
14274}
14275
14276HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14277{
14278 LogFlowThisFunc(("\n"));
14279
14280 AutoCaller autoCaller(this);
14281 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14282
14283 ComPtr<IInternalSessionControl> directControl;
14284 {
14285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14286 directControl = mData->mSession.mDirectControl;
14287 }
14288
14289 /* ignore notifications sent after #OnSessionEnd() is called */
14290 if (!directControl)
14291 return S_OK;
14292
14293 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14294}
14295
14296/**
14297 * @note Locks this object for reading.
14298 */
14299HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14300{
14301 LogFlowThisFunc(("\n"));
14302
14303 AutoCaller autoCaller(this);
14304 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14305
14306 ComPtr<IInternalSessionControl> directControl;
14307 {
14308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14309 directControl = mData->mSession.mDirectControl;
14310 }
14311
14312 /* ignore notifications sent after #OnSessionEnd() is called */
14313 if (!directControl)
14314 return S_OK;
14315
14316 return directControl->OnVRDEServerChange(aRestart);
14317}
14318
14319/**
14320 * @note Locks this object for reading.
14321 */
14322HRESULT SessionMachine::onVideoCaptureChange()
14323{
14324 LogFlowThisFunc(("\n"));
14325
14326 AutoCaller autoCaller(this);
14327 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14328
14329 ComPtr<IInternalSessionControl> directControl;
14330 {
14331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14332 directControl = mData->mSession.mDirectControl;
14333 }
14334
14335 /* ignore notifications sent after #OnSessionEnd() is called */
14336 if (!directControl)
14337 return S_OK;
14338
14339 return directControl->OnVideoCaptureChange();
14340}
14341
14342/**
14343 * @note Locks this object for reading.
14344 */
14345HRESULT SessionMachine::onUSBControllerChange()
14346{
14347 LogFlowThisFunc(("\n"));
14348
14349 AutoCaller autoCaller(this);
14350 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14351
14352 ComPtr<IInternalSessionControl> directControl;
14353 {
14354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14355 directControl = mData->mSession.mDirectControl;
14356 }
14357
14358 /* ignore notifications sent after #OnSessionEnd() is called */
14359 if (!directControl)
14360 return S_OK;
14361
14362 return directControl->OnUSBControllerChange();
14363}
14364
14365/**
14366 * @note Locks this object for reading.
14367 */
14368HRESULT SessionMachine::onSharedFolderChange()
14369{
14370 LogFlowThisFunc(("\n"));
14371
14372 AutoCaller autoCaller(this);
14373 AssertComRCReturnRC(autoCaller.rc());
14374
14375 ComPtr<IInternalSessionControl> directControl;
14376 {
14377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14378 directControl = mData->mSession.mDirectControl;
14379 }
14380
14381 /* ignore notifications sent after #OnSessionEnd() is called */
14382 if (!directControl)
14383 return S_OK;
14384
14385 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14386}
14387
14388/**
14389 * @note Locks this object for reading.
14390 */
14391HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14392{
14393 LogFlowThisFunc(("\n"));
14394
14395 AutoCaller autoCaller(this);
14396 AssertComRCReturnRC(autoCaller.rc());
14397
14398 ComPtr<IInternalSessionControl> directControl;
14399 {
14400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14401 directControl = mData->mSession.mDirectControl;
14402 }
14403
14404 /* ignore notifications sent after #OnSessionEnd() is called */
14405 if (!directControl)
14406 return S_OK;
14407
14408 return directControl->OnClipboardModeChange(aClipboardMode);
14409}
14410
14411/**
14412 * @note Locks this object for reading.
14413 */
14414HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14415{
14416 LogFlowThisFunc(("\n"));
14417
14418 AutoCaller autoCaller(this);
14419 AssertComRCReturnRC(autoCaller.rc());
14420
14421 ComPtr<IInternalSessionControl> directControl;
14422 {
14423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14424 directControl = mData->mSession.mDirectControl;
14425 }
14426
14427 /* ignore notifications sent after #OnSessionEnd() is called */
14428 if (!directControl)
14429 return S_OK;
14430
14431 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14432}
14433
14434/**
14435 * @note Locks this object for reading.
14436 */
14437HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14438{
14439 LogFlowThisFunc(("\n"));
14440
14441 AutoCaller autoCaller(this);
14442 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14443
14444 ComPtr<IInternalSessionControl> directControl;
14445 {
14446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14447 directControl = mData->mSession.mDirectControl;
14448 }
14449
14450 /* ignore notifications sent after #OnSessionEnd() is called */
14451 if (!directControl)
14452 return S_OK;
14453
14454 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14455}
14456
14457/**
14458 * @note Locks this object for reading.
14459 */
14460HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14461{
14462 LogFlowThisFunc(("\n"));
14463
14464 AutoCaller autoCaller(this);
14465 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14466
14467 ComPtr<IInternalSessionControl> directControl;
14468 {
14469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14470 directControl = mData->mSession.mDirectControl;
14471 }
14472
14473 /* ignore notifications sent after #OnSessionEnd() is called */
14474 if (!directControl)
14475 return S_OK;
14476
14477 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14478}
14479
14480/**
14481 * Returns @c true if this machine's USB controller reports it has a matching
14482 * filter for the given USB device and @c false otherwise.
14483 *
14484 * @note locks this object for reading.
14485 */
14486bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14487{
14488 AutoCaller autoCaller(this);
14489 /* silently return if not ready -- this method may be called after the
14490 * direct machine session has been called */
14491 if (!autoCaller.isOk())
14492 return false;
14493
14494#ifdef VBOX_WITH_USB
14495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14496
14497 switch (mData->mMachineState)
14498 {
14499 case MachineState_Starting:
14500 case MachineState_Restoring:
14501 case MachineState_TeleportingIn:
14502 case MachineState_Paused:
14503 case MachineState_Running:
14504 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14505 * elsewhere... */
14506 alock.release();
14507 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14508 default: break;
14509 }
14510#else
14511 NOREF(aDevice);
14512 NOREF(aMaskedIfs);
14513#endif
14514 return false;
14515}
14516
14517/**
14518 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14519 */
14520HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14521 IVirtualBoxErrorInfo *aError,
14522 ULONG aMaskedIfs)
14523{
14524 LogFlowThisFunc(("\n"));
14525
14526 AutoCaller autoCaller(this);
14527
14528 /* This notification may happen after the machine object has been
14529 * uninitialized (the session was closed), so don't assert. */
14530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14531
14532 ComPtr<IInternalSessionControl> directControl;
14533 {
14534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14535 directControl = mData->mSession.mDirectControl;
14536 }
14537
14538 /* fail on notifications sent after #OnSessionEnd() is called, it is
14539 * expected by the caller */
14540 if (!directControl)
14541 return E_FAIL;
14542
14543 /* No locks should be held at this point. */
14544 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14545 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14546
14547 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14548}
14549
14550/**
14551 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14552 */
14553HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14554 IVirtualBoxErrorInfo *aError)
14555{
14556 LogFlowThisFunc(("\n"));
14557
14558 AutoCaller autoCaller(this);
14559
14560 /* This notification may happen after the machine object has been
14561 * uninitialized (the session was closed), so don't assert. */
14562 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14563
14564 ComPtr<IInternalSessionControl> directControl;
14565 {
14566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14567 directControl = mData->mSession.mDirectControl;
14568 }
14569
14570 /* fail on notifications sent after #OnSessionEnd() is called, it is
14571 * expected by the caller */
14572 if (!directControl)
14573 return E_FAIL;
14574
14575 /* No locks should be held at this point. */
14576 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14577 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14578
14579 return directControl->OnUSBDeviceDetach(aId, aError);
14580}
14581
14582// protected methods
14583/////////////////////////////////////////////////////////////////////////////
14584
14585/**
14586 * Helper method to finalize saving the state.
14587 *
14588 * @note Must be called from under this object's lock.
14589 *
14590 * @param aRc S_OK if the snapshot has been taken successfully
14591 * @param aErrMsg human readable error message for failure
14592 *
14593 * @note Locks mParent + this objects for writing.
14594 */
14595HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14596{
14597 LogFlowThisFuncEnter();
14598
14599 AutoCaller autoCaller(this);
14600 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14601
14602 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14603
14604 HRESULT rc = S_OK;
14605
14606 if (SUCCEEDED(aRc))
14607 {
14608 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14609
14610 /* save all VM settings */
14611 rc = saveSettings(NULL);
14612 // no need to check whether VirtualBox.xml needs saving also since
14613 // we can't have a name change pending at this point
14614 }
14615 else
14616 {
14617 // delete the saved state file (it might have been already created);
14618 // we need not check whether this is shared with a snapshot here because
14619 // we certainly created this saved state file here anew
14620 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14621 }
14622
14623 /* notify the progress object about operation completion */
14624 Assert(mConsoleTaskData.mProgress);
14625 if (SUCCEEDED(aRc))
14626 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14627 else
14628 {
14629 if (aErrMsg.length())
14630 mConsoleTaskData.mProgress->notifyComplete(aRc,
14631 COM_IIDOF(ISession),
14632 getComponentName(),
14633 aErrMsg.c_str());
14634 else
14635 mConsoleTaskData.mProgress->notifyComplete(aRc);
14636 }
14637
14638 /* clear out the temporary saved state data */
14639 mConsoleTaskData.mLastState = MachineState_Null;
14640 mConsoleTaskData.strStateFilePath.setNull();
14641 mConsoleTaskData.mProgress.setNull();
14642
14643 LogFlowThisFuncLeave();
14644 return rc;
14645}
14646
14647/**
14648 * Deletes the given file if it is no longer in use by either the current machine state
14649 * (if the machine is "saved") or any of the machine's snapshots.
14650 *
14651 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14652 * but is different for each SnapshotMachine. When calling this, the order of calling this
14653 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14654 * is therefore critical. I know, it's all rather messy.
14655 *
14656 * @param strStateFile
14657 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14658 */
14659void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14660 Snapshot *pSnapshotToIgnore)
14661{
14662 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14663 if ( (strStateFile.isNotEmpty())
14664 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14665 )
14666 // ... and it must also not be shared with other snapshots
14667 if ( !mData->mFirstSnapshot
14668 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14669 // this checks the SnapshotMachine's state file paths
14670 )
14671 RTFileDelete(strStateFile.c_str());
14672}
14673
14674/**
14675 * Locks the attached media.
14676 *
14677 * All attached hard disks are locked for writing and DVD/floppy are locked for
14678 * reading. Parents of attached hard disks (if any) are locked for reading.
14679 *
14680 * This method also performs accessibility check of all media it locks: if some
14681 * media is inaccessible, the method will return a failure and a bunch of
14682 * extended error info objects per each inaccessible medium.
14683 *
14684 * Note that this method is atomic: if it returns a success, all media are
14685 * locked as described above; on failure no media is locked at all (all
14686 * succeeded individual locks will be undone).
14687 *
14688 * The caller is responsible for doing the necessary state sanity checks.
14689 *
14690 * The locks made by this method must be undone by calling #unlockMedia() when
14691 * no more needed.
14692 */
14693HRESULT SessionMachine::lockMedia()
14694{
14695 AutoCaller autoCaller(this);
14696 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14697
14698 AutoMultiWriteLock2 alock(this->lockHandle(),
14699 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14700
14701 /* bail out if trying to lock things with already set up locking */
14702 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14703
14704 MultiResult mrc(S_OK);
14705
14706 /* Collect locking information for all medium objects attached to the VM. */
14707 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14708 it != mMediaData->mAttachments.end();
14709 ++it)
14710 {
14711 MediumAttachment* pAtt = *it;
14712 DeviceType_T devType = pAtt->i_getType();
14713 Medium *pMedium = pAtt->i_getMedium();
14714
14715 MediumLockList *pMediumLockList(new MediumLockList());
14716 // There can be attachments without a medium (floppy/dvd), and thus
14717 // it's impossible to create a medium lock list. It still makes sense
14718 // to have the empty medium lock list in the map in case a medium is
14719 // attached later.
14720 if (pMedium != NULL)
14721 {
14722 MediumType_T mediumType = pMedium->i_getType();
14723 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14724 || mediumType == MediumType_Shareable;
14725 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14726
14727 alock.release();
14728 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14729 !fIsReadOnlyLock /* fMediumLockWrite */,
14730 NULL,
14731 *pMediumLockList);
14732 alock.acquire();
14733 if (FAILED(mrc))
14734 {
14735 delete pMediumLockList;
14736 mData->mSession.mLockedMedia.Clear();
14737 break;
14738 }
14739 }
14740
14741 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14742 if (FAILED(rc))
14743 {
14744 mData->mSession.mLockedMedia.Clear();
14745 mrc = setError(rc,
14746 tr("Collecting locking information for all attached media failed"));
14747 break;
14748 }
14749 }
14750
14751 if (SUCCEEDED(mrc))
14752 {
14753 /* Now lock all media. If this fails, nothing is locked. */
14754 alock.release();
14755 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14756 alock.acquire();
14757 if (FAILED(rc))
14758 {
14759 mrc = setError(rc,
14760 tr("Locking of attached media failed"));
14761 }
14762 }
14763
14764 return mrc;
14765}
14766
14767/**
14768 * Undoes the locks made by by #lockMedia().
14769 */
14770void SessionMachine::unlockMedia()
14771{
14772 AutoCaller autoCaller(this);
14773 AssertComRCReturnVoid(autoCaller.rc());
14774
14775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14776
14777 /* we may be holding important error info on the current thread;
14778 * preserve it */
14779 ErrorInfoKeeper eik;
14780
14781 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14782 AssertComRC(rc);
14783}
14784
14785/**
14786 * Helper to change the machine state (reimplementation).
14787 *
14788 * @note Locks this object for writing.
14789 * @note This method must not call saveSettings or SaveSettings, otherwise
14790 * it can cause crashes in random places due to unexpectedly committing
14791 * the current settings. The caller is responsible for that. The call
14792 * to saveStateSettings is fine, because this method does not commit.
14793 */
14794HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14795{
14796 LogFlowThisFuncEnter();
14797 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14798
14799 AutoCaller autoCaller(this);
14800 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14801
14802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14803
14804 MachineState_T oldMachineState = mData->mMachineState;
14805
14806 AssertMsgReturn(oldMachineState != aMachineState,
14807 ("oldMachineState=%s, aMachineState=%s\n",
14808 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14809 E_FAIL);
14810
14811 HRESULT rc = S_OK;
14812
14813 int stsFlags = 0;
14814 bool deleteSavedState = false;
14815
14816 /* detect some state transitions */
14817
14818 if ( ( oldMachineState == MachineState_Saved
14819 && aMachineState == MachineState_Restoring)
14820 || ( ( oldMachineState == MachineState_PoweredOff
14821 || oldMachineState == MachineState_Teleported
14822 || oldMachineState == MachineState_Aborted
14823 )
14824 && ( aMachineState == MachineState_TeleportingIn
14825 || aMachineState == MachineState_Starting
14826 )
14827 )
14828 )
14829 {
14830 /* The EMT thread is about to start */
14831
14832 /* Nothing to do here for now... */
14833
14834 /// @todo NEWMEDIA don't let mDVDDrive and other children
14835 /// change anything when in the Starting/Restoring state
14836 }
14837 else if ( ( oldMachineState == MachineState_Running
14838 || oldMachineState == MachineState_Paused
14839 || oldMachineState == MachineState_Teleporting
14840 || oldMachineState == MachineState_LiveSnapshotting
14841 || oldMachineState == MachineState_Stuck
14842 || oldMachineState == MachineState_Starting
14843 || oldMachineState == MachineState_Stopping
14844 || oldMachineState == MachineState_Saving
14845 || oldMachineState == MachineState_Restoring
14846 || oldMachineState == MachineState_TeleportingPausedVM
14847 || oldMachineState == MachineState_TeleportingIn
14848 )
14849 && ( aMachineState == MachineState_PoweredOff
14850 || aMachineState == MachineState_Saved
14851 || aMachineState == MachineState_Teleported
14852 || aMachineState == MachineState_Aborted
14853 )
14854 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14855 * snapshot */
14856 && ( mConsoleTaskData.mSnapshot.isNull()
14857 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14858 )
14859 )
14860 {
14861 /* The EMT thread has just stopped, unlock attached media. Note that as
14862 * opposed to locking that is done from Console, we do unlocking here
14863 * because the VM process may have aborted before having a chance to
14864 * properly unlock all media it locked. */
14865
14866 unlockMedia();
14867 }
14868
14869 if (oldMachineState == MachineState_Restoring)
14870 {
14871 if (aMachineState != MachineState_Saved)
14872 {
14873 /*
14874 * delete the saved state file once the machine has finished
14875 * restoring from it (note that Console sets the state from
14876 * Restoring to Saved if the VM couldn't restore successfully,
14877 * to give the user an ability to fix an error and retry --
14878 * we keep the saved state file in this case)
14879 */
14880 deleteSavedState = true;
14881 }
14882 }
14883 else if ( oldMachineState == MachineState_Saved
14884 && ( aMachineState == MachineState_PoweredOff
14885 || aMachineState == MachineState_Aborted
14886 || aMachineState == MachineState_Teleported
14887 )
14888 )
14889 {
14890 /*
14891 * delete the saved state after Console::ForgetSavedState() is called
14892 * or if the VM process (owning a direct VM session) crashed while the
14893 * VM was Saved
14894 */
14895
14896 /// @todo (dmik)
14897 // Not sure that deleting the saved state file just because of the
14898 // client death before it attempted to restore the VM is a good
14899 // thing. But when it crashes we need to go to the Aborted state
14900 // which cannot have the saved state file associated... The only
14901 // way to fix this is to make the Aborted condition not a VM state
14902 // but a bool flag: i.e., when a crash occurs, set it to true and
14903 // change the state to PoweredOff or Saved depending on the
14904 // saved state presence.
14905
14906 deleteSavedState = true;
14907 mData->mCurrentStateModified = TRUE;
14908 stsFlags |= SaveSTS_CurStateModified;
14909 }
14910
14911 if ( aMachineState == MachineState_Starting
14912 || aMachineState == MachineState_Restoring
14913 || aMachineState == MachineState_TeleportingIn
14914 )
14915 {
14916 /* set the current state modified flag to indicate that the current
14917 * state is no more identical to the state in the
14918 * current snapshot */
14919 if (!mData->mCurrentSnapshot.isNull())
14920 {
14921 mData->mCurrentStateModified = TRUE;
14922 stsFlags |= SaveSTS_CurStateModified;
14923 }
14924 }
14925
14926 if (deleteSavedState)
14927 {
14928 if (mRemoveSavedState)
14929 {
14930 Assert(!mSSData->strStateFilePath.isEmpty());
14931
14932 // it is safe to delete the saved state file if ...
14933 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14934 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14935 // ... none of the snapshots share the saved state file
14936 )
14937 RTFileDelete(mSSData->strStateFilePath.c_str());
14938 }
14939
14940 mSSData->strStateFilePath.setNull();
14941 stsFlags |= SaveSTS_StateFilePath;
14942 }
14943
14944 /* redirect to the underlying peer machine */
14945 mPeer->setMachineState(aMachineState);
14946
14947 if ( aMachineState == MachineState_PoweredOff
14948 || aMachineState == MachineState_Teleported
14949 || aMachineState == MachineState_Aborted
14950 || aMachineState == MachineState_Saved)
14951 {
14952 /* the machine has stopped execution
14953 * (or the saved state file was adopted) */
14954 stsFlags |= SaveSTS_StateTimeStamp;
14955 }
14956
14957 if ( ( oldMachineState == MachineState_PoweredOff
14958 || oldMachineState == MachineState_Aborted
14959 || oldMachineState == MachineState_Teleported
14960 )
14961 && aMachineState == MachineState_Saved)
14962 {
14963 /* the saved state file was adopted */
14964 Assert(!mSSData->strStateFilePath.isEmpty());
14965 stsFlags |= SaveSTS_StateFilePath;
14966 }
14967
14968#ifdef VBOX_WITH_GUEST_PROPS
14969 if ( aMachineState == MachineState_PoweredOff
14970 || aMachineState == MachineState_Aborted
14971 || aMachineState == MachineState_Teleported)
14972 {
14973 /* Make sure any transient guest properties get removed from the
14974 * property store on shutdown. */
14975
14976 HWData::GuestPropertyMap::const_iterator it;
14977 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14978 if (!fNeedsSaving)
14979 for (it = mHWData->mGuestProperties.begin();
14980 it != mHWData->mGuestProperties.end(); ++it)
14981 if ( (it->second.mFlags & guestProp::TRANSIENT)
14982 || (it->second.mFlags & guestProp::TRANSRESET))
14983 {
14984 fNeedsSaving = true;
14985 break;
14986 }
14987 if (fNeedsSaving)
14988 {
14989 mData->mCurrentStateModified = TRUE;
14990 stsFlags |= SaveSTS_CurStateModified;
14991 }
14992 }
14993#endif
14994
14995 rc = saveStateSettings(stsFlags);
14996
14997 if ( ( oldMachineState != MachineState_PoweredOff
14998 && oldMachineState != MachineState_Aborted
14999 && oldMachineState != MachineState_Teleported
15000 )
15001 && ( aMachineState == MachineState_PoweredOff
15002 || aMachineState == MachineState_Aborted
15003 || aMachineState == MachineState_Teleported
15004 )
15005 )
15006 {
15007 /* we've been shut down for any reason */
15008 /* no special action so far */
15009 }
15010
15011 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15012 LogFlowThisFuncLeave();
15013 return rc;
15014}
15015
15016/**
15017 * Sends the current machine state value to the VM process.
15018 *
15019 * @note Locks this object for reading, then calls a client process.
15020 */
15021HRESULT SessionMachine::updateMachineStateOnClient()
15022{
15023 AutoCaller autoCaller(this);
15024 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15025
15026 ComPtr<IInternalSessionControl> directControl;
15027 {
15028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15029 AssertReturn(!!mData, E_FAIL);
15030 directControl = mData->mSession.mDirectControl;
15031
15032 /* directControl may be already set to NULL here in #OnSessionEnd()
15033 * called too early by the direct session process while there is still
15034 * some operation (like deleting the snapshot) in progress. The client
15035 * process in this case is waiting inside Session::close() for the
15036 * "end session" process object to complete, while #uninit() called by
15037 * #checkForDeath() on the Watcher thread is waiting for the pending
15038 * operation to complete. For now, we accept this inconsistent behavior
15039 * and simply do nothing here. */
15040
15041 if (mData->mSession.mState == SessionState_Unlocking)
15042 return S_OK;
15043
15044 AssertReturn(!directControl.isNull(), E_FAIL);
15045 }
15046
15047 return directControl->UpdateMachineState(mData->mMachineState);
15048}
Note: See TracBrowser for help on using the repository browser.

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