VirtualBox

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

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

6813 - stage 5 - Make use of server side API wrapper code in all interfaces

  • 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 49951 2013-12-17 11:44:22Z 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->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->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->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->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->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->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->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->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->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->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->onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->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
1101 StringsList llGroups;
1102 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1103 if (FAILED(rc))
1104 return rc;
1105
1106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1107
1108 // changing machine groups is possible while the VM is offline
1109 rc = checkStateDependency(OfflineStateDep);
1110 if (FAILED(rc)) return rc;
1111
1112 setModified(IsModified_MachineData);
1113 mUserData.backup();
1114 mUserData->s.llGroups = llGroups;
1115
1116 return S_OK;
1117}
1118
1119STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1120{
1121 CheckComArgOutPointerValid(aOSTypeId);
1122
1123 AutoCaller autoCaller(this);
1124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1125
1126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1127
1128 mUserData->s.strOsType.cloneTo(aOSTypeId);
1129
1130 return S_OK;
1131}
1132
1133STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1134{
1135 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1136
1137 AutoCaller autoCaller(this);
1138 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1139
1140 /* look up the object by Id to check it is valid */
1141 ComPtr<IGuestOSType> guestOSType;
1142 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1143 if (FAILED(rc)) return rc;
1144
1145 /* when setting, always use the "etalon" value for consistency -- lookup
1146 * by ID is case-insensitive and the input value may have different case */
1147 Bstr osTypeId;
1148 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1149 if (FAILED(rc)) return rc;
1150
1151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1152
1153 rc = checkStateDependency(MutableStateDep);
1154 if (FAILED(rc)) return rc;
1155
1156 setModified(IsModified_MachineData);
1157 mUserData.backup();
1158 mUserData->s.strOsType = osTypeId;
1159
1160 return S_OK;
1161}
1162
1163
1164STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1165{
1166 CheckComArgOutPointerValid(aFirmwareType);
1167
1168 AutoCaller autoCaller(this);
1169 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1170
1171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1172
1173 *aFirmwareType = mHWData->mFirmwareType;
1174
1175 return S_OK;
1176}
1177
1178STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1179{
1180 AutoCaller autoCaller(this);
1181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 HRESULT rc = checkStateDependency(MutableStateDep);
1185 if (FAILED(rc)) return rc;
1186
1187 setModified(IsModified_MachineData);
1188 mHWData.backup();
1189 mHWData->mFirmwareType = aFirmwareType;
1190
1191 return S_OK;
1192}
1193
1194STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1195{
1196 CheckComArgOutPointerValid(aKeyboardHIDType);
1197
1198 AutoCaller autoCaller(this);
1199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1200
1201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1204
1205 return S_OK;
1206}
1207
1208STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1209{
1210 AutoCaller autoCaller(this);
1211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1213
1214 HRESULT rc = checkStateDependency(MutableStateDep);
1215 if (FAILED(rc)) return rc;
1216
1217 setModified(IsModified_MachineData);
1218 mHWData.backup();
1219 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1220
1221 return S_OK;
1222}
1223
1224STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1225{
1226 CheckComArgOutPointerValid(aPointingHIDType);
1227
1228 AutoCaller autoCaller(this);
1229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1230
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 *aPointingHIDType = mHWData->mPointingHIDType;
1234
1235 return S_OK;
1236}
1237
1238STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1239{
1240 AutoCaller autoCaller(this);
1241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1243
1244 HRESULT rc = checkStateDependency(MutableStateDep);
1245 if (FAILED(rc)) return rc;
1246
1247 setModified(IsModified_MachineData);
1248 mHWData.backup();
1249 mHWData->mPointingHIDType = aPointingHIDType;
1250
1251 return S_OK;
1252}
1253
1254STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1255{
1256 CheckComArgOutPointerValid(aChipsetType);
1257
1258 AutoCaller autoCaller(this);
1259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1260
1261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 *aChipsetType = mHWData->mChipsetType;
1264
1265 return S_OK;
1266}
1267
1268STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1269{
1270 AutoCaller autoCaller(this);
1271 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1273
1274 HRESULT rc = checkStateDependency(MutableStateDep);
1275 if (FAILED(rc)) return rc;
1276
1277 if (aChipsetType != mHWData->mChipsetType)
1278 {
1279 setModified(IsModified_MachineData);
1280 mHWData.backup();
1281 mHWData->mChipsetType = aChipsetType;
1282
1283 // Resize network adapter array, to be finalized on commit/rollback.
1284 // We must not throw away entries yet, otherwise settings are lost
1285 // without a way to roll back.
1286 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1287 size_t oldCount = mNetworkAdapters.size();
1288 if (newCount > oldCount)
1289 {
1290 mNetworkAdapters.resize(newCount);
1291 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1292 {
1293 unconst(mNetworkAdapters[slot]).createObject();
1294 mNetworkAdapters[slot]->init(this, slot);
1295 }
1296 }
1297 }
1298
1299 return S_OK;
1300}
1301
1302STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1303{
1304 CheckComArgOutPointerValid(aHWVersion);
1305
1306 AutoCaller autoCaller(this);
1307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1308
1309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1310
1311 mHWData->mHWVersion.cloneTo(aHWVersion);
1312
1313 return S_OK;
1314}
1315
1316STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1317{
1318 /* check known version */
1319 Utf8Str hwVersion = aHWVersion;
1320 if ( hwVersion.compare("1") != 0
1321 && hwVersion.compare("2") != 0)
1322 return setError(E_INVALIDARG,
1323 tr("Invalid hardware version: %ls\n"), aHWVersion);
1324
1325 AutoCaller autoCaller(this);
1326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1327
1328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1329
1330 HRESULT rc = checkStateDependency(MutableStateDep);
1331 if (FAILED(rc)) return rc;
1332
1333 setModified(IsModified_MachineData);
1334 mHWData.backup();
1335 mHWData->mHWVersion = hwVersion;
1336
1337 return S_OK;
1338}
1339
1340STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1341{
1342 CheckComArgOutPointerValid(aUUID);
1343
1344 AutoCaller autoCaller(this);
1345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1346
1347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1348
1349 if (!mHWData->mHardwareUUID.isZero())
1350 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1351 else
1352 mData->mUuid.toUtf16().cloneTo(aUUID);
1353
1354 return S_OK;
1355}
1356
1357STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1358{
1359 Guid hardwareUUID(aUUID);
1360 if (!hardwareUUID.isValid())
1361 return E_INVALIDARG;
1362
1363 AutoCaller autoCaller(this);
1364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1365
1366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1367
1368 HRESULT rc = checkStateDependency(MutableStateDep);
1369 if (FAILED(rc)) return rc;
1370
1371 setModified(IsModified_MachineData);
1372 mHWData.backup();
1373 if (hardwareUUID == mData->mUuid)
1374 mHWData->mHardwareUUID.clear();
1375 else
1376 mHWData->mHardwareUUID = hardwareUUID;
1377
1378 return S_OK;
1379}
1380
1381STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1382{
1383 CheckComArgOutPointerValid(memorySize);
1384
1385 AutoCaller autoCaller(this);
1386 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1387
1388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1389
1390 *memorySize = mHWData->mMemorySize;
1391
1392 return S_OK;
1393}
1394
1395STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1396{
1397 /* check RAM limits */
1398 if ( memorySize < MM_RAM_MIN_IN_MB
1399 || memorySize > MM_RAM_MAX_IN_MB
1400 )
1401 return setError(E_INVALIDARG,
1402 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1403 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1404
1405 AutoCaller autoCaller(this);
1406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1407
1408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 HRESULT rc = checkStateDependency(MutableStateDep);
1411 if (FAILED(rc)) return rc;
1412
1413 setModified(IsModified_MachineData);
1414 mHWData.backup();
1415 mHWData->mMemorySize = memorySize;
1416
1417 return S_OK;
1418}
1419
1420STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1421{
1422 CheckComArgOutPointerValid(CPUCount);
1423
1424 AutoCaller autoCaller(this);
1425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1426
1427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1428
1429 *CPUCount = mHWData->mCPUCount;
1430
1431 return S_OK;
1432}
1433
1434STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1435{
1436 /* check CPU limits */
1437 if ( CPUCount < SchemaDefs::MinCPUCount
1438 || CPUCount > SchemaDefs::MaxCPUCount
1439 )
1440 return setError(E_INVALIDARG,
1441 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1442 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1443
1444 AutoCaller autoCaller(this);
1445 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1446
1447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1448
1449 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1450 if (mHWData->mCPUHotPlugEnabled)
1451 {
1452 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1453 {
1454 if (mHWData->mCPUAttached[idx])
1455 return setError(E_INVALIDARG,
1456 tr("There is still a CPU attached to socket %lu."
1457 "Detach the CPU before removing the socket"),
1458 CPUCount, idx+1);
1459 }
1460 }
1461
1462 HRESULT rc = checkStateDependency(MutableStateDep);
1463 if (FAILED(rc)) return rc;
1464
1465 setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mCPUCount = CPUCount;
1468
1469 return S_OK;
1470}
1471
1472STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1473{
1474 CheckComArgOutPointerValid(aExecutionCap);
1475
1476 AutoCaller autoCaller(this);
1477 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1478
1479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1480
1481 *aExecutionCap = mHWData->mCpuExecutionCap;
1482
1483 return S_OK;
1484}
1485
1486STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1487{
1488 HRESULT rc = S_OK;
1489
1490 /* check throttle limits */
1491 if ( aExecutionCap < 1
1492 || aExecutionCap > 100
1493 )
1494 return setError(E_INVALIDARG,
1495 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1496 aExecutionCap, 1, 100);
1497
1498 AutoCaller autoCaller(this);
1499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1500
1501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1502
1503 alock.release();
1504 rc = onCPUExecutionCapChange(aExecutionCap);
1505 alock.acquire();
1506 if (FAILED(rc)) return rc;
1507
1508 setModified(IsModified_MachineData);
1509 mHWData.backup();
1510 mHWData->mCpuExecutionCap = aExecutionCap;
1511
1512 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1513 if (Global::IsOnline(mData->mMachineState))
1514 saveSettings(NULL);
1515
1516 return S_OK;
1517}
1518
1519
1520STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1521{
1522 CheckComArgOutPointerValid(aEnabled);
1523
1524 AutoCaller autoCaller(this);
1525 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1526
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aEnabled = mHWData->mCPUHotPlugEnabled;
1530
1531 return S_OK;
1532}
1533
1534STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1535{
1536 HRESULT rc = S_OK;
1537
1538 AutoCaller autoCaller(this);
1539 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1540
1541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1542
1543 rc = checkStateDependency(MutableStateDep);
1544 if (FAILED(rc)) return rc;
1545
1546 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1547 {
1548 if (aEnabled)
1549 {
1550 setModified(IsModified_MachineData);
1551 mHWData.backup();
1552
1553 /* Add the amount of CPUs currently attached */
1554 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1555 {
1556 mHWData->mCPUAttached[i] = true;
1557 }
1558 }
1559 else
1560 {
1561 /*
1562 * We can disable hotplug only if the amount of maximum CPUs is equal
1563 * to the amount of attached CPUs
1564 */
1565 unsigned cCpusAttached = 0;
1566 unsigned iHighestId = 0;
1567
1568 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1569 {
1570 if (mHWData->mCPUAttached[i])
1571 {
1572 cCpusAttached++;
1573 iHighestId = i;
1574 }
1575 }
1576
1577 if ( (cCpusAttached != mHWData->mCPUCount)
1578 || (iHighestId >= mHWData->mCPUCount))
1579 return setError(E_INVALIDARG,
1580 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1581
1582 setModified(IsModified_MachineData);
1583 mHWData.backup();
1584 }
1585 }
1586
1587 mHWData->mCPUHotPlugEnabled = aEnabled;
1588
1589 return rc;
1590}
1591
1592STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1593{
1594#ifdef VBOX_WITH_USB_CARDREADER
1595 CheckComArgOutPointerValid(aEnabled);
1596
1597 AutoCaller autoCaller(this);
1598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1599
1600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1601
1602 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1603
1604 return S_OK;
1605#else
1606 NOREF(aEnabled);
1607 return E_NOTIMPL;
1608#endif
1609}
1610
1611STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1612{
1613#ifdef VBOX_WITH_USB_CARDREADER
1614 AutoCaller autoCaller(this);
1615 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 HRESULT rc = checkStateDependency(MutableStateDep);
1619 if (FAILED(rc)) return rc;
1620
1621 setModified(IsModified_MachineData);
1622 mHWData.backup();
1623 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1624
1625 return S_OK;
1626#else
1627 NOREF(aEnabled);
1628 return E_NOTIMPL;
1629#endif
1630}
1631
1632STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1633{
1634 CheckComArgOutPointerValid(aEnabled);
1635
1636 AutoCaller autoCaller(this);
1637 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1639
1640 *aEnabled = mHWData->mHPETEnabled;
1641
1642 return S_OK;
1643}
1644
1645STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1646{
1647 HRESULT rc = S_OK;
1648
1649 AutoCaller autoCaller(this);
1650 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1652
1653 rc = checkStateDependency(MutableStateDep);
1654 if (FAILED(rc)) return rc;
1655
1656 setModified(IsModified_MachineData);
1657 mHWData.backup();
1658
1659 mHWData->mHPETEnabled = aEnabled;
1660
1661 return rc;
1662}
1663
1664STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1665{
1666 AutoCaller autoCaller(this);
1667 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1668
1669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1670
1671 *fEnabled = mHWData->mVideoCaptureEnabled;
1672 return S_OK;
1673}
1674
1675STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1676{
1677 HRESULT rc = S_OK;
1678
1679 AutoCaller autoCaller(this);
1680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 setModified(IsModified_MachineData);
1684 mHWData.backup();
1685 mHWData->mVideoCaptureEnabled = fEnabled;
1686
1687 alock.release();
1688 rc = onVideoCaptureChange();
1689 alock.acquire();
1690 if (FAILED(rc))
1691 {
1692 /*
1693 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1694 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1695 * determine if it should start or stop capturing. Therefore we need to manually
1696 * undo change.
1697 */
1698 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1699 return rc;
1700 }
1701
1702 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1703 if (Global::IsOnline(mData->mMachineState))
1704 saveSettings(NULL);
1705
1706 return rc;
1707}
1708
1709STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1710{
1711 CheckComArgOutSafeArrayPointerValid(aScreens);
1712
1713 AutoCaller autoCaller(this);
1714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1715
1716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1717
1718 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1719 for (unsigned i = 0; i < screens.size(); i++)
1720 screens[i] = mHWData->maVideoCaptureScreens[i];
1721 screens.detachTo(ComSafeArrayOutArg(aScreens));
1722 return S_OK;
1723}
1724
1725STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1726{
1727 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1728 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1729 bool fChanged = false;
1730
1731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1732
1733 for (unsigned i = 0; i < screens.size(); i++)
1734 {
1735 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1736 {
1737 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1738 fChanged = true;
1739 }
1740 }
1741 if (fChanged)
1742 {
1743 alock.release();
1744 HRESULT rc = onVideoCaptureChange();
1745 alock.acquire();
1746 if (FAILED(rc)) return rc;
1747 setModified(IsModified_MachineData);
1748
1749 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1750 if (Global::IsOnline(mData->mMachineState))
1751 saveSettings(NULL);
1752 }
1753
1754 return S_OK;
1755}
1756
1757STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1758{
1759 AutoCaller autoCaller(this);
1760 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1761
1762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1763 if (mHWData->mVideoCaptureFile.isEmpty())
1764 {
1765 Utf8Str defaultFile;
1766 getDefaultVideoCaptureFile(defaultFile);
1767 defaultFile.cloneTo(apFile);
1768 }
1769 else
1770 mHWData->mVideoCaptureFile.cloneTo(apFile);
1771 return S_OK;
1772}
1773
1774STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1775{
1776 Utf8Str strFile(aFile);
1777 AutoCaller autoCaller(this);
1778 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1779
1780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1781
1782 if ( Global::IsOnline(mData->mMachineState)
1783 && mHWData->mVideoCaptureEnabled)
1784 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1785
1786 if (!RTPathStartsWithRoot(strFile.c_str()))
1787 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1788
1789 if (!strFile.isEmpty())
1790 {
1791 Utf8Str defaultFile;
1792 getDefaultVideoCaptureFile(defaultFile);
1793 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1794 strFile.setNull();
1795 }
1796
1797 setModified(IsModified_MachineData);
1798 mHWData.backup();
1799 mHWData->mVideoCaptureFile = strFile;
1800
1801 return S_OK;
1802}
1803
1804STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1805{
1806 AutoCaller autoCaller(this);
1807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1808
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 *aHorzRes = mHWData->mVideoCaptureWidth;
1811 return S_OK;
1812}
1813
1814STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1815{
1816 AutoCaller autoCaller(this);
1817 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1818
1819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1820
1821 if ( Global::IsOnline(mData->mMachineState)
1822 && mHWData->mVideoCaptureEnabled)
1823 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1824
1825 setModified(IsModified_MachineData);
1826 mHWData.backup();
1827 mHWData->mVideoCaptureWidth = aHorzRes;
1828
1829 return S_OK;
1830}
1831
1832STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1833{
1834 AutoCaller autoCaller(this);
1835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1836
1837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1838 *aVertRes = mHWData->mVideoCaptureHeight;
1839 return S_OK;
1840}
1841
1842STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1843{
1844 AutoCaller autoCaller(this);
1845 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1846
1847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1848
1849 if ( Global::IsOnline(mData->mMachineState)
1850 && mHWData->mVideoCaptureEnabled)
1851 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1852
1853 setModified(IsModified_MachineData);
1854 mHWData.backup();
1855 mHWData->mVideoCaptureHeight = aVertRes;
1856
1857 return S_OK;
1858}
1859
1860STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1861{
1862 AutoCaller autoCaller(this);
1863 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1864
1865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1866 *aRate = mHWData->mVideoCaptureRate;
1867 return S_OK;
1868}
1869
1870STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1871{
1872 AutoCaller autoCaller(this);
1873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1874
1875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 if ( Global::IsOnline(mData->mMachineState)
1878 && mHWData->mVideoCaptureEnabled)
1879 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1880
1881 setModified(IsModified_MachineData);
1882 mHWData.backup();
1883 mHWData->mVideoCaptureRate = aRate;
1884
1885 return S_OK;
1886}
1887
1888STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1889{
1890 AutoCaller autoCaller(this);
1891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1892
1893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1894 *aFPS = mHWData->mVideoCaptureFPS;
1895 return S_OK;
1896}
1897
1898STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1899{
1900 AutoCaller autoCaller(this);
1901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1902
1903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1904
1905 if ( Global::IsOnline(mData->mMachineState)
1906 && mHWData->mVideoCaptureEnabled)
1907 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1908
1909 setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mVideoCaptureFPS = aFPS;
1912
1913 return S_OK;
1914}
1915
1916STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1917{
1918 CheckComArgOutPointerValid(aGraphicsControllerType);
1919
1920 AutoCaller autoCaller(this);
1921 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1922
1923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1924
1925 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1926
1927 return S_OK;
1928}
1929
1930STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1931{
1932 switch (aGraphicsControllerType)
1933 {
1934 case GraphicsControllerType_Null:
1935 case GraphicsControllerType_VBoxVGA:
1936 break;
1937 default:
1938 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1939 }
1940
1941 AutoCaller autoCaller(this);
1942 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1943
1944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1945
1946 HRESULT rc = checkStateDependency(MutableStateDep);
1947 if (FAILED(rc)) return rc;
1948
1949 setModified(IsModified_MachineData);
1950 mHWData.backup();
1951 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1952
1953 return S_OK;
1954}
1955
1956STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1957{
1958 CheckComArgOutPointerValid(memorySize);
1959
1960 AutoCaller autoCaller(this);
1961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1962
1963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1964
1965 *memorySize = mHWData->mVRAMSize;
1966
1967 return S_OK;
1968}
1969
1970STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1971{
1972 /* check VRAM limits */
1973 if (memorySize < SchemaDefs::MinGuestVRAM ||
1974 memorySize > SchemaDefs::MaxGuestVRAM)
1975 return setError(E_INVALIDARG,
1976 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1977 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1978
1979 AutoCaller autoCaller(this);
1980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1981
1982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1983
1984 HRESULT rc = checkStateDependency(MutableStateDep);
1985 if (FAILED(rc)) return rc;
1986
1987 setModified(IsModified_MachineData);
1988 mHWData.backup();
1989 mHWData->mVRAMSize = memorySize;
1990
1991 return S_OK;
1992}
1993
1994/** @todo this method should not be public */
1995STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1996{
1997 CheckComArgOutPointerValid(memoryBalloonSize);
1998
1999 AutoCaller autoCaller(this);
2000 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2001
2002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2003
2004 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2005
2006 return S_OK;
2007}
2008
2009/**
2010 * Set the memory balloon size.
2011 *
2012 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2013 * we have to make sure that we never call IGuest from here.
2014 */
2015STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2016{
2017 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2018#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2019 /* check limits */
2020 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2021 return setError(E_INVALIDARG,
2022 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2023 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2024
2025 AutoCaller autoCaller(this);
2026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2027
2028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2029
2030 setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2033
2034 return S_OK;
2035#else
2036 NOREF(memoryBalloonSize);
2037 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2038#endif
2039}
2040
2041STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2042{
2043 CheckComArgOutPointerValid(aEnabled);
2044
2045 AutoCaller autoCaller(this);
2046 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2047
2048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 *aEnabled = mHWData->mPageFusionEnabled;
2051 return S_OK;
2052}
2053
2054STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2055{
2056#ifdef VBOX_WITH_PAGE_SHARING
2057 AutoCaller autoCaller(this);
2058 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2059
2060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2061
2062 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2063 setModified(IsModified_MachineData);
2064 mHWData.backup();
2065 mHWData->mPageFusionEnabled = aEnabled;
2066 return S_OK;
2067#else
2068 NOREF(aEnabled);
2069 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2070#endif
2071}
2072
2073STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2074{
2075 CheckComArgOutPointerValid(aEnabled);
2076
2077 AutoCaller autoCaller(this);
2078 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2079
2080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2081
2082 *aEnabled = mHWData->mAccelerate3DEnabled;
2083
2084 return S_OK;
2085}
2086
2087STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2088{
2089 AutoCaller autoCaller(this);
2090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2091
2092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2093
2094 HRESULT rc = checkStateDependency(MutableStateDep);
2095 if (FAILED(rc)) return rc;
2096
2097 /** @todo check validity! */
2098
2099 setModified(IsModified_MachineData);
2100 mHWData.backup();
2101 mHWData->mAccelerate3DEnabled = enable;
2102
2103 return S_OK;
2104}
2105
2106
2107STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2108{
2109 CheckComArgOutPointerValid(aEnabled);
2110
2111 AutoCaller autoCaller(this);
2112 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2113
2114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2115
2116 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2117
2118 return S_OK;
2119}
2120
2121STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2122{
2123 AutoCaller autoCaller(this);
2124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2125
2126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2127
2128 HRESULT rc = checkStateDependency(MutableStateDep);
2129 if (FAILED(rc)) return rc;
2130
2131 /** @todo check validity! */
2132
2133 setModified(IsModified_MachineData);
2134 mHWData.backup();
2135 mHWData->mAccelerate2DVideoEnabled = enable;
2136
2137 return S_OK;
2138}
2139
2140STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2141{
2142 CheckComArgOutPointerValid(monitorCount);
2143
2144 AutoCaller autoCaller(this);
2145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2146
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 *monitorCount = mHWData->mMonitorCount;
2150
2151 return S_OK;
2152}
2153
2154STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2155{
2156 /* make sure monitor count is a sensible number */
2157 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2158 return setError(E_INVALIDARG,
2159 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2160 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2161
2162 AutoCaller autoCaller(this);
2163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2164
2165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2166
2167 HRESULT rc = checkStateDependency(MutableStateDep);
2168 if (FAILED(rc)) return rc;
2169
2170 setModified(IsModified_MachineData);
2171 mHWData.backup();
2172 mHWData->mMonitorCount = monitorCount;
2173
2174 return S_OK;
2175}
2176
2177STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2178{
2179 CheckComArgOutPointerValid(biosSettings);
2180
2181 AutoCaller autoCaller(this);
2182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2183
2184 /* mBIOSSettings is constant during life time, no need to lock */
2185 mBIOSSettings.queryInterfaceTo(biosSettings);
2186
2187 return S_OK;
2188}
2189
2190STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2191{
2192 CheckComArgOutPointerValid(aVal);
2193
2194 AutoCaller autoCaller(this);
2195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2196
2197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2198
2199 switch (property)
2200 {
2201 case CPUPropertyType_PAE:
2202 *aVal = mHWData->mPAEEnabled;
2203 break;
2204
2205 case CPUPropertyType_Synthetic:
2206 *aVal = mHWData->mSyntheticCpu;
2207 break;
2208
2209 case CPUPropertyType_LongMode:
2210 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2211 *aVal = TRUE;
2212 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2213 *aVal = FALSE;
2214#if HC_ARCH_BITS == 64
2215 else
2216 *aVal = TRUE;
2217#else
2218 else
2219 {
2220 *aVal = FALSE;
2221
2222 ComPtr<IGuestOSType> ptrGuestOSType;
2223 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2224 if (SUCCEEDED(hrc2))
2225 {
2226 BOOL fIs64Bit = FALSE;
2227 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2228 if (SUCCEEDED(hrc2) && fIs64Bit)
2229 {
2230 ComObjPtr<Host> ptrHost = mParent->host();
2231 alock.release();
2232
2233 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2234 if (FAILED(hrc2))
2235 *aVal = FALSE;
2236 }
2237 }
2238 }
2239#endif
2240 break;
2241
2242 case CPUPropertyType_TripleFaultReset:
2243 *aVal = mHWData->mTripleFaultReset;
2244 break;
2245
2246 default:
2247 return E_INVALIDARG;
2248 }
2249 return S_OK;
2250}
2251
2252STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2253{
2254 AutoCaller autoCaller(this);
2255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2256
2257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2258
2259 HRESULT rc = checkStateDependency(MutableStateDep);
2260 if (FAILED(rc)) return rc;
2261
2262 switch (property)
2263 {
2264 case CPUPropertyType_PAE:
2265 setModified(IsModified_MachineData);
2266 mHWData.backup();
2267 mHWData->mPAEEnabled = !!aVal;
2268 break;
2269
2270 case CPUPropertyType_Synthetic:
2271 setModified(IsModified_MachineData);
2272 mHWData.backup();
2273 mHWData->mSyntheticCpu = !!aVal;
2274 break;
2275
2276 case CPUPropertyType_LongMode:
2277 setModified(IsModified_MachineData);
2278 mHWData.backup();
2279 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2280 break;
2281
2282 case CPUPropertyType_TripleFaultReset:
2283 setModified(IsModified_MachineData);
2284 mHWData.backup();
2285 mHWData->mTripleFaultReset = !!aVal;
2286 break;
2287
2288 default:
2289 return E_INVALIDARG;
2290 }
2291 return S_OK;
2292}
2293
2294STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2295{
2296 CheckComArgOutPointerValid(aValEax);
2297 CheckComArgOutPointerValid(aValEbx);
2298 CheckComArgOutPointerValid(aValEcx);
2299 CheckComArgOutPointerValid(aValEdx);
2300
2301 AutoCaller autoCaller(this);
2302 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2303
2304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2305
2306 switch(aId)
2307 {
2308 case 0x0:
2309 case 0x1:
2310 case 0x2:
2311 case 0x3:
2312 case 0x4:
2313 case 0x5:
2314 case 0x6:
2315 case 0x7:
2316 case 0x8:
2317 case 0x9:
2318 case 0xA:
2319 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2320 return E_INVALIDARG;
2321
2322 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2323 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2324 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2325 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2326 break;
2327
2328 case 0x80000000:
2329 case 0x80000001:
2330 case 0x80000002:
2331 case 0x80000003:
2332 case 0x80000004:
2333 case 0x80000005:
2334 case 0x80000006:
2335 case 0x80000007:
2336 case 0x80000008:
2337 case 0x80000009:
2338 case 0x8000000A:
2339 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2340 return E_INVALIDARG;
2341
2342 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2343 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2344 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2345 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2346 break;
2347
2348 default:
2349 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2350 }
2351 return S_OK;
2352}
2353
2354STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2355{
2356 AutoCaller autoCaller(this);
2357 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2358
2359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2360
2361 HRESULT rc = checkStateDependency(MutableStateDep);
2362 if (FAILED(rc)) return rc;
2363
2364 switch(aId)
2365 {
2366 case 0x0:
2367 case 0x1:
2368 case 0x2:
2369 case 0x3:
2370 case 0x4:
2371 case 0x5:
2372 case 0x6:
2373 case 0x7:
2374 case 0x8:
2375 case 0x9:
2376 case 0xA:
2377 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2378 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2379 setModified(IsModified_MachineData);
2380 mHWData.backup();
2381 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2382 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2383 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2384 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2385 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2386 break;
2387
2388 case 0x80000000:
2389 case 0x80000001:
2390 case 0x80000002:
2391 case 0x80000003:
2392 case 0x80000004:
2393 case 0x80000005:
2394 case 0x80000006:
2395 case 0x80000007:
2396 case 0x80000008:
2397 case 0x80000009:
2398 case 0x8000000A:
2399 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2400 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2401 setModified(IsModified_MachineData);
2402 mHWData.backup();
2403 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2404 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2405 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2406 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2407 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2408 break;
2409
2410 default:
2411 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2412 }
2413 return S_OK;
2414}
2415
2416STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2417{
2418 AutoCaller autoCaller(this);
2419 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2420
2421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2422
2423 HRESULT rc = checkStateDependency(MutableStateDep);
2424 if (FAILED(rc)) return rc;
2425
2426 switch(aId)
2427 {
2428 case 0x0:
2429 case 0x1:
2430 case 0x2:
2431 case 0x3:
2432 case 0x4:
2433 case 0x5:
2434 case 0x6:
2435 case 0x7:
2436 case 0x8:
2437 case 0x9:
2438 case 0xA:
2439 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2440 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2441 setModified(IsModified_MachineData);
2442 mHWData.backup();
2443 /* Invalidate leaf. */
2444 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2445 break;
2446
2447 case 0x80000000:
2448 case 0x80000001:
2449 case 0x80000002:
2450 case 0x80000003:
2451 case 0x80000004:
2452 case 0x80000005:
2453 case 0x80000006:
2454 case 0x80000007:
2455 case 0x80000008:
2456 case 0x80000009:
2457 case 0x8000000A:
2458 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2459 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2460 setModified(IsModified_MachineData);
2461 mHWData.backup();
2462 /* Invalidate leaf. */
2463 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2464 break;
2465
2466 default:
2467 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2468 }
2469 return S_OK;
2470}
2471
2472STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2473{
2474 AutoCaller autoCaller(this);
2475 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2476
2477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2478
2479 HRESULT rc = checkStateDependency(MutableStateDep);
2480 if (FAILED(rc)) return rc;
2481
2482 setModified(IsModified_MachineData);
2483 mHWData.backup();
2484
2485 /* Invalidate all standard leafs. */
2486 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2487 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2488
2489 /* Invalidate all extended leafs. */
2490 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2491 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2492
2493 return S_OK;
2494}
2495
2496STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2497{
2498 CheckComArgOutPointerValid(aVal);
2499
2500 AutoCaller autoCaller(this);
2501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2502
2503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 switch(property)
2506 {
2507 case HWVirtExPropertyType_Enabled:
2508 *aVal = mHWData->mHWVirtExEnabled;
2509 break;
2510
2511 case HWVirtExPropertyType_VPID:
2512 *aVal = mHWData->mHWVirtExVPIDEnabled;
2513 break;
2514
2515 case HWVirtExPropertyType_NestedPaging:
2516 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2517 break;
2518
2519 case HWVirtExPropertyType_UnrestrictedExecution:
2520 *aVal = mHWData->mHWVirtExUXEnabled;
2521 break;
2522
2523 case HWVirtExPropertyType_LargePages:
2524 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2525#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2526 *aVal = FALSE;
2527#endif
2528 break;
2529
2530 case HWVirtExPropertyType_Force:
2531 *aVal = mHWData->mHWVirtExForceEnabled;
2532 break;
2533
2534 default:
2535 return E_INVALIDARG;
2536 }
2537 return S_OK;
2538}
2539
2540STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2541{
2542 AutoCaller autoCaller(this);
2543 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2544
2545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2546
2547 HRESULT rc = checkStateDependency(MutableStateDep);
2548 if (FAILED(rc)) return rc;
2549
2550 switch(property)
2551 {
2552 case HWVirtExPropertyType_Enabled:
2553 setModified(IsModified_MachineData);
2554 mHWData.backup();
2555 mHWData->mHWVirtExEnabled = !!aVal;
2556 break;
2557
2558 case HWVirtExPropertyType_VPID:
2559 setModified(IsModified_MachineData);
2560 mHWData.backup();
2561 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2562 break;
2563
2564 case HWVirtExPropertyType_NestedPaging:
2565 setModified(IsModified_MachineData);
2566 mHWData.backup();
2567 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2568 break;
2569
2570 case HWVirtExPropertyType_UnrestrictedExecution:
2571 setModified(IsModified_MachineData);
2572 mHWData.backup();
2573 mHWData->mHWVirtExUXEnabled = !!aVal;
2574 break;
2575
2576 case HWVirtExPropertyType_LargePages:
2577 setModified(IsModified_MachineData);
2578 mHWData.backup();
2579 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2580 break;
2581
2582 case HWVirtExPropertyType_Force:
2583 setModified(IsModified_MachineData);
2584 mHWData.backup();
2585 mHWData->mHWVirtExForceEnabled = !!aVal;
2586 break;
2587
2588 default:
2589 return E_INVALIDARG;
2590 }
2591
2592 return S_OK;
2593}
2594
2595STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2596{
2597 CheckComArgOutPointerValid(aSnapshotFolder);
2598
2599 AutoCaller autoCaller(this);
2600 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2601
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 Utf8Str strFullSnapshotFolder;
2605 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2606 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2607
2608 return S_OK;
2609}
2610
2611STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2612{
2613 /* @todo (r=dmik):
2614 * 1. Allow to change the name of the snapshot folder containing snapshots
2615 * 2. Rename the folder on disk instead of just changing the property
2616 * value (to be smart and not to leave garbage). Note that it cannot be
2617 * done here because the change may be rolled back. Thus, the right
2618 * place is #saveSettings().
2619 */
2620
2621 AutoCaller autoCaller(this);
2622 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2623
2624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2625
2626 HRESULT rc = checkStateDependency(MutableStateDep);
2627 if (FAILED(rc)) return rc;
2628
2629 if (!mData->mCurrentSnapshot.isNull())
2630 return setError(E_FAIL,
2631 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2632
2633 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2634
2635 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2636 if (strSnapshotFolder.isEmpty())
2637 strSnapshotFolder = "Snapshots";
2638 int vrc = calculateFullPath(strSnapshotFolder,
2639 strSnapshotFolder);
2640 if (RT_FAILURE(vrc))
2641 return setError(E_FAIL,
2642 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2643 aSnapshotFolder, vrc);
2644
2645 setModified(IsModified_MachineData);
2646 mUserData.backup();
2647
2648 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2649
2650 return S_OK;
2651}
2652
2653STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2654{
2655 CheckComArgOutSafeArrayPointerValid(aAttachments);
2656
2657 AutoCaller autoCaller(this);
2658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2659
2660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2663 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2664
2665 return S_OK;
2666}
2667
2668STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2669{
2670 CheckComArgOutPointerValid(vrdeServer);
2671
2672 AutoCaller autoCaller(this);
2673 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2674
2675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
2677 Assert(!!mVRDEServer);
2678 mVRDEServer.queryInterfaceTo(vrdeServer);
2679
2680 return S_OK;
2681}
2682
2683STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2684{
2685 CheckComArgOutPointerValid(audioAdapter);
2686
2687 AutoCaller autoCaller(this);
2688 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2689
2690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 mAudioAdapter.queryInterfaceTo(audioAdapter);
2693 return S_OK;
2694}
2695
2696STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2697{
2698#ifdef VBOX_WITH_VUSB
2699 CheckComArgOutPointerValid(aUSBControllers);
2700
2701 AutoCaller autoCaller(this);
2702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2703
2704 clearError();
2705 MultiResult rc(S_OK);
2706
2707# ifdef VBOX_WITH_USB
2708 rc = mParent->host()->i_checkUSBProxyService();
2709 if (FAILED(rc)) return rc;
2710# endif
2711
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2715 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2716 return S_OK;
2717#else
2718 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2719 * extended error info to indicate that USB is simply not available
2720 * (w/o treating it as a failure), for example, as in OSE */
2721 NOREF(aUSBControllers);
2722 ReturnComNotImplemented();
2723#endif /* VBOX_WITH_VUSB */
2724}
2725
2726STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2727{
2728#ifdef VBOX_WITH_VUSB
2729 CheckComArgOutPointerValid(aUSBDeviceFilters);
2730
2731 AutoCaller autoCaller(this);
2732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2733
2734 clearError();
2735 MultiResult rc(S_OK);
2736
2737# ifdef VBOX_WITH_USB
2738 rc = mParent->host()->i_checkUSBProxyService();
2739 if (FAILED(rc)) return rc;
2740# endif
2741
2742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2743
2744 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2745#else
2746 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2747 * extended error info to indicate that USB is simply not available
2748 * (w/o treating it as a failure), for example, as in OSE */
2749 NOREF(aUSBDeviceFilters);
2750 ReturnComNotImplemented();
2751#endif /* VBOX_WITH_VUSB */
2752}
2753
2754STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2755{
2756 CheckComArgOutPointerValid(aFilePath);
2757
2758 AutoLimitedCaller autoCaller(this);
2759 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2760
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 mData->m_strConfigFileFull.cloneTo(aFilePath);
2764 return S_OK;
2765}
2766
2767STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2768{
2769 CheckComArgOutPointerValid(aModified);
2770
2771 AutoCaller autoCaller(this);
2772 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2773
2774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2775
2776 HRESULT rc = checkStateDependency(MutableStateDep);
2777 if (FAILED(rc)) return rc;
2778
2779 if (!mData->pMachineConfigFile->fileExists())
2780 // this is a new machine, and no config file exists yet:
2781 *aModified = TRUE;
2782 else
2783 *aModified = (mData->flModifications != 0);
2784
2785 return S_OK;
2786}
2787
2788STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2789{
2790 CheckComArgOutPointerValid(aSessionState);
2791
2792 AutoCaller autoCaller(this);
2793 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2794
2795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2796
2797 *aSessionState = mData->mSession.mState;
2798
2799 return S_OK;
2800}
2801
2802STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2803{
2804 CheckComArgOutPointerValid(aSessionType);
2805
2806 AutoCaller autoCaller(this);
2807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2808
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 mData->mSession.mType.cloneTo(aSessionType);
2812
2813 return S_OK;
2814}
2815
2816STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2817{
2818 CheckComArgOutPointerValid(aSessionPID);
2819
2820 AutoCaller autoCaller(this);
2821 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2822
2823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2824
2825 *aSessionPID = mData->mSession.mPID;
2826
2827 return S_OK;
2828}
2829
2830STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2831{
2832 CheckComArgOutPointerValid(machineState);
2833
2834 AutoCaller autoCaller(this);
2835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2836
2837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2838
2839 *machineState = mData->mMachineState;
2840
2841 return S_OK;
2842}
2843
2844STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2845{
2846 CheckComArgOutPointerValid(aLastStateChange);
2847
2848 AutoCaller autoCaller(this);
2849 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2850
2851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2854
2855 return S_OK;
2856}
2857
2858STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2859{
2860 CheckComArgOutPointerValid(aStateFilePath);
2861
2862 AutoCaller autoCaller(this);
2863 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2864
2865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2866
2867 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2868
2869 return S_OK;
2870}
2871
2872STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2873{
2874 CheckComArgOutPointerValid(aLogFolder);
2875
2876 AutoCaller autoCaller(this);
2877 AssertComRCReturnRC(autoCaller.rc());
2878
2879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 Utf8Str logFolder;
2882 getLogFolder(logFolder);
2883 logFolder.cloneTo(aLogFolder);
2884
2885 return S_OK;
2886}
2887
2888STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2889{
2890 CheckComArgOutPointerValid(aCurrentSnapshot);
2891
2892 AutoCaller autoCaller(this);
2893 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2894
2895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2896
2897 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2898
2899 return S_OK;
2900}
2901
2902STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2903{
2904 CheckComArgOutPointerValid(aSnapshotCount);
2905
2906 AutoCaller autoCaller(this);
2907 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2908
2909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2910
2911 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2912 ? 0
2913 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2914
2915 return S_OK;
2916}
2917
2918STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2919{
2920 CheckComArgOutPointerValid(aCurrentStateModified);
2921
2922 AutoCaller autoCaller(this);
2923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2924
2925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 /* Note: for machines with no snapshots, we always return FALSE
2928 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2929 * reasons :) */
2930
2931 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2932 ? FALSE
2933 : mData->mCurrentStateModified;
2934
2935 return S_OK;
2936}
2937
2938STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2939{
2940 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2941
2942 AutoCaller autoCaller(this);
2943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2944
2945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2946
2947 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2948 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2949
2950 return S_OK;
2951}
2952
2953STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2954{
2955 CheckComArgOutPointerValid(aClipboardMode);
2956
2957 AutoCaller autoCaller(this);
2958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2959
2960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2961
2962 *aClipboardMode = mHWData->mClipboardMode;
2963
2964 return S_OK;
2965}
2966
2967STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2968{
2969 HRESULT rc = S_OK;
2970
2971 AutoCaller autoCaller(this);
2972 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2973
2974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2975
2976 alock.release();
2977 rc = onClipboardModeChange(aClipboardMode);
2978 alock.acquire();
2979 if (FAILED(rc)) return rc;
2980
2981 setModified(IsModified_MachineData);
2982 mHWData.backup();
2983 mHWData->mClipboardMode = aClipboardMode;
2984
2985 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2986 if (Global::IsOnline(mData->mMachineState))
2987 saveSettings(NULL);
2988
2989 return S_OK;
2990}
2991
2992STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2993{
2994 CheckComArgOutPointerValid(aDragAndDropMode);
2995
2996 AutoCaller autoCaller(this);
2997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2998
2999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3000
3001 *aDragAndDropMode = mHWData->mDragAndDropMode;
3002
3003 return S_OK;
3004}
3005
3006STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3007{
3008 HRESULT rc = S_OK;
3009
3010 AutoCaller autoCaller(this);
3011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3012
3013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3014
3015 alock.release();
3016 rc = onDragAndDropModeChange(aDragAndDropMode);
3017 alock.acquire();
3018 if (FAILED(rc)) return rc;
3019
3020 setModified(IsModified_MachineData);
3021 mHWData.backup();
3022 mHWData->mDragAndDropMode = aDragAndDropMode;
3023
3024 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3025 if (Global::IsOnline(mData->mMachineState))
3026 saveSettings(NULL);
3027
3028 return S_OK;
3029}
3030
3031STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3032{
3033 CheckComArgOutPointerValid(aPatterns);
3034
3035 AutoCaller autoCaller(this);
3036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3037
3038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3039
3040 try
3041 {
3042 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3043 }
3044 catch (...)
3045 {
3046 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3047 }
3048
3049 return S_OK;
3050}
3051
3052STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3053{
3054 AutoCaller autoCaller(this);
3055 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3056
3057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 HRESULT rc = checkStateDependency(MutableStateDep);
3060 if (FAILED(rc)) return rc;
3061
3062 setModified(IsModified_MachineData);
3063 mHWData.backup();
3064 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3065 return rc;
3066}
3067
3068STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3069{
3070 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3071
3072 AutoCaller autoCaller(this);
3073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3074
3075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3076
3077 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3078 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3079
3080 return S_OK;
3081}
3082
3083STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3084{
3085 CheckComArgOutPointerValid(aEnabled);
3086
3087 AutoCaller autoCaller(this);
3088 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3089
3090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3091
3092 *aEnabled = mUserData->s.fTeleporterEnabled;
3093
3094 return S_OK;
3095}
3096
3097STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3098{
3099 AutoCaller autoCaller(this);
3100 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3101
3102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3103
3104 /* Only allow it to be set to true when PoweredOff or Aborted.
3105 (Clearing it is always permitted.) */
3106 if ( aEnabled
3107 && mData->mRegistered
3108 && ( !isSessionMachine()
3109 || ( mData->mMachineState != MachineState_PoweredOff
3110 && mData->mMachineState != MachineState_Teleported
3111 && mData->mMachineState != MachineState_Aborted
3112 )
3113 )
3114 )
3115 return setError(VBOX_E_INVALID_VM_STATE,
3116 tr("The machine is not powered off (state is %s)"),
3117 Global::stringifyMachineState(mData->mMachineState));
3118
3119 setModified(IsModified_MachineData);
3120 mUserData.backup();
3121 mUserData->s.fTeleporterEnabled = !!aEnabled;
3122
3123 return S_OK;
3124}
3125
3126STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3127{
3128 CheckComArgOutPointerValid(aPort);
3129
3130 AutoCaller autoCaller(this);
3131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3132
3133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3134
3135 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3136
3137 return S_OK;
3138}
3139
3140STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3141{
3142 if (aPort >= _64K)
3143 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3144
3145 AutoCaller autoCaller(this);
3146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3147
3148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3149
3150 HRESULT rc = checkStateDependency(MutableStateDep);
3151 if (FAILED(rc)) return rc;
3152
3153 setModified(IsModified_MachineData);
3154 mUserData.backup();
3155 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3156
3157 return S_OK;
3158}
3159
3160STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3161{
3162 CheckComArgOutPointerValid(aAddress);
3163
3164 AutoCaller autoCaller(this);
3165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3166
3167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3168
3169 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3170
3171 return S_OK;
3172}
3173
3174STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3175{
3176 AutoCaller autoCaller(this);
3177 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3178
3179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3180
3181 HRESULT rc = checkStateDependency(MutableStateDep);
3182 if (FAILED(rc)) return rc;
3183
3184 setModified(IsModified_MachineData);
3185 mUserData.backup();
3186 mUserData->s.strTeleporterAddress = aAddress;
3187
3188 return S_OK;
3189}
3190
3191STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3192{
3193 CheckComArgOutPointerValid(aPassword);
3194
3195 AutoCaller autoCaller(this);
3196 HRESULT hrc = autoCaller.rc();
3197 if (SUCCEEDED(hrc))
3198 {
3199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3200 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3201 }
3202
3203 return hrc;
3204}
3205
3206STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3207{
3208 /*
3209 * Hash the password first.
3210 */
3211 Utf8Str strPassword(aPassword);
3212 if (!strPassword.isEmpty())
3213 {
3214 if (VBoxIsPasswordHashed(&strPassword))
3215 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3216 VBoxHashPassword(&strPassword);
3217 }
3218
3219 /*
3220 * Do the update.
3221 */
3222 AutoCaller autoCaller(this);
3223 HRESULT hrc = autoCaller.rc();
3224 if (SUCCEEDED(hrc))
3225 {
3226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3227 hrc = checkStateDependency(MutableStateDep);
3228 if (SUCCEEDED(hrc))
3229 {
3230 setModified(IsModified_MachineData);
3231 mUserData.backup();
3232 mUserData->s.strTeleporterPassword = strPassword;
3233 }
3234 }
3235
3236 return hrc;
3237}
3238
3239STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3240{
3241 CheckComArgOutPointerValid(aState);
3242
3243 AutoCaller autoCaller(this);
3244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3245
3246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3247
3248 *aState = mUserData->s.enmFaultToleranceState;
3249 return S_OK;
3250}
3251
3252STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3253{
3254 AutoCaller autoCaller(this);
3255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3256
3257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3258
3259 /* @todo deal with running state change. */
3260 HRESULT rc = checkStateDependency(MutableStateDep);
3261 if (FAILED(rc)) return rc;
3262
3263 setModified(IsModified_MachineData);
3264 mUserData.backup();
3265 mUserData->s.enmFaultToleranceState = aState;
3266 return S_OK;
3267}
3268
3269STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3270{
3271 CheckComArgOutPointerValid(aAddress);
3272
3273 AutoCaller autoCaller(this);
3274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3275
3276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3277
3278 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3279 return S_OK;
3280}
3281
3282STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3283{
3284 AutoCaller autoCaller(this);
3285 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3286
3287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3288
3289 /* @todo deal with running state change. */
3290 HRESULT rc = checkStateDependency(MutableStateDep);
3291 if (FAILED(rc)) return rc;
3292
3293 setModified(IsModified_MachineData);
3294 mUserData.backup();
3295 mUserData->s.strFaultToleranceAddress = aAddress;
3296 return S_OK;
3297}
3298
3299STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3300{
3301 CheckComArgOutPointerValid(aPort);
3302
3303 AutoCaller autoCaller(this);
3304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3305
3306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3307
3308 *aPort = mUserData->s.uFaultTolerancePort;
3309 return S_OK;
3310}
3311
3312STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3313{
3314 AutoCaller autoCaller(this);
3315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3316
3317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3318
3319 /* @todo deal with running state change. */
3320 HRESULT rc = checkStateDependency(MutableStateDep);
3321 if (FAILED(rc)) return rc;
3322
3323 setModified(IsModified_MachineData);
3324 mUserData.backup();
3325 mUserData->s.uFaultTolerancePort = aPort;
3326 return S_OK;
3327}
3328
3329STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3330{
3331 CheckComArgOutPointerValid(aPassword);
3332
3333 AutoCaller autoCaller(this);
3334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3335
3336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3337
3338 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3339
3340 return S_OK;
3341}
3342
3343STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3344{
3345 AutoCaller autoCaller(this);
3346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3347
3348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3349
3350 /* @todo deal with running state change. */
3351 HRESULT rc = checkStateDependency(MutableStateDep);
3352 if (FAILED(rc)) return rc;
3353
3354 setModified(IsModified_MachineData);
3355 mUserData.backup();
3356 mUserData->s.strFaultTolerancePassword = aPassword;
3357
3358 return S_OK;
3359}
3360
3361STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3362{
3363 CheckComArgOutPointerValid(aInterval);
3364
3365 AutoCaller autoCaller(this);
3366 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3367
3368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3369
3370 *aInterval = mUserData->s.uFaultToleranceInterval;
3371 return S_OK;
3372}
3373
3374STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3375{
3376 AutoCaller autoCaller(this);
3377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3378
3379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3380
3381 /* @todo deal with running state change. */
3382 HRESULT rc = checkStateDependency(MutableStateDep);
3383 if (FAILED(rc)) return rc;
3384
3385 setModified(IsModified_MachineData);
3386 mUserData.backup();
3387 mUserData->s.uFaultToleranceInterval = aInterval;
3388 return S_OK;
3389}
3390
3391STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3392{
3393 CheckComArgOutPointerValid(aEnabled);
3394
3395 AutoCaller autoCaller(this);
3396 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3397
3398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3399
3400 *aEnabled = mUserData->s.fRTCUseUTC;
3401
3402 return S_OK;
3403}
3404
3405STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3406{
3407 AutoCaller autoCaller(this);
3408 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3409
3410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3411
3412 /* Only allow it to be set to true when PoweredOff or Aborted.
3413 (Clearing it is always permitted.) */
3414 if ( aEnabled
3415 && mData->mRegistered
3416 && ( !isSessionMachine()
3417 || ( mData->mMachineState != MachineState_PoweredOff
3418 && mData->mMachineState != MachineState_Teleported
3419 && mData->mMachineState != MachineState_Aborted
3420 )
3421 )
3422 )
3423 return setError(VBOX_E_INVALID_VM_STATE,
3424 tr("The machine is not powered off (state is %s)"),
3425 Global::stringifyMachineState(mData->mMachineState));
3426
3427 setModified(IsModified_MachineData);
3428 mUserData.backup();
3429 mUserData->s.fRTCUseUTC = !!aEnabled;
3430
3431 return S_OK;
3432}
3433
3434STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3435{
3436 CheckComArgOutPointerValid(aEnabled);
3437
3438 AutoCaller autoCaller(this);
3439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3440
3441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3442
3443 *aEnabled = mHWData->mIOCacheEnabled;
3444
3445 return S_OK;
3446}
3447
3448STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3449{
3450 AutoCaller autoCaller(this);
3451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3452
3453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3454
3455 HRESULT rc = checkStateDependency(MutableStateDep);
3456 if (FAILED(rc)) return rc;
3457
3458 setModified(IsModified_MachineData);
3459 mHWData.backup();
3460 mHWData->mIOCacheEnabled = aEnabled;
3461
3462 return S_OK;
3463}
3464
3465STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3466{
3467 CheckComArgOutPointerValid(aIOCacheSize);
3468
3469 AutoCaller autoCaller(this);
3470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3471
3472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3473
3474 *aIOCacheSize = mHWData->mIOCacheSize;
3475
3476 return S_OK;
3477}
3478
3479STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3480{
3481 AutoCaller autoCaller(this);
3482 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3483
3484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3485
3486 HRESULT rc = checkStateDependency(MutableStateDep);
3487 if (FAILED(rc)) return rc;
3488
3489 setModified(IsModified_MachineData);
3490 mHWData.backup();
3491 mHWData->mIOCacheSize = aIOCacheSize;
3492
3493 return S_OK;
3494}
3495
3496
3497/**
3498 * @note Locks objects!
3499 */
3500STDMETHODIMP Machine::LockMachine(ISession *aSession,
3501 LockType_T lockType)
3502{
3503 CheckComArgNotNull(aSession);
3504
3505 AutoCaller autoCaller(this);
3506 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3507
3508 /* check the session state */
3509 SessionState_T state;
3510 HRESULT rc = aSession->COMGETTER(State)(&state);
3511 if (FAILED(rc)) return rc;
3512
3513 if (state != SessionState_Unlocked)
3514 return setError(VBOX_E_INVALID_OBJECT_STATE,
3515 tr("The given session is busy"));
3516
3517 // get the client's IInternalSessionControl interface
3518 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3519 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3520 E_INVALIDARG);
3521
3522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3523
3524 if (!mData->mRegistered)
3525 return setError(E_UNEXPECTED,
3526 tr("The machine '%s' is not registered"),
3527 mUserData->s.strName.c_str());
3528
3529 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3530
3531 SessionState_T oldState = mData->mSession.mState;
3532 /* Hack: in case the session is closing and there is a progress object
3533 * which allows waiting for the session to be closed, take the opportunity
3534 * and do a limited wait (max. 1 second). This helps a lot when the system
3535 * is busy and thus session closing can take a little while. */
3536 if ( mData->mSession.mState == SessionState_Unlocking
3537 && mData->mSession.mProgress)
3538 {
3539 alock.release();
3540 mData->mSession.mProgress->WaitForCompletion(1000);
3541 alock.acquire();
3542 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3543 }
3544
3545 // try again now
3546 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3547 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3548 )
3549 {
3550 // OK, share the session... we are now dealing with three processes:
3551 // 1) VBoxSVC (where this code runs);
3552 // 2) process C: the caller's client process (who wants a shared session);
3553 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3554
3555 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3556 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3557 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3558 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3559 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3560
3561 /*
3562 * Release the lock before calling the client process. It's safe here
3563 * since the only thing to do after we get the lock again is to add
3564 * the remote control to the list (which doesn't directly influence
3565 * anything).
3566 */
3567 alock.release();
3568
3569 // get the console of the session holding the write lock (this is a remote call)
3570 ComPtr<IConsole> pConsoleW;
3571 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3572 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3573 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3574 if (FAILED(rc))
3575 // the failure may occur w/o any error info (from RPC), so provide one
3576 return setError(VBOX_E_VM_ERROR,
3577 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3578
3579 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3580
3581 // share the session machine and W's console with the caller's session
3582 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3583 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3584 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3585
3586 if (FAILED(rc))
3587 // the failure may occur w/o any error info (from RPC), so provide one
3588 return setError(VBOX_E_VM_ERROR,
3589 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3590 alock.acquire();
3591
3592 // need to revalidate the state after acquiring the lock again
3593 if (mData->mSession.mState != SessionState_Locked)
3594 {
3595 pSessionControl->Uninitialize();
3596 return setError(VBOX_E_INVALID_SESSION_STATE,
3597 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3598 mUserData->s.strName.c_str());
3599 }
3600
3601 // add the caller's session to the list
3602 mData->mSession.mRemoteControls.push_back(pSessionControl);
3603 }
3604 else if ( mData->mSession.mState == SessionState_Locked
3605 || mData->mSession.mState == SessionState_Unlocking
3606 )
3607 {
3608 // sharing not permitted, or machine still unlocking:
3609 return setError(VBOX_E_INVALID_OBJECT_STATE,
3610 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3611 mUserData->s.strName.c_str());
3612 }
3613 else
3614 {
3615 // machine is not locked: then write-lock the machine (create the session machine)
3616
3617 // must not be busy
3618 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3619
3620 // get the caller's session PID
3621 RTPROCESS pid = NIL_RTPROCESS;
3622 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3623 pSessionControl->GetPID((ULONG*)&pid);
3624 Assert(pid != NIL_RTPROCESS);
3625
3626 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3627
3628 if (fLaunchingVMProcess)
3629 {
3630 if (mData->mSession.mPID == NIL_RTPROCESS)
3631 {
3632 // two or more clients racing for a lock, the one which set the
3633 // session state to Spawning will win, the others will get an
3634 // error as we can't decide here if waiting a little would help
3635 // (only for shared locks this would avoid an error)
3636 return setError(VBOX_E_INVALID_OBJECT_STATE,
3637 tr("The machine '%s' already has a lock request pending"),
3638 mUserData->s.strName.c_str());
3639 }
3640
3641 // this machine is awaiting for a spawning session to be opened:
3642 // then the calling process must be the one that got started by
3643 // LaunchVMProcess()
3644
3645 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3646 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3647
3648 if (mData->mSession.mPID != pid)
3649 return setError(E_ACCESSDENIED,
3650 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3651 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3652 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3653 }
3654
3655 // create the mutable SessionMachine from the current machine
3656 ComObjPtr<SessionMachine> sessionMachine;
3657 sessionMachine.createObject();
3658 rc = sessionMachine->init(this);
3659 AssertComRC(rc);
3660
3661 /* NOTE: doing return from this function after this point but
3662 * before the end is forbidden since it may call SessionMachine::uninit()
3663 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3664 * lock while still holding the Machine lock in alock so that a deadlock
3665 * is possible due to the wrong lock order. */
3666
3667 if (SUCCEEDED(rc))
3668 {
3669 /*
3670 * Set the session state to Spawning to protect against subsequent
3671 * attempts to open a session and to unregister the machine after
3672 * we release the lock.
3673 */
3674 SessionState_T origState = mData->mSession.mState;
3675 mData->mSession.mState = SessionState_Spawning;
3676
3677#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3678 /* Get the client token ID to be passed to the client process */
3679 Utf8Str strTokenId;
3680 sessionMachine->getTokenId(strTokenId);
3681 Assert(!strTokenId.isEmpty());
3682#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3683 /* Get the client token to be passed to the client process */
3684 ComPtr<IToken> pToken(sessionMachine->getToken());
3685 /* The token is now "owned" by pToken, fix refcount */
3686 if (!pToken.isNull())
3687 pToken->Release();
3688#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3689
3690 /*
3691 * Release the lock before calling the client process -- it will call
3692 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3693 * because the state is Spawning, so that LaunchVMProcess() and
3694 * LockMachine() calls will fail. This method, called before we
3695 * acquire the lock again, will fail because of the wrong PID.
3696 *
3697 * Note that mData->mSession.mRemoteControls accessed outside
3698 * the lock may not be modified when state is Spawning, so it's safe.
3699 */
3700 alock.release();
3701
3702 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3703#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3704 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3705#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3706 rc = pSessionControl->AssignMachine(sessionMachine, lockType, pToken);
3707 /* Now the token is owned by the client process. */
3708 pToken.setNull();
3709#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3710 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3711
3712 /* The failure may occur w/o any error info (from RPC), so provide one */
3713 if (FAILED(rc))
3714 setError(VBOX_E_VM_ERROR,
3715 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3716
3717 if ( SUCCEEDED(rc)
3718 && fLaunchingVMProcess
3719 )
3720 {
3721 /* complete the remote session initialization */
3722
3723 /* get the console from the direct session */
3724 ComPtr<IConsole> console;
3725 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3726 ComAssertComRC(rc);
3727
3728 if (SUCCEEDED(rc) && !console)
3729 {
3730 ComAssert(!!console);
3731 rc = E_FAIL;
3732 }
3733
3734 /* assign machine & console to the remote session */
3735 if (SUCCEEDED(rc))
3736 {
3737 /*
3738 * after LaunchVMProcess(), the first and the only
3739 * entry in remoteControls is that remote session
3740 */
3741 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3742 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3743 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3744
3745 /* The failure may occur w/o any error info (from RPC), so provide one */
3746 if (FAILED(rc))
3747 setError(VBOX_E_VM_ERROR,
3748 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3749 }
3750
3751 if (FAILED(rc))
3752 pSessionControl->Uninitialize();
3753 }
3754
3755 /* acquire the lock again */
3756 alock.acquire();
3757
3758 /* Restore the session state */
3759 mData->mSession.mState = origState;
3760 }
3761
3762 // finalize spawning anyway (this is why we don't return on errors above)
3763 if (fLaunchingVMProcess)
3764 {
3765 /* Note that the progress object is finalized later */
3766 /** @todo Consider checking mData->mSession.mProgress for cancellation
3767 * around here. */
3768
3769 /* We don't reset mSession.mPID here because it is necessary for
3770 * SessionMachine::uninit() to reap the child process later. */
3771
3772 if (FAILED(rc))
3773 {
3774 /* Close the remote session, remove the remote control from the list
3775 * and reset session state to Closed (@note keep the code in sync
3776 * with the relevant part in checkForSpawnFailure()). */
3777
3778 Assert(mData->mSession.mRemoteControls.size() == 1);
3779 if (mData->mSession.mRemoteControls.size() == 1)
3780 {
3781 ErrorInfoKeeper eik;
3782 mData->mSession.mRemoteControls.front()->Uninitialize();
3783 }
3784
3785 mData->mSession.mRemoteControls.clear();
3786 mData->mSession.mState = SessionState_Unlocked;
3787 }
3788 }
3789 else
3790 {
3791 /* memorize PID of the directly opened session */
3792 if (SUCCEEDED(rc))
3793 mData->mSession.mPID = pid;
3794 }
3795
3796 if (SUCCEEDED(rc))
3797 {
3798 /* memorize the direct session control and cache IUnknown for it */
3799 mData->mSession.mDirectControl = pSessionControl;
3800 mData->mSession.mState = SessionState_Locked;
3801 /* associate the SessionMachine with this Machine */
3802 mData->mSession.mMachine = sessionMachine;
3803
3804 /* request an IUnknown pointer early from the remote party for later
3805 * identity checks (it will be internally cached within mDirectControl
3806 * at least on XPCOM) */
3807 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3808 NOREF(unk);
3809 }
3810
3811 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3812 * would break the lock order */
3813 alock.release();
3814
3815 /* uninitialize the created session machine on failure */
3816 if (FAILED(rc))
3817 sessionMachine->uninit();
3818
3819 }
3820
3821 if (SUCCEEDED(rc))
3822 {
3823 /*
3824 * tell the client watcher thread to update the set of
3825 * machines that have open sessions
3826 */
3827 mParent->updateClientWatcher();
3828
3829 if (oldState != SessionState_Locked)
3830 /* fire an event */
3831 mParent->onSessionStateChange(getId(), SessionState_Locked);
3832 }
3833
3834 return rc;
3835}
3836
3837/**
3838 * @note Locks objects!
3839 */
3840STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3841 IN_BSTR aFrontend,
3842 IN_BSTR aEnvironment,
3843 IProgress **aProgress)
3844{
3845 CheckComArgStr(aFrontend);
3846 Utf8Str strFrontend(aFrontend);
3847 Utf8Str strEnvironment(aEnvironment);
3848 /* "emergencystop" doesn't need the session, so skip the checks/interface
3849 * retrieval. This code doesn't quite fit in here, but introducing a
3850 * special API method would be even more effort, and would require explicit
3851 * support by every API client. It's better to hide the feature a bit. */
3852 if (strFrontend != "emergencystop")
3853 CheckComArgNotNull(aSession);
3854 CheckComArgOutPointerValid(aProgress);
3855
3856 AutoCaller autoCaller(this);
3857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3858
3859 HRESULT rc = S_OK;
3860 if (strFrontend.isEmpty())
3861 {
3862 Bstr bstrFrontend;
3863 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3864 if (FAILED(rc))
3865 return rc;
3866 strFrontend = bstrFrontend;
3867 if (strFrontend.isEmpty())
3868 {
3869 ComPtr<ISystemProperties> systemProperties;
3870 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3871 if (FAILED(rc))
3872 return rc;
3873 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3874 if (FAILED(rc))
3875 return rc;
3876 strFrontend = bstrFrontend;
3877 }
3878 /* paranoia - emergencystop is not a valid default */
3879 if (strFrontend == "emergencystop")
3880 strFrontend = Utf8Str::Empty;
3881 }
3882 /* default frontend: Qt GUI */
3883 if (strFrontend.isEmpty())
3884 strFrontend = "GUI/Qt";
3885
3886 if (strFrontend != "emergencystop")
3887 {
3888 /* check the session state */
3889 SessionState_T state;
3890 rc = aSession->COMGETTER(State)(&state);
3891 if (FAILED(rc))
3892 return rc;
3893
3894 if (state != SessionState_Unlocked)
3895 return setError(VBOX_E_INVALID_OBJECT_STATE,
3896 tr("The given session is busy"));
3897
3898 /* get the IInternalSessionControl interface */
3899 ComPtr<IInternalSessionControl> control(aSession);
3900 ComAssertMsgRet(!control.isNull(),
3901 ("No IInternalSessionControl interface"),
3902 E_INVALIDARG);
3903
3904 /* get the teleporter enable state for the progress object init. */
3905 BOOL fTeleporterEnabled;
3906 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3907 if (FAILED(rc))
3908 return rc;
3909
3910 /* create a progress object */
3911 ComObjPtr<ProgressProxy> progress;
3912 progress.createObject();
3913 rc = progress->init(mParent,
3914 static_cast<IMachine*>(this),
3915 Bstr(tr("Starting VM")).raw(),
3916 TRUE /* aCancelable */,
3917 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3918 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3919 2 /* uFirstOperationWeight */,
3920 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3921
3922 if (SUCCEEDED(rc))
3923 {
3924 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3925 if (SUCCEEDED(rc))
3926 {
3927 progress.queryInterfaceTo(aProgress);
3928
3929 /* signal the client watcher thread */
3930 mParent->updateClientWatcher();
3931
3932 /* fire an event */
3933 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3934 }
3935 }
3936 }
3937 else
3938 {
3939 /* no progress object - either instant success or failure */
3940 *aProgress = NULL;
3941
3942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3943
3944 if (mData->mSession.mState != SessionState_Locked)
3945 return setError(VBOX_E_INVALID_OBJECT_STATE,
3946 tr("The machine '%s' is not locked by a session"),
3947 mUserData->s.strName.c_str());
3948
3949 /* must have a VM process associated - do not kill normal API clients
3950 * with an open session */
3951 if (!Global::IsOnline(mData->mMachineState))
3952 return setError(VBOX_E_INVALID_OBJECT_STATE,
3953 tr("The machine '%s' does not have a VM process"),
3954 mUserData->s.strName.c_str());
3955
3956 /* forcibly terminate the VM process */
3957 if (mData->mSession.mPID != NIL_RTPROCESS)
3958 RTProcTerminate(mData->mSession.mPID);
3959
3960 /* signal the client watcher thread, as most likely the client has
3961 * been terminated */
3962 mParent->updateClientWatcher();
3963 }
3964
3965 return rc;
3966}
3967
3968STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3969{
3970 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3971 return setError(E_INVALIDARG,
3972 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3973 aPosition, SchemaDefs::MaxBootPosition);
3974
3975 if (aDevice == DeviceType_USB)
3976 return setError(E_NOTIMPL,
3977 tr("Booting from USB device is currently not supported"));
3978
3979 AutoCaller autoCaller(this);
3980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3981
3982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3983
3984 HRESULT rc = checkStateDependency(MutableStateDep);
3985 if (FAILED(rc)) return rc;
3986
3987 setModified(IsModified_MachineData);
3988 mHWData.backup();
3989 mHWData->mBootOrder[aPosition - 1] = aDevice;
3990
3991 return S_OK;
3992}
3993
3994STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3995{
3996 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3997 return setError(E_INVALIDARG,
3998 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3999 aPosition, SchemaDefs::MaxBootPosition);
4000
4001 AutoCaller autoCaller(this);
4002 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4003
4004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4005
4006 *aDevice = mHWData->mBootOrder[aPosition - 1];
4007
4008 return S_OK;
4009}
4010
4011STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4012 LONG aControllerPort,
4013 LONG aDevice,
4014 DeviceType_T aType,
4015 IMedium *aMedium)
4016{
4017 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4018 aControllerName, aControllerPort, aDevice, aType, aMedium));
4019
4020 CheckComArgStrNotEmptyOrNull(aControllerName);
4021
4022 AutoCaller autoCaller(this);
4023 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4024
4025 // request the host lock first, since might be calling Host methods for getting host drives;
4026 // next, protect the media tree all the while we're in here, as well as our member variables
4027 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4028 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4029
4030 HRESULT rc = checkStateDependency(MutableStateDep);
4031 if (FAILED(rc)) return rc;
4032
4033 /// @todo NEWMEDIA implicit machine registration
4034 if (!mData->mRegistered)
4035 return setError(VBOX_E_INVALID_OBJECT_STATE,
4036 tr("Cannot attach storage devices to an unregistered machine"));
4037
4038 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4039
4040 /* Check for an existing controller. */
4041 ComObjPtr<StorageController> ctl;
4042 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4043 if (FAILED(rc)) return rc;
4044
4045 StorageControllerType_T ctrlType;
4046 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4047 if (FAILED(rc))
4048 return setError(E_FAIL,
4049 tr("Could not get type of controller '%ls'"),
4050 aControllerName);
4051
4052 bool fSilent = false;
4053 Utf8Str strReconfig;
4054
4055 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4056 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4057 if ( mData->mMachineState == MachineState_Paused
4058 && strReconfig == "1")
4059 fSilent = true;
4060
4061 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4062 bool fHotplug = false;
4063 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4064 fHotplug = true;
4065
4066 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4067 return setError(VBOX_E_INVALID_VM_STATE,
4068 tr("Controller '%ls' does not support hotplugging"),
4069 aControllerName);
4070
4071 // check that the port and device are not out of range
4072 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
4073 if (FAILED(rc)) return rc;
4074
4075 /* check if the device slot is already busy */
4076 MediumAttachment *pAttachTemp;
4077 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4078 aControllerName,
4079 aControllerPort,
4080 aDevice)))
4081 {
4082 Medium *pMedium = pAttachTemp->i_getMedium();
4083 if (pMedium)
4084 {
4085 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4086 return setError(VBOX_E_OBJECT_IN_USE,
4087 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4088 pMedium->i_getLocationFull().c_str(),
4089 aControllerPort,
4090 aDevice,
4091 aControllerName);
4092 }
4093 else
4094 return setError(VBOX_E_OBJECT_IN_USE,
4095 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4096 aControllerPort, aDevice, aControllerName);
4097 }
4098
4099 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4100 if (aMedium && medium.isNull())
4101 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4102
4103 AutoCaller mediumCaller(medium);
4104 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4105
4106 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4107
4108 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4109 && !medium.isNull()
4110 )
4111 return setError(VBOX_E_OBJECT_IN_USE,
4112 tr("Medium '%s' is already attached to this virtual machine"),
4113 medium->i_getLocationFull().c_str());
4114
4115 if (!medium.isNull())
4116 {
4117 MediumType_T mtype = medium->i_getType();
4118 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4119 // For DVDs it's not written to the config file, so needs no global config
4120 // version bump. For floppies it's a new attribute "type", which is ignored
4121 // by older VirtualBox version, so needs no global config version bump either.
4122 // For hard disks this type is not accepted.
4123 if (mtype == MediumType_MultiAttach)
4124 {
4125 // This type is new with VirtualBox 4.0 and therefore requires settings
4126 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4127 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4128 // two reasons: The medium type is a property of the media registry tree, which
4129 // can reside in the global config file (for pre-4.0 media); we would therefore
4130 // possibly need to bump the global config version. We don't want to do that though
4131 // because that might make downgrading to pre-4.0 impossible.
4132 // As a result, we can only use these two new types if the medium is NOT in the
4133 // global registry:
4134 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4135 if ( medium->i_isInRegistry(uuidGlobalRegistry)
4136 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4137 )
4138 return setError(VBOX_E_INVALID_OBJECT_STATE,
4139 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4140 "to machines that were created with VirtualBox 4.0 or later"),
4141 medium->i_getLocationFull().c_str());
4142 }
4143 }
4144
4145 bool fIndirect = false;
4146 if (!medium.isNull())
4147 fIndirect = medium->i_isReadOnly();
4148 bool associate = true;
4149
4150 do
4151 {
4152 if ( aType == DeviceType_HardDisk
4153 && mMediaData.isBackedUp())
4154 {
4155 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4156
4157 /* check if the medium was attached to the VM before we started
4158 * changing attachments in which case the attachment just needs to
4159 * be restored */
4160 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4161 {
4162 AssertReturn(!fIndirect, E_FAIL);
4163
4164 /* see if it's the same bus/channel/device */
4165 if (pAttachTemp->i_matches(aControllerName, aControllerPort, aDevice))
4166 {
4167 /* the simplest case: restore the whole attachment
4168 * and return, nothing else to do */
4169 mMediaData->mAttachments.push_back(pAttachTemp);
4170
4171 /* Reattach the medium to the VM. */
4172 if (fHotplug || fSilent)
4173 {
4174 mediumLock.release();
4175 treeLock.release();
4176 alock.release();
4177
4178 MediumLockList *pMediumLockList(new MediumLockList());
4179
4180 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4181 true /* fMediumLockWrite */,
4182 NULL,
4183 *pMediumLockList);
4184 alock.acquire();
4185 if (FAILED(rc))
4186 delete pMediumLockList;
4187 else
4188 {
4189 mData->mSession.mLockedMedia.Unlock();
4190 alock.release();
4191 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4192 mData->mSession.mLockedMedia.Lock();
4193 alock.acquire();
4194 }
4195 alock.release();
4196
4197 if (SUCCEEDED(rc))
4198 {
4199 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4200 /* Remove lock list in case of error. */
4201 if (FAILED(rc))
4202 {
4203 mData->mSession.mLockedMedia.Unlock();
4204 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4205 mData->mSession.mLockedMedia.Lock();
4206 }
4207 }
4208 }
4209
4210 return S_OK;
4211 }
4212
4213 /* bus/channel/device differ; we need a new attachment object,
4214 * but don't try to associate it again */
4215 associate = false;
4216 break;
4217 }
4218 }
4219
4220 /* go further only if the attachment is to be indirect */
4221 if (!fIndirect)
4222 break;
4223
4224 /* perform the so called smart attachment logic for indirect
4225 * attachments. Note that smart attachment is only applicable to base
4226 * hard disks. */
4227
4228 if (medium->i_getParent().isNull())
4229 {
4230 /* first, investigate the backup copy of the current hard disk
4231 * attachments to make it possible to re-attach existing diffs to
4232 * another device slot w/o losing their contents */
4233 if (mMediaData.isBackedUp())
4234 {
4235 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4236
4237 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4238 uint32_t foundLevel = 0;
4239
4240 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4241 it != oldAtts.end();
4242 ++it)
4243 {
4244 uint32_t level = 0;
4245 MediumAttachment *pAttach = *it;
4246 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4247 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4248 if (pMedium.isNull())
4249 continue;
4250
4251 if (pMedium->i_getBase(&level) == medium)
4252 {
4253 /* skip the hard disk if its currently attached (we
4254 * cannot attach the same hard disk twice) */
4255 if (findAttachment(mMediaData->mAttachments,
4256 pMedium))
4257 continue;
4258
4259 /* matched device, channel and bus (i.e. attached to the
4260 * same place) will win and immediately stop the search;
4261 * otherwise the attachment that has the youngest
4262 * descendant of medium will be used
4263 */
4264 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
4265 {
4266 /* the simplest case: restore the whole attachment
4267 * and return, nothing else to do */
4268 mMediaData->mAttachments.push_back(*it);
4269
4270 /* Reattach the medium to the VM. */
4271 if (fHotplug || fSilent)
4272 {
4273 mediumLock.release();
4274 treeLock.release();
4275 alock.release();
4276
4277 MediumLockList *pMediumLockList(new MediumLockList());
4278
4279 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4280 true /* fMediumLockWrite */,
4281 NULL,
4282 *pMediumLockList);
4283 alock.acquire();
4284 if (FAILED(rc))
4285 delete pMediumLockList;
4286 else
4287 {
4288 mData->mSession.mLockedMedia.Unlock();
4289 alock.release();
4290 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4291 mData->mSession.mLockedMedia.Lock();
4292 alock.acquire();
4293 }
4294 alock.release();
4295
4296 if (SUCCEEDED(rc))
4297 {
4298 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4299 /* Remove lock list in case of error. */
4300 if (FAILED(rc))
4301 {
4302 mData->mSession.mLockedMedia.Unlock();
4303 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4304 mData->mSession.mLockedMedia.Lock();
4305 }
4306 }
4307 }
4308
4309 return S_OK;
4310 }
4311 else if ( foundIt == oldAtts.end()
4312 || level > foundLevel /* prefer younger */
4313 )
4314 {
4315 foundIt = it;
4316 foundLevel = level;
4317 }
4318 }
4319 }
4320
4321 if (foundIt != oldAtts.end())
4322 {
4323 /* use the previously attached hard disk */
4324 medium = (*foundIt)->i_getMedium();
4325 mediumCaller.attach(medium);
4326 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4327 mediumLock.attach(medium);
4328 /* not implicit, doesn't require association with this VM */
4329 fIndirect = false;
4330 associate = false;
4331 /* go right to the MediumAttachment creation */
4332 break;
4333 }
4334 }
4335
4336 /* must give up the medium lock and medium tree lock as below we
4337 * go over snapshots, which needs a lock with higher lock order. */
4338 mediumLock.release();
4339 treeLock.release();
4340
4341 /* then, search through snapshots for the best diff in the given
4342 * hard disk's chain to base the new diff on */
4343
4344 ComObjPtr<Medium> base;
4345 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4346 while (snap)
4347 {
4348 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4349
4350 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4351
4352 MediumAttachment *pAttachFound = NULL;
4353 uint32_t foundLevel = 0;
4354
4355 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4356 it != snapAtts.end();
4357 ++it)
4358 {
4359 MediumAttachment *pAttach = *it;
4360 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4361 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4362 if (pMedium.isNull())
4363 continue;
4364
4365 uint32_t level = 0;
4366 if (pMedium->i_getBase(&level) == medium)
4367 {
4368 /* matched device, channel and bus (i.e. attached to the
4369 * same place) will win and immediately stop the search;
4370 * otherwise the attachment that has the youngest
4371 * descendant of medium will be used
4372 */
4373 if ( pAttach->i_getDevice() == aDevice
4374 && pAttach->i_getPort() == aControllerPort
4375 && pAttach->i_getControllerName() == aControllerName
4376 )
4377 {
4378 pAttachFound = pAttach;
4379 break;
4380 }
4381 else if ( !pAttachFound
4382 || level > foundLevel /* prefer younger */
4383 )
4384 {
4385 pAttachFound = pAttach;
4386 foundLevel = level;
4387 }
4388 }
4389 }
4390
4391 if (pAttachFound)
4392 {
4393 base = pAttachFound->i_getMedium();
4394 break;
4395 }
4396
4397 snap = snap->getParent();
4398 }
4399
4400 /* re-lock medium tree and the medium, as we need it below */
4401 treeLock.acquire();
4402 mediumLock.acquire();
4403
4404 /* found a suitable diff, use it as a base */
4405 if (!base.isNull())
4406 {
4407 medium = base;
4408 mediumCaller.attach(medium);
4409 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4410 mediumLock.attach(medium);
4411 }
4412 }
4413
4414 Utf8Str strFullSnapshotFolder;
4415 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4416
4417 ComObjPtr<Medium> diff;
4418 diff.createObject();
4419 // store this diff in the same registry as the parent
4420 Guid uuidRegistryParent;
4421 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4422 {
4423 // parent image has no registry: this can happen if we're attaching a new immutable
4424 // image that has not yet been attached (medium then points to the base and we're
4425 // creating the diff image for the immutable, and the parent is not yet registered);
4426 // put the parent in the machine registry then
4427 mediumLock.release();
4428 treeLock.release();
4429 alock.release();
4430 addMediumToRegistry(medium);
4431 alock.acquire();
4432 treeLock.acquire();
4433 mediumLock.acquire();
4434 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4435 }
4436 rc = diff->init(mParent,
4437 medium->i_getPreferredDiffFormat(),
4438 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4439 uuidRegistryParent);
4440 if (FAILED(rc)) return rc;
4441
4442 /* Apply the normal locking logic to the entire chain. */
4443 MediumLockList *pMediumLockList(new MediumLockList());
4444 mediumLock.release();
4445 treeLock.release();
4446 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4447 true /* fMediumLockWrite */,
4448 medium,
4449 *pMediumLockList);
4450 treeLock.acquire();
4451 mediumLock.acquire();
4452 if (SUCCEEDED(rc))
4453 {
4454 mediumLock.release();
4455 treeLock.release();
4456 rc = pMediumLockList->Lock();
4457 treeLock.acquire();
4458 mediumLock.acquire();
4459 if (FAILED(rc))
4460 setError(rc,
4461 tr("Could not lock medium when creating diff '%s'"),
4462 diff->i_getLocationFull().c_str());
4463 else
4464 {
4465 /* will release the lock before the potentially lengthy
4466 * operation, so protect with the special state */
4467 MachineState_T oldState = mData->mMachineState;
4468 setMachineState(MachineState_SettingUp);
4469
4470 mediumLock.release();
4471 treeLock.release();
4472 alock.release();
4473
4474 rc = medium->i_createDiffStorage(diff,
4475 MediumVariant_Standard,
4476 pMediumLockList,
4477 NULL /* aProgress */,
4478 true /* aWait */);
4479
4480 alock.acquire();
4481 treeLock.acquire();
4482 mediumLock.acquire();
4483
4484 setMachineState(oldState);
4485 }
4486 }
4487
4488 /* Unlock the media and free the associated memory. */
4489 delete pMediumLockList;
4490
4491 if (FAILED(rc)) return rc;
4492
4493 /* use the created diff for the actual attachment */
4494 medium = diff;
4495 mediumCaller.attach(medium);
4496 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4497 mediumLock.attach(medium);
4498 }
4499 while (0);
4500
4501 ComObjPtr<MediumAttachment> attachment;
4502 attachment.createObject();
4503 rc = attachment->init(this,
4504 medium,
4505 aControllerName,
4506 aControllerPort,
4507 aDevice,
4508 aType,
4509 fIndirect,
4510 false /* fPassthrough */,
4511 false /* fTempEject */,
4512 false /* fNonRotational */,
4513 false /* fDiscard */,
4514 fHotplug /* fHotPluggable */,
4515 Utf8Str::Empty);
4516 if (FAILED(rc)) return rc;
4517
4518 if (associate && !medium.isNull())
4519 {
4520 // as the last step, associate the medium to the VM
4521 rc = medium->i_addBackReference(mData->mUuid);
4522 // here we can fail because of Deleting, or being in process of creating a Diff
4523 if (FAILED(rc)) return rc;
4524
4525 mediumLock.release();
4526 treeLock.release();
4527 alock.release();
4528 addMediumToRegistry(medium);
4529 alock.acquire();
4530 treeLock.acquire();
4531 mediumLock.acquire();
4532 }
4533
4534 /* success: finally remember the attachment */
4535 setModified(IsModified_Storage);
4536 mMediaData.backup();
4537 mMediaData->mAttachments.push_back(attachment);
4538
4539 mediumLock.release();
4540 treeLock.release();
4541 alock.release();
4542
4543 if (fHotplug || fSilent)
4544 {
4545 if (!medium.isNull())
4546 {
4547 MediumLockList *pMediumLockList(new MediumLockList());
4548
4549 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4550 true /* fMediumLockWrite */,
4551 NULL,
4552 *pMediumLockList);
4553 alock.acquire();
4554 if (FAILED(rc))
4555 delete pMediumLockList;
4556 else
4557 {
4558 mData->mSession.mLockedMedia.Unlock();
4559 alock.release();
4560 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4561 mData->mSession.mLockedMedia.Lock();
4562 alock.acquire();
4563 }
4564 alock.release();
4565 }
4566
4567 if (SUCCEEDED(rc))
4568 {
4569 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4570 /* Remove lock list in case of error. */
4571 if (FAILED(rc))
4572 {
4573 mData->mSession.mLockedMedia.Unlock();
4574 mData->mSession.mLockedMedia.Remove(attachment);
4575 mData->mSession.mLockedMedia.Lock();
4576 }
4577 }
4578 }
4579
4580 mParent->saveModifiedRegistries();
4581
4582 return rc;
4583}
4584
4585STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4586 LONG aDevice)
4587{
4588 CheckComArgStrNotEmptyOrNull(aControllerName);
4589
4590 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4591 aControllerName, aControllerPort, aDevice));
4592
4593 AutoCaller autoCaller(this);
4594 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4595
4596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4597
4598 HRESULT rc = checkStateDependency(MutableStateDep);
4599 if (FAILED(rc)) return rc;
4600
4601 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4602
4603 /* Check for an existing controller. */
4604 ComObjPtr<StorageController> ctl;
4605 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4606 if (FAILED(rc)) return rc;
4607
4608 StorageControllerType_T ctrlType;
4609 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4610 if (FAILED(rc))
4611 return setError(E_FAIL,
4612 tr("Could not get type of controller '%ls'"),
4613 aControllerName);
4614
4615 bool fSilent = false;
4616 Utf8Str strReconfig;
4617
4618 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4619 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4620 if ( mData->mMachineState == MachineState_Paused
4621 && strReconfig == "1")
4622 fSilent = true;
4623
4624 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4625 bool fHotplug = false;
4626 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4627 fHotplug = true;
4628
4629 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4630 return setError(VBOX_E_INVALID_VM_STATE,
4631 tr("Controller '%ls' does not support hotplugging"),
4632 aControllerName);
4633
4634 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4635 aControllerName,
4636 aControllerPort,
4637 aDevice);
4638 if (!pAttach)
4639 return setError(VBOX_E_OBJECT_NOT_FOUND,
4640 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4641 aDevice, aControllerPort, aControllerName);
4642
4643 if (fHotplug && !pAttach->i_getHotPluggable())
4644 return setError(VBOX_E_NOT_SUPPORTED,
4645 tr("The device slot %d on port %d of controller '%ls' does not support hotplugging"),
4646 aDevice, aControllerPort, aControllerName);
4647
4648 /*
4649 * The VM has to detach the device before we delete any implicit diffs.
4650 * If this fails we can roll back without loosing data.
4651 */
4652 if (fHotplug || fSilent)
4653 {
4654 alock.release();
4655 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4656 alock.acquire();
4657 }
4658 if (FAILED(rc)) return rc;
4659
4660 /* If we are here everything went well and we can delete the implicit now. */
4661 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4662
4663 alock.release();
4664
4665 mParent->saveModifiedRegistries();
4666
4667 return rc;
4668}
4669
4670STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4671 LONG aDevice, BOOL aPassthrough)
4672{
4673 CheckComArgStrNotEmptyOrNull(aControllerName);
4674
4675 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4676 aControllerName, aControllerPort, aDevice, aPassthrough));
4677
4678 AutoCaller autoCaller(this);
4679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4680
4681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4682
4683 HRESULT rc = checkStateDependency(MutableStateDep);
4684 if (FAILED(rc)) return rc;
4685
4686 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4687
4688 if (Global::IsOnlineOrTransient(mData->mMachineState))
4689 return setError(VBOX_E_INVALID_VM_STATE,
4690 tr("Invalid machine state: %s"),
4691 Global::stringifyMachineState(mData->mMachineState));
4692
4693 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4694 aControllerName,
4695 aControllerPort,
4696 aDevice);
4697 if (!pAttach)
4698 return setError(VBOX_E_OBJECT_NOT_FOUND,
4699 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4700 aDevice, aControllerPort, aControllerName);
4701
4702
4703 setModified(IsModified_Storage);
4704 mMediaData.backup();
4705
4706 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4707
4708 if (pAttach->i_getType() != DeviceType_DVD)
4709 return setError(E_INVALIDARG,
4710 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4711 aDevice, aControllerPort, aControllerName);
4712 pAttach->i_updatePassthrough(!!aPassthrough);
4713
4714 return S_OK;
4715}
4716
4717STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4718 LONG aDevice, BOOL aTemporaryEject)
4719{
4720 CheckComArgStrNotEmptyOrNull(aControllerName);
4721
4722 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4723 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4724
4725 AutoCaller autoCaller(this);
4726 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4727
4728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4729
4730 HRESULT rc = checkStateDependency(MutableStateDep);
4731 if (FAILED(rc)) return rc;
4732
4733 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4734 aControllerName,
4735 aControllerPort,
4736 aDevice);
4737 if (!pAttach)
4738 return setError(VBOX_E_OBJECT_NOT_FOUND,
4739 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4740 aDevice, aControllerPort, aControllerName);
4741
4742
4743 setModified(IsModified_Storage);
4744 mMediaData.backup();
4745
4746 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4747
4748 if (pAttach->i_getType() != DeviceType_DVD)
4749 return setError(E_INVALIDARG,
4750 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4751 aDevice, aControllerPort, aControllerName);
4752 pAttach->i_updateTempEject(!!aTemporaryEject);
4753
4754 return S_OK;
4755}
4756
4757STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4758 LONG aDevice, BOOL aNonRotational)
4759{
4760 CheckComArgStrNotEmptyOrNull(aControllerName);
4761
4762 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4763 aControllerName, aControllerPort, aDevice, aNonRotational));
4764
4765 AutoCaller autoCaller(this);
4766 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4767
4768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4769
4770 HRESULT rc = checkStateDependency(MutableStateDep);
4771 if (FAILED(rc)) return rc;
4772
4773 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4774
4775 if (Global::IsOnlineOrTransient(mData->mMachineState))
4776 return setError(VBOX_E_INVALID_VM_STATE,
4777 tr("Invalid machine state: %s"),
4778 Global::stringifyMachineState(mData->mMachineState));
4779
4780 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4781 aControllerName,
4782 aControllerPort,
4783 aDevice);
4784 if (!pAttach)
4785 return setError(VBOX_E_OBJECT_NOT_FOUND,
4786 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4787 aDevice, aControllerPort, aControllerName);
4788
4789
4790 setModified(IsModified_Storage);
4791 mMediaData.backup();
4792
4793 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4794
4795 if (pAttach->i_getType() != DeviceType_HardDisk)
4796 return setError(E_INVALIDARG,
4797 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"),
4798 aDevice, aControllerPort, aControllerName);
4799 pAttach->i_updateNonRotational(!!aNonRotational);
4800
4801 return S_OK;
4802}
4803
4804STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4805 LONG aDevice, BOOL aDiscard)
4806{
4807 CheckComArgStrNotEmptyOrNull(aControllerName);
4808
4809 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4810 aControllerName, aControllerPort, aDevice, aDiscard));
4811
4812 AutoCaller autoCaller(this);
4813 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4814
4815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4816
4817 HRESULT rc = checkStateDependency(MutableStateDep);
4818 if (FAILED(rc)) return rc;
4819
4820 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4821
4822 if (Global::IsOnlineOrTransient(mData->mMachineState))
4823 return setError(VBOX_E_INVALID_VM_STATE,
4824 tr("Invalid machine state: %s"),
4825 Global::stringifyMachineState(mData->mMachineState));
4826
4827 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4828 aControllerName,
4829 aControllerPort,
4830 aDevice);
4831 if (!pAttach)
4832 return setError(VBOX_E_OBJECT_NOT_FOUND,
4833 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4834 aDevice, aControllerPort, aControllerName);
4835
4836
4837 setModified(IsModified_Storage);
4838 mMediaData.backup();
4839
4840 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4841
4842 if (pAttach->i_getType() != DeviceType_HardDisk)
4843 return setError(E_INVALIDARG,
4844 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"),
4845 aDevice, aControllerPort, aControllerName);
4846 pAttach->i_updateDiscard(!!aDiscard);
4847
4848 return S_OK;
4849}
4850
4851STDMETHODIMP Machine::SetHotPluggableForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4852 LONG aDevice, BOOL aHotPluggable)
4853{
4854 CheckComArgStrNotEmptyOrNull(aControllerName);
4855
4856 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4857 aControllerName, aControllerPort, aDevice, aHotPluggable));
4858
4859 AutoCaller autoCaller(this);
4860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4861
4862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4863
4864 HRESULT rc = checkStateDependency(MutableStateDep);
4865 if (FAILED(rc)) return rc;
4866
4867 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4868
4869 if (Global::IsOnlineOrTransient(mData->mMachineState))
4870 return setError(VBOX_E_INVALID_VM_STATE,
4871 tr("Invalid machine state: %s"),
4872 Global::stringifyMachineState(mData->mMachineState));
4873
4874 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4875 aControllerName,
4876 aControllerPort,
4877 aDevice);
4878 if (!pAttach)
4879 return setError(VBOX_E_OBJECT_NOT_FOUND,
4880 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4881 aDevice, aControllerPort, aControllerName);
4882
4883 /* Check for an existing controller. */
4884 ComObjPtr<StorageController> ctl;
4885 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4886 if (FAILED(rc)) return rc;
4887
4888 StorageControllerType_T ctrlType;
4889 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4890 if (FAILED(rc))
4891 return setError(E_FAIL,
4892 tr("Could not get type of controller '%ls'"),
4893 aControllerName);
4894
4895 if (!isControllerHotplugCapable(ctrlType))
4896 return setError(VBOX_E_NOT_SUPPORTED,
4897 tr("Controller '%ls' does not support changing the hot-pluggable device flag"),
4898 aControllerName);
4899
4900 setModified(IsModified_Storage);
4901 mMediaData.backup();
4902
4903 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4904
4905 if (pAttach->i_getType() == DeviceType_Floppy)
4906 return setError(E_INVALIDARG,
4907 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"),
4908 aDevice, aControllerPort, aControllerName);
4909 pAttach->i_updateHotPluggable(!!aHotPluggable);
4910
4911 return S_OK;
4912}
4913
4914STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4915 LONG aDevice)
4916{
4917 int rc = S_OK;
4918 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4919 aControllerName, aControllerPort, aDevice));
4920
4921 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4922
4923 return rc;
4924}
4925
4926STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4927 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4928{
4929 CheckComArgStrNotEmptyOrNull(aControllerName);
4930
4931 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4932 aControllerName, aControllerPort, aDevice));
4933
4934 AutoCaller autoCaller(this);
4935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4936
4937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4938
4939 HRESULT rc = checkStateDependency(MutableStateDep);
4940 if (FAILED(rc)) return rc;
4941
4942 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4943
4944 if (Global::IsOnlineOrTransient(mData->mMachineState))
4945 return setError(VBOX_E_INVALID_VM_STATE,
4946 tr("Invalid machine state: %s"),
4947 Global::stringifyMachineState(mData->mMachineState));
4948
4949 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4950 aControllerName,
4951 aControllerPort,
4952 aDevice);
4953 if (!pAttach)
4954 return setError(VBOX_E_OBJECT_NOT_FOUND,
4955 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4956 aDevice, aControllerPort, aControllerName);
4957
4958
4959 setModified(IsModified_Storage);
4960 mMediaData.backup();
4961
4962 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4963 if (aBandwidthGroup && group.isNull())
4964 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4965
4966 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4967
4968 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4969 if (strBandwidthGroupOld.isNotEmpty())
4970 {
4971 /* Get the bandwidth group object and release it - this must not fail. */
4972 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4973 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4974 Assert(SUCCEEDED(rc));
4975
4976 pBandwidthGroupOld->i_release();
4977 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4978 }
4979
4980 if (!group.isNull())
4981 {
4982 group->i_reference();
4983 pAttach->i_updateBandwidthGroup(group->i_getName());
4984 }
4985
4986 return S_OK;
4987}
4988
4989STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4990 LONG aControllerPort,
4991 LONG aDevice,
4992 DeviceType_T aType)
4993{
4994 HRESULT rc = S_OK;
4995
4996 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4997 aControllerName, aControllerPort, aDevice, aType));
4998
4999 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
5000
5001 return rc;
5002}
5003
5004
5005
5006STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
5007 LONG aControllerPort,
5008 LONG aDevice,
5009 BOOL aForce)
5010{
5011 int rc = S_OK;
5012 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
5013 aControllerName, aControllerPort, aForce));
5014
5015 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
5016
5017 return rc;
5018}
5019
5020STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
5021 LONG aControllerPort,
5022 LONG aDevice,
5023 IMedium *aMedium,
5024 BOOL aForce)
5025{
5026 int rc = S_OK;
5027 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
5028 aControllerName, aControllerPort, aDevice, aForce));
5029
5030 CheckComArgStrNotEmptyOrNull(aControllerName);
5031
5032 AutoCaller autoCaller(this);
5033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5034
5035 // request the host lock first, since might be calling Host methods for getting host drives;
5036 // next, protect the media tree all the while we're in here, as well as our member variables
5037 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
5038 this->lockHandle(),
5039 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5040
5041 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5042 aControllerName,
5043 aControllerPort,
5044 aDevice);
5045 if (pAttach.isNull())
5046 return setError(VBOX_E_OBJECT_NOT_FOUND,
5047 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
5048 aDevice, aControllerPort, aControllerName);
5049
5050 /* Remember previously mounted medium. The medium before taking the
5051 * backup is not necessarily the same thing. */
5052 ComObjPtr<Medium> oldmedium;
5053 oldmedium = pAttach->i_getMedium();
5054
5055 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
5056 if (aMedium && pMedium.isNull())
5057 return setError(E_INVALIDARG, "The given medium pointer is invalid");
5058
5059 AutoCaller mediumCaller(pMedium);
5060 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
5061
5062 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5063 if (pMedium)
5064 {
5065 DeviceType_T mediumType = pAttach->i_getType();
5066 switch (mediumType)
5067 {
5068 case DeviceType_DVD:
5069 case DeviceType_Floppy:
5070 break;
5071
5072 default:
5073 return setError(VBOX_E_INVALID_OBJECT_STATE,
5074 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5075 aControllerPort,
5076 aDevice,
5077 aControllerName);
5078 }
5079 }
5080
5081 setModified(IsModified_Storage);
5082 mMediaData.backup();
5083
5084 {
5085 // The backup operation makes the pAttach reference point to the
5086 // old settings. Re-get the correct reference.
5087 pAttach = findAttachment(mMediaData->mAttachments,
5088 aControllerName,
5089 aControllerPort,
5090 aDevice);
5091 if (!oldmedium.isNull())
5092 oldmedium->i_removeBackReference(mData->mUuid);
5093 if (!pMedium.isNull())
5094 {
5095 pMedium->i_addBackReference(mData->mUuid);
5096
5097 mediumLock.release();
5098 multiLock.release();
5099 addMediumToRegistry(pMedium);
5100 multiLock.acquire();
5101 mediumLock.acquire();
5102 }
5103
5104 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5105 pAttach->i_updateMedium(pMedium);
5106 }
5107
5108 setModified(IsModified_Storage);
5109
5110 mediumLock.release();
5111 multiLock.release();
5112 rc = onMediumChange(pAttach, aForce);
5113 multiLock.acquire();
5114 mediumLock.acquire();
5115
5116 /* On error roll back this change only. */
5117 if (FAILED(rc))
5118 {
5119 if (!pMedium.isNull())
5120 pMedium->i_removeBackReference(mData->mUuid);
5121 pAttach = findAttachment(mMediaData->mAttachments,
5122 aControllerName,
5123 aControllerPort,
5124 aDevice);
5125 /* If the attachment is gone in the meantime, bail out. */
5126 if (pAttach.isNull())
5127 return rc;
5128 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5129 if (!oldmedium.isNull())
5130 oldmedium->i_addBackReference(mData->mUuid);
5131 pAttach->i_updateMedium(oldmedium);
5132 }
5133
5134 mediumLock.release();
5135 multiLock.release();
5136
5137 mParent->saveModifiedRegistries();
5138
5139 return rc;
5140}
5141
5142STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5143 LONG aControllerPort,
5144 LONG aDevice,
5145 IMedium **aMedium)
5146{
5147 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5148 aControllerName, aControllerPort, aDevice));
5149
5150 CheckComArgStrNotEmptyOrNull(aControllerName);
5151 CheckComArgOutPointerValid(aMedium);
5152
5153 AutoCaller autoCaller(this);
5154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5155
5156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5157
5158 *aMedium = NULL;
5159
5160 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5161 aControllerName,
5162 aControllerPort,
5163 aDevice);
5164 if (pAttach.isNull())
5165 return setError(VBOX_E_OBJECT_NOT_FOUND,
5166 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5167 aDevice, aControllerPort, aControllerName);
5168
5169 pAttach->i_getMedium().queryInterfaceTo(aMedium);
5170
5171 return S_OK;
5172}
5173
5174STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5175{
5176 CheckComArgOutPointerValid(port);
5177 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5178
5179 AutoCaller autoCaller(this);
5180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5181
5182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5183
5184 mSerialPorts[slot].queryInterfaceTo(port);
5185
5186 return S_OK;
5187}
5188
5189STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5190{
5191 CheckComArgOutPointerValid(port);
5192 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5193
5194 AutoCaller autoCaller(this);
5195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5196
5197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5198
5199 mParallelPorts[slot].queryInterfaceTo(port);
5200
5201 return S_OK;
5202}
5203
5204STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5205{
5206 CheckComArgOutPointerValid(adapter);
5207 /* Do not assert if slot is out of range, just return the advertised
5208 status. testdriver/vbox.py triggers this in logVmInfo. */
5209 if (slot >= mNetworkAdapters.size())
5210 return setError(E_INVALIDARG,
5211 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5212 slot, mNetworkAdapters.size());
5213
5214 AutoCaller autoCaller(this);
5215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5216
5217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5218
5219 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5220
5221 return S_OK;
5222}
5223
5224STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5225{
5226 CheckComArgOutSafeArrayPointerValid(aKeys);
5227
5228 AutoCaller autoCaller(this);
5229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5230
5231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5232
5233 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5234 int i = 0;
5235 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5236 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5237 ++it, ++i)
5238 {
5239 const Utf8Str &strKey = it->first;
5240 strKey.cloneTo(&saKeys[i]);
5241 }
5242 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5243
5244 return S_OK;
5245 }
5246
5247 /**
5248 * @note Locks this object for reading.
5249 */
5250STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5251 BSTR *aValue)
5252{
5253 CheckComArgStrNotEmptyOrNull(aKey);
5254 CheckComArgOutPointerValid(aValue);
5255
5256 AutoCaller autoCaller(this);
5257 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5258
5259 /* start with nothing found */
5260 Bstr bstrResult("");
5261
5262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5263
5264 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5265 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5266 // found:
5267 bstrResult = it->second; // source is a Utf8Str
5268
5269 /* return the result to caller (may be empty) */
5270 bstrResult.cloneTo(aValue);
5271
5272 return S_OK;
5273}
5274
5275 /**
5276 * @note Locks mParent for writing + this object for writing.
5277 */
5278STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5279{
5280 CheckComArgStrNotEmptyOrNull(aKey);
5281
5282 AutoCaller autoCaller(this);
5283 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5284
5285 Utf8Str strKey(aKey);
5286 Utf8Str strValue(aValue);
5287 Utf8Str strOldValue; // empty
5288
5289 // locking note: we only hold the read lock briefly to look up the old value,
5290 // then release it and call the onExtraCanChange callbacks. There is a small
5291 // chance of a race insofar as the callback might be called twice if two callers
5292 // change the same key at the same time, but that's a much better solution
5293 // than the deadlock we had here before. The actual changing of the extradata
5294 // is then performed under the write lock and race-free.
5295
5296 // look up the old value first; if nothing has changed then we need not do anything
5297 {
5298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5299 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5300 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5301 strOldValue = it->second;
5302 }
5303
5304 bool fChanged;
5305 if ((fChanged = (strOldValue != strValue)))
5306 {
5307 // ask for permission from all listeners outside the locks;
5308 // onExtraDataCanChange() only briefly requests the VirtualBox
5309 // lock to copy the list of callbacks to invoke
5310 Bstr error;
5311 Bstr bstrValue(aValue);
5312
5313 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5314 {
5315 const char *sep = error.isEmpty() ? "" : ": ";
5316 CBSTR err = error.raw();
5317 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5318 sep, err));
5319 return setError(E_ACCESSDENIED,
5320 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5321 aKey,
5322 bstrValue.raw(),
5323 sep,
5324 err);
5325 }
5326
5327 // data is changing and change not vetoed: then write it out under the lock
5328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5329
5330 if (isSnapshotMachine())
5331 {
5332 HRESULT rc = checkStateDependency(MutableStateDep);
5333 if (FAILED(rc)) return rc;
5334 }
5335
5336 if (strValue.isEmpty())
5337 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5338 else
5339 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5340 // creates a new key if needed
5341
5342 bool fNeedsGlobalSaveSettings = false;
5343 saveSettings(&fNeedsGlobalSaveSettings);
5344
5345 if (fNeedsGlobalSaveSettings)
5346 {
5347 // save the global settings; for that we should hold only the VirtualBox lock
5348 alock.release();
5349 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5350 mParent->saveSettings();
5351 }
5352 }
5353
5354 // fire notification outside the lock
5355 if (fChanged)
5356 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5357
5358 return S_OK;
5359}
5360
5361STDMETHODIMP Machine::SetSettingsFilePath(IN_BSTR aFilePath, IProgress **aProgress)
5362{
5363 CheckComArgStrNotEmptyOrNull(aFilePath);
5364 CheckComArgOutPointerValid(aProgress);
5365
5366 AutoCaller autoCaller(this);
5367 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5368
5369 *aProgress = NULL;
5370 ReturnComNotImplemented();
5371}
5372
5373STDMETHODIMP Machine::SaveSettings()
5374{
5375 AutoCaller autoCaller(this);
5376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5377
5378 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5379
5380 /* when there was auto-conversion, we want to save the file even if
5381 * the VM is saved */
5382 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5383 if (FAILED(rc)) return rc;
5384
5385 /* the settings file path may never be null */
5386 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5387
5388 /* save all VM data excluding snapshots */
5389 bool fNeedsGlobalSaveSettings = false;
5390 rc = saveSettings(&fNeedsGlobalSaveSettings);
5391 mlock.release();
5392
5393 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5394 {
5395 // save the global settings; for that we should hold only the VirtualBox lock
5396 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5397 rc = mParent->saveSettings();
5398 }
5399
5400 return rc;
5401}
5402
5403STDMETHODIMP Machine::DiscardSettings()
5404{
5405 AutoCaller autoCaller(this);
5406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5407
5408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5409
5410 HRESULT rc = checkStateDependency(MutableStateDep);
5411 if (FAILED(rc)) return rc;
5412
5413 /*
5414 * during this rollback, the session will be notified if data has
5415 * been actually changed
5416 */
5417 rollback(true /* aNotify */);
5418
5419 return S_OK;
5420}
5421
5422/** @note Locks objects! */
5423STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5424 ComSafeArrayOut(IMedium*, aMedia))
5425{
5426 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5427 AutoLimitedCaller autoCaller(this);
5428 AssertComRCReturnRC(autoCaller.rc());
5429
5430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5431
5432 Guid id(getId());
5433
5434 if (mData->mSession.mState != SessionState_Unlocked)
5435 return setError(VBOX_E_INVALID_OBJECT_STATE,
5436 tr("Cannot unregister the machine '%s' while it is locked"),
5437 mUserData->s.strName.c_str());
5438
5439 // wait for state dependents to drop to zero
5440 ensureNoStateDependencies();
5441
5442 if (!mData->mAccessible)
5443 {
5444 // inaccessible maschines can only be unregistered; uninitialize ourselves
5445 // here because currently there may be no unregistered that are inaccessible
5446 // (this state combination is not supported). Note releasing the caller and
5447 // leaving the lock before calling uninit()
5448 alock.release();
5449 autoCaller.release();
5450
5451 uninit();
5452
5453 mParent->unregisterMachine(this, id);
5454 // calls VirtualBox::saveSettings()
5455
5456 return S_OK;
5457 }
5458
5459 HRESULT rc = S_OK;
5460
5461 // discard saved state
5462 if (mData->mMachineState == MachineState_Saved)
5463 {
5464 // add the saved state file to the list of files the caller should delete
5465 Assert(!mSSData->strStateFilePath.isEmpty());
5466 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5467
5468 mSSData->strStateFilePath.setNull();
5469
5470 // unconditionally set the machine state to powered off, we now
5471 // know no session has locked the machine
5472 mData->mMachineState = MachineState_PoweredOff;
5473 }
5474
5475 size_t cSnapshots = 0;
5476 if (mData->mFirstSnapshot)
5477 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5478 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5479 // fail now before we start detaching media
5480 return setError(VBOX_E_INVALID_OBJECT_STATE,
5481 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5482 mUserData->s.strName.c_str(), cSnapshots);
5483
5484 // This list collects the medium objects from all medium attachments
5485 // which we will detach from the machine and its snapshots, in a specific
5486 // order which allows for closing all media without getting "media in use"
5487 // errors, simply by going through the list from the front to the back:
5488 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5489 // and must be closed before the parent media from the snapshots, or closing the parents
5490 // will fail because they still have children);
5491 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5492 // the root ("first") snapshot of the machine.
5493 MediaList llMedia;
5494
5495 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5496 && mMediaData->mAttachments.size()
5497 )
5498 {
5499 // we have media attachments: detach them all and add the Medium objects to our list
5500 if (cleanupMode != CleanupMode_UnregisterOnly)
5501 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5502 else
5503 return setError(VBOX_E_INVALID_OBJECT_STATE,
5504 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5505 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5506 }
5507
5508 if (cSnapshots)
5509 {
5510 // autoCleanup must be true here, or we would have failed above
5511
5512 // add the media from the medium attachments of the snapshots to llMedia
5513 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5514 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5515 // into the children first
5516
5517 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5518 MachineState_T oldState = mData->mMachineState;
5519 mData->mMachineState = MachineState_DeletingSnapshot;
5520
5521 // make a copy of the first snapshot so the refcount does not drop to 0
5522 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5523 // because of the AutoCaller voodoo)
5524 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5525
5526 // GO!
5527 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5528
5529 mData->mMachineState = oldState;
5530 }
5531
5532 if (FAILED(rc))
5533 {
5534 rollbackMedia();
5535 return rc;
5536 }
5537
5538 // commit all the media changes made above
5539 commitMedia();
5540
5541 mData->mRegistered = false;
5542
5543 // machine lock no longer needed
5544 alock.release();
5545
5546 // return media to caller
5547 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5548 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5549
5550 mParent->unregisterMachine(this, id);
5551 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5552
5553 return S_OK;
5554}
5555
5556struct Machine::DeleteTask
5557{
5558 ComObjPtr<Machine> pMachine;
5559 RTCList<ComPtr<IMedium> > llMediums;
5560 StringsList llFilesToDelete;
5561 ComObjPtr<Progress> pProgress;
5562};
5563
5564STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5565{
5566 LogFlowFuncEnter();
5567
5568 AutoCaller autoCaller(this);
5569 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5570
5571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5572
5573 HRESULT rc = checkStateDependency(MutableStateDep);
5574 if (FAILED(rc)) return rc;
5575
5576 if (mData->mRegistered)
5577 return setError(VBOX_E_INVALID_VM_STATE,
5578 tr("Cannot delete settings of a registered machine"));
5579
5580 DeleteTask *pTask = new DeleteTask;
5581 pTask->pMachine = this;
5582 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5583
5584 // collect files to delete
5585 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5586
5587 for (size_t i = 0; i < sfaMedia.size(); ++i)
5588 {
5589 IMedium *pIMedium(sfaMedia[i]);
5590 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5591 if (pMedium.isNull())
5592 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5593 SafeArray<BSTR> ids;
5594 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5595 if (FAILED(rc)) return rc;
5596 /* At this point the medium should not have any back references
5597 * anymore. If it has it is attached to another VM and *must* not
5598 * deleted. */
5599 if (ids.size() < 1)
5600 pTask->llMediums.append(pMedium);
5601 }
5602 if (mData->pMachineConfigFile->fileExists())
5603 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5604
5605 pTask->pProgress.createObject();
5606 pTask->pProgress->init(getVirtualBox(),
5607 static_cast<IMachine*>(this) /* aInitiator */,
5608 Bstr(tr("Deleting files")).raw(),
5609 true /* fCancellable */,
5610 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5611 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5612
5613 int vrc = RTThreadCreate(NULL,
5614 Machine::deleteThread,
5615 (void*)pTask,
5616 0,
5617 RTTHREADTYPE_MAIN_WORKER,
5618 0,
5619 "MachineDelete");
5620
5621 pTask->pProgress.queryInterfaceTo(aProgress);
5622
5623 if (RT_FAILURE(vrc))
5624 {
5625 delete pTask;
5626 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5627 }
5628
5629 LogFlowFuncLeave();
5630
5631 return S_OK;
5632}
5633
5634/**
5635 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5636 * calls Machine::deleteTaskWorker() on the actual machine object.
5637 * @param Thread
5638 * @param pvUser
5639 * @return
5640 */
5641/*static*/
5642DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5643{
5644 LogFlowFuncEnter();
5645
5646 DeleteTask *pTask = (DeleteTask*)pvUser;
5647 Assert(pTask);
5648 Assert(pTask->pMachine);
5649 Assert(pTask->pProgress);
5650
5651 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5652 pTask->pProgress->notifyComplete(rc);
5653
5654 delete pTask;
5655
5656 LogFlowFuncLeave();
5657
5658 NOREF(Thread);
5659
5660 return VINF_SUCCESS;
5661}
5662
5663/**
5664 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5665 * @param task
5666 * @return
5667 */
5668HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5669{
5670 AutoCaller autoCaller(this);
5671 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5672
5673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5674
5675 HRESULT rc = S_OK;
5676
5677 try
5678 {
5679 ULONG uLogHistoryCount = 3;
5680 ComPtr<ISystemProperties> systemProperties;
5681 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5682 if (FAILED(rc)) throw rc;
5683
5684 if (!systemProperties.isNull())
5685 {
5686 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5687 if (FAILED(rc)) throw rc;
5688 }
5689
5690 MachineState_T oldState = mData->mMachineState;
5691 setMachineState(MachineState_SettingUp);
5692 alock.release();
5693 for (size_t i = 0; i < task.llMediums.size(); ++i)
5694 {
5695 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5696 {
5697 AutoCaller mac(pMedium);
5698 if (FAILED(mac.rc())) throw mac.rc();
5699 Utf8Str strLocation = pMedium->i_getLocationFull();
5700 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5701 if (FAILED(rc)) throw rc;
5702 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5703 }
5704 ComPtr<IProgress> pProgress2;
5705 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5706 if (FAILED(rc)) throw rc;
5707 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5708 if (FAILED(rc)) throw rc;
5709 /* Check the result of the asynchronous process. */
5710 LONG iRc;
5711 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5712 if (FAILED(rc)) throw rc;
5713 /* If the thread of the progress object has an error, then
5714 * retrieve the error info from there, or it'll be lost. */
5715 if (FAILED(iRc))
5716 throw setError(ProgressErrorInfo(pProgress2));
5717
5718 /* Close the medium, deliberately without checking the return
5719 * code, and without leaving any trace in the error info, as
5720 * a failure here is a very minor issue, which shouldn't happen
5721 * as above we even managed to delete the medium. */
5722 {
5723 ErrorInfoKeeper eik;
5724 pMedium->Close();
5725 }
5726 }
5727 setMachineState(oldState);
5728 alock.acquire();
5729
5730 // delete the files pushed on the task list by Machine::Delete()
5731 // (this includes saved states of the machine and snapshots and
5732 // medium storage files from the IMedium list passed in, and the
5733 // machine XML file)
5734 StringsList::const_iterator it = task.llFilesToDelete.begin();
5735 while (it != task.llFilesToDelete.end())
5736 {
5737 const Utf8Str &strFile = *it;
5738 LogFunc(("Deleting file %s\n", strFile.c_str()));
5739 int vrc = RTFileDelete(strFile.c_str());
5740 if (RT_FAILURE(vrc))
5741 throw setError(VBOX_E_IPRT_ERROR,
5742 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5743
5744 ++it;
5745 if (it == task.llFilesToDelete.end())
5746 {
5747 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5748 if (FAILED(rc)) throw rc;
5749 break;
5750 }
5751
5752 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5753 if (FAILED(rc)) throw rc;
5754 }
5755
5756 /* delete the settings only when the file actually exists */
5757 if (mData->pMachineConfigFile->fileExists())
5758 {
5759 /* Delete any backup or uncommitted XML files. Ignore failures.
5760 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5761 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5762 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5763 RTFileDelete(otherXml.c_str());
5764 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5765 RTFileDelete(otherXml.c_str());
5766
5767 /* delete the Logs folder, nothing important should be left
5768 * there (we don't check for errors because the user might have
5769 * some private files there that we don't want to delete) */
5770 Utf8Str logFolder;
5771 getLogFolder(logFolder);
5772 Assert(logFolder.length());
5773 if (RTDirExists(logFolder.c_str()))
5774 {
5775 /* Delete all VBox.log[.N] files from the Logs folder
5776 * (this must be in sync with the rotation logic in
5777 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5778 * files that may have been created by the GUI. */
5779 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5780 logFolder.c_str(), RTPATH_DELIMITER);
5781 RTFileDelete(log.c_str());
5782 log = Utf8StrFmt("%s%cVBox.png",
5783 logFolder.c_str(), RTPATH_DELIMITER);
5784 RTFileDelete(log.c_str());
5785 for (int i = uLogHistoryCount; i > 0; i--)
5786 {
5787 log = Utf8StrFmt("%s%cVBox.log.%d",
5788 logFolder.c_str(), RTPATH_DELIMITER, i);
5789 RTFileDelete(log.c_str());
5790 log = Utf8StrFmt("%s%cVBox.png.%d",
5791 logFolder.c_str(), RTPATH_DELIMITER, i);
5792 RTFileDelete(log.c_str());
5793 }
5794
5795 RTDirRemove(logFolder.c_str());
5796 }
5797
5798 /* delete the Snapshots folder, nothing important should be left
5799 * there (we don't check for errors because the user might have
5800 * some private files there that we don't want to delete) */
5801 Utf8Str strFullSnapshotFolder;
5802 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5803 Assert(!strFullSnapshotFolder.isEmpty());
5804 if (RTDirExists(strFullSnapshotFolder.c_str()))
5805 RTDirRemove(strFullSnapshotFolder.c_str());
5806
5807 // delete the directory that contains the settings file, but only
5808 // if it matches the VM name
5809 Utf8Str settingsDir;
5810 if (isInOwnDir(&settingsDir))
5811 RTDirRemove(settingsDir.c_str());
5812 }
5813
5814 alock.release();
5815
5816 mParent->saveModifiedRegistries();
5817 }
5818 catch (HRESULT aRC) { rc = aRC; }
5819
5820 return rc;
5821}
5822
5823STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5824{
5825 CheckComArgOutPointerValid(aSnapshot);
5826
5827 AutoCaller autoCaller(this);
5828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5829
5830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5831
5832 ComObjPtr<Snapshot> pSnapshot;
5833 HRESULT rc;
5834
5835 if (!aNameOrId || !*aNameOrId)
5836 // null case (caller wants root snapshot): findSnapshotById() handles this
5837 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5838 else
5839 {
5840 Guid uuid(aNameOrId);
5841 if (uuid.isValid())
5842 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5843 else
5844 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5845 }
5846 pSnapshot.queryInterfaceTo(aSnapshot);
5847
5848 return rc;
5849}
5850
5851STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5852{
5853 CheckComArgStrNotEmptyOrNull(aName);
5854 CheckComArgStrNotEmptyOrNull(aHostPath);
5855
5856 AutoCaller autoCaller(this);
5857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5858
5859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5860
5861 HRESULT rc = checkStateDependency(MutableStateDep);
5862 if (FAILED(rc)) return rc;
5863
5864 Utf8Str strName(aName);
5865
5866 ComObjPtr<SharedFolder> sharedFolder;
5867 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5868 if (SUCCEEDED(rc))
5869 return setError(VBOX_E_OBJECT_IN_USE,
5870 tr("Shared folder named '%s' already exists"),
5871 strName.c_str());
5872
5873 sharedFolder.createObject();
5874 rc = sharedFolder->init(getMachine(),
5875 strName,
5876 aHostPath,
5877 !!aWritable,
5878 !!aAutoMount,
5879 true /* fFailOnError */);
5880 if (FAILED(rc)) return rc;
5881
5882 setModified(IsModified_SharedFolders);
5883 mHWData.backup();
5884 mHWData->mSharedFolders.push_back(sharedFolder);
5885
5886 /* inform the direct session if any */
5887 alock.release();
5888 onSharedFolderChange();
5889
5890 return S_OK;
5891}
5892
5893STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5894{
5895 CheckComArgStrNotEmptyOrNull(aName);
5896
5897 AutoCaller autoCaller(this);
5898 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5899
5900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5901
5902 HRESULT rc = checkStateDependency(MutableStateDep);
5903 if (FAILED(rc)) return rc;
5904
5905 ComObjPtr<SharedFolder> sharedFolder;
5906 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5907 if (FAILED(rc)) return rc;
5908
5909 setModified(IsModified_SharedFolders);
5910 mHWData.backup();
5911 mHWData->mSharedFolders.remove(sharedFolder);
5912
5913 /* inform the direct session if any */
5914 alock.release();
5915 onSharedFolderChange();
5916
5917 return S_OK;
5918}
5919
5920STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5921{
5922 CheckComArgOutPointerValid(aCanShow);
5923
5924 /* start with No */
5925 *aCanShow = FALSE;
5926
5927 AutoCaller autoCaller(this);
5928 AssertComRCReturnRC(autoCaller.rc());
5929
5930 ComPtr<IInternalSessionControl> directControl;
5931 {
5932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5933
5934 if (mData->mSession.mState != SessionState_Locked)
5935 return setError(VBOX_E_INVALID_VM_STATE,
5936 tr("Machine is not locked for session (session state: %s)"),
5937 Global::stringifySessionState(mData->mSession.mState));
5938
5939 directControl = mData->mSession.mDirectControl;
5940 }
5941
5942 /* ignore calls made after #OnSessionEnd() is called */
5943 if (!directControl)
5944 return S_OK;
5945
5946 LONG64 dummy;
5947 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5948}
5949
5950STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5951{
5952 CheckComArgOutPointerValid(aWinId);
5953
5954 AutoCaller autoCaller(this);
5955 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5956
5957 ComPtr<IInternalSessionControl> directControl;
5958 {
5959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5960
5961 if (mData->mSession.mState != SessionState_Locked)
5962 return setError(E_FAIL,
5963 tr("Machine is not locked for session (session state: %s)"),
5964 Global::stringifySessionState(mData->mSession.mState));
5965
5966 directControl = mData->mSession.mDirectControl;
5967 }
5968
5969 /* ignore calls made after #OnSessionEnd() is called */
5970 if (!directControl)
5971 return S_OK;
5972
5973 BOOL dummy;
5974 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5975}
5976
5977#ifdef VBOX_WITH_GUEST_PROPS
5978/**
5979 * Look up a guest property in VBoxSVC's internal structures.
5980 */
5981HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5982 BSTR *aValue,
5983 LONG64 *aTimestamp,
5984 BSTR *aFlags) const
5985{
5986 using namespace guestProp;
5987
5988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5989 Utf8Str strName(aName);
5990 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5991
5992 if (it != mHWData->mGuestProperties.end())
5993 {
5994 char szFlags[MAX_FLAGS_LEN + 1];
5995 it->second.strValue.cloneTo(aValue);
5996 *aTimestamp = it->second.mTimestamp;
5997 writeFlags(it->second.mFlags, szFlags);
5998 Bstr(szFlags).cloneTo(aFlags);
5999 }
6000
6001 return S_OK;
6002}
6003
6004/**
6005 * Query the VM that a guest property belongs to for the property.
6006 * @returns E_ACCESSDENIED if the VM process is not available or not
6007 * currently handling queries and the lookup should then be done in
6008 * VBoxSVC.
6009 */
6010HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
6011 BSTR *aValue,
6012 LONG64 *aTimestamp,
6013 BSTR *aFlags) const
6014{
6015 HRESULT rc;
6016 ComPtr<IInternalSessionControl> directControl;
6017 directControl = mData->mSession.mDirectControl;
6018
6019 /* fail if we were called after #OnSessionEnd() is called. This is a
6020 * silly race condition. */
6021
6022 /** @todo This code is bothering API clients (like python script clients) with
6023 * the AccessGuestProperty call, creating unncessary IPC. Need to
6024 * have a way of figuring out which kind of direct session it is... */
6025 if (!directControl)
6026 rc = E_ACCESSDENIED;
6027 else
6028 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
6029 false /* isSetter */,
6030 aValue, aTimestamp, aFlags);
6031 return rc;
6032}
6033#endif // VBOX_WITH_GUEST_PROPS
6034
6035STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
6036 BSTR *aValue,
6037 LONG64 *aTimestamp,
6038 BSTR *aFlags)
6039{
6040#ifndef VBOX_WITH_GUEST_PROPS
6041 ReturnComNotImplemented();
6042#else // VBOX_WITH_GUEST_PROPS
6043 CheckComArgStrNotEmptyOrNull(aName);
6044 CheckComArgOutPointerValid(aValue);
6045 CheckComArgOutPointerValid(aTimestamp);
6046 CheckComArgOutPointerValid(aFlags);
6047
6048 AutoCaller autoCaller(this);
6049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6050
6051 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
6052 if (rc == E_ACCESSDENIED)
6053 /* The VM is not running or the service is not (yet) accessible */
6054 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
6055 return rc;
6056#endif // VBOX_WITH_GUEST_PROPS
6057}
6058
6059STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
6060{
6061 LONG64 dummyTimestamp;
6062 Bstr dummyFlags;
6063 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
6064}
6065
6066STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
6067{
6068 Bstr dummyValue;
6069 Bstr dummyFlags;
6070 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
6071}
6072
6073#ifdef VBOX_WITH_GUEST_PROPS
6074/**
6075 * Set a guest property in VBoxSVC's internal structures.
6076 */
6077HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
6078 IN_BSTR aFlags)
6079{
6080 using namespace guestProp;
6081
6082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6083 HRESULT rc = S_OK;
6084
6085 rc = checkStateDependency(MutableStateDep);
6086 if (FAILED(rc)) return rc;
6087
6088 try
6089 {
6090 Utf8Str utf8Name(aName);
6091 Utf8Str utf8Flags(aFlags);
6092 uint32_t fFlags = NILFLAG;
6093 if ( aFlags != NULL
6094 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6095 return setError(E_INVALIDARG,
6096 tr("Invalid guest property flag values: '%ls'"),
6097 aFlags);
6098
6099 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6100 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6101 if (it == mHWData->mGuestProperties.end())
6102 {
6103 if (!fDelete)
6104 {
6105 setModified(IsModified_MachineData);
6106 mHWData.backupEx();
6107
6108 RTTIMESPEC time;
6109 HWData::GuestProperty prop;
6110 prop.strValue = aValue;
6111 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6112 prop.mFlags = fFlags;
6113 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6114 }
6115 }
6116 else
6117 {
6118 if (it->second.mFlags & (RDONLYHOST))
6119 {
6120 rc = setError(E_ACCESSDENIED,
6121 tr("The property '%ls' cannot be changed by the host"),
6122 aName);
6123 }
6124 else
6125 {
6126 setModified(IsModified_MachineData);
6127 mHWData.backupEx();
6128
6129 /* The backupEx() operation invalidates our iterator,
6130 * so get a new one. */
6131 it = mHWData->mGuestProperties.find(utf8Name);
6132 Assert(it != mHWData->mGuestProperties.end());
6133
6134 if (!fDelete)
6135 {
6136 RTTIMESPEC time;
6137 it->second.strValue = aValue;
6138 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6139 it->second.mFlags = fFlags;
6140 }
6141 else
6142 mHWData->mGuestProperties.erase(it);
6143 }
6144 }
6145
6146 if ( SUCCEEDED(rc)
6147 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6148 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6149 RTSTR_MAX,
6150 utf8Name.c_str(),
6151 RTSTR_MAX,
6152 NULL)
6153 )
6154 )
6155 {
6156 alock.release();
6157
6158 mParent->onGuestPropertyChange(mData->mUuid, aName,
6159 aValue ? aValue : Bstr("").raw(),
6160 aFlags ? aFlags : Bstr("").raw());
6161 }
6162 }
6163 catch (std::bad_alloc &)
6164 {
6165 rc = E_OUTOFMEMORY;
6166 }
6167
6168 return rc;
6169}
6170
6171/**
6172 * Set a property on the VM that that property belongs to.
6173 * @returns E_ACCESSDENIED if the VM process is not available or not
6174 * currently handling queries and the setting should then be done in
6175 * VBoxSVC.
6176 */
6177HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6178 IN_BSTR aFlags)
6179{
6180 HRESULT rc;
6181
6182 try
6183 {
6184 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6185
6186 BSTR dummy = NULL; /* will not be changed (setter) */
6187 LONG64 dummy64;
6188 if (!directControl)
6189 rc = E_ACCESSDENIED;
6190 else
6191 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6192 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6193 true /* isSetter */,
6194 &dummy, &dummy64, &dummy);
6195 }
6196 catch (std::bad_alloc &)
6197 {
6198 rc = E_OUTOFMEMORY;
6199 }
6200
6201 return rc;
6202}
6203#endif // VBOX_WITH_GUEST_PROPS
6204
6205STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6206 IN_BSTR aFlags)
6207{
6208#ifndef VBOX_WITH_GUEST_PROPS
6209 ReturnComNotImplemented();
6210#else // VBOX_WITH_GUEST_PROPS
6211 CheckComArgStrNotEmptyOrNull(aName);
6212 CheckComArgMaybeNull(aFlags);
6213 CheckComArgMaybeNull(aValue);
6214
6215 AutoCaller autoCaller(this);
6216 if (FAILED(autoCaller.rc()))
6217 return autoCaller.rc();
6218
6219 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6220 if (rc == E_ACCESSDENIED)
6221 /* The VM is not running or the service is not (yet) accessible */
6222 rc = setGuestPropertyToService(aName, aValue, aFlags);
6223 return rc;
6224#endif // VBOX_WITH_GUEST_PROPS
6225}
6226
6227STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6228{
6229 return SetGuestProperty(aName, aValue, NULL);
6230}
6231
6232STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6233{
6234 return SetGuestProperty(aName, NULL, NULL);
6235}
6236
6237#ifdef VBOX_WITH_GUEST_PROPS
6238/**
6239 * Enumerate the guest properties in VBoxSVC's internal structures.
6240 */
6241HRESULT Machine::enumerateGuestPropertiesInService
6242 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6243 ComSafeArrayOut(BSTR, aValues),
6244 ComSafeArrayOut(LONG64, aTimestamps),
6245 ComSafeArrayOut(BSTR, aFlags))
6246{
6247 using namespace guestProp;
6248
6249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6250 Utf8Str strPatterns(aPatterns);
6251
6252 HWData::GuestPropertyMap propMap;
6253
6254 /*
6255 * Look for matching patterns and build up a list.
6256 */
6257 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6258 while (it != mHWData->mGuestProperties.end())
6259 {
6260 if ( strPatterns.isEmpty()
6261 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6262 RTSTR_MAX,
6263 it->first.c_str(),
6264 RTSTR_MAX,
6265 NULL)
6266 )
6267 {
6268 propMap.insert(*it);
6269 }
6270
6271 it++;
6272 }
6273
6274 alock.release();
6275
6276 /*
6277 * And build up the arrays for returning the property information.
6278 */
6279 size_t cEntries = propMap.size();
6280 SafeArray<BSTR> names(cEntries);
6281 SafeArray<BSTR> values(cEntries);
6282 SafeArray<LONG64> timestamps(cEntries);
6283 SafeArray<BSTR> flags(cEntries);
6284 size_t iProp = 0;
6285
6286 it = propMap.begin();
6287 while (it != propMap.end())
6288 {
6289 char szFlags[MAX_FLAGS_LEN + 1];
6290 it->first.cloneTo(&names[iProp]);
6291 it->second.strValue.cloneTo(&values[iProp]);
6292 timestamps[iProp] = it->second.mTimestamp;
6293 writeFlags(it->second.mFlags, szFlags);
6294 Bstr(szFlags).cloneTo(&flags[iProp++]);
6295 it++;
6296 }
6297 names.detachTo(ComSafeArrayOutArg(aNames));
6298 values.detachTo(ComSafeArrayOutArg(aValues));
6299 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6300 flags.detachTo(ComSafeArrayOutArg(aFlags));
6301 return S_OK;
6302}
6303
6304/**
6305 * Enumerate the properties managed by a VM.
6306 * @returns E_ACCESSDENIED if the VM process is not available or not
6307 * currently handling queries and the setting should then be done in
6308 * VBoxSVC.
6309 */
6310HRESULT Machine::enumerateGuestPropertiesOnVM
6311 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6312 ComSafeArrayOut(BSTR, aValues),
6313 ComSafeArrayOut(LONG64, aTimestamps),
6314 ComSafeArrayOut(BSTR, aFlags))
6315{
6316 HRESULT rc;
6317 ComPtr<IInternalSessionControl> directControl;
6318 directControl = mData->mSession.mDirectControl;
6319
6320 if (!directControl)
6321 rc = E_ACCESSDENIED;
6322 else
6323 rc = directControl->EnumerateGuestProperties
6324 (aPatterns, ComSafeArrayOutArg(aNames),
6325 ComSafeArrayOutArg(aValues),
6326 ComSafeArrayOutArg(aTimestamps),
6327 ComSafeArrayOutArg(aFlags));
6328 return rc;
6329}
6330#endif // VBOX_WITH_GUEST_PROPS
6331
6332STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6333 ComSafeArrayOut(BSTR, aNames),
6334 ComSafeArrayOut(BSTR, aValues),
6335 ComSafeArrayOut(LONG64, aTimestamps),
6336 ComSafeArrayOut(BSTR, aFlags))
6337{
6338#ifndef VBOX_WITH_GUEST_PROPS
6339 ReturnComNotImplemented();
6340#else // VBOX_WITH_GUEST_PROPS
6341 CheckComArgMaybeNull(aPatterns);
6342 CheckComArgOutSafeArrayPointerValid(aNames);
6343 CheckComArgOutSafeArrayPointerValid(aValues);
6344 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6345 CheckComArgOutSafeArrayPointerValid(aFlags);
6346
6347 AutoCaller autoCaller(this);
6348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6349
6350 HRESULT rc = enumerateGuestPropertiesOnVM
6351 (aPatterns, ComSafeArrayOutArg(aNames),
6352 ComSafeArrayOutArg(aValues),
6353 ComSafeArrayOutArg(aTimestamps),
6354 ComSafeArrayOutArg(aFlags));
6355 if (rc == E_ACCESSDENIED)
6356 /* The VM is not running or the service is not (yet) accessible */
6357 rc = enumerateGuestPropertiesInService
6358 (aPatterns, ComSafeArrayOutArg(aNames),
6359 ComSafeArrayOutArg(aValues),
6360 ComSafeArrayOutArg(aTimestamps),
6361 ComSafeArrayOutArg(aFlags));
6362 return rc;
6363#endif // VBOX_WITH_GUEST_PROPS
6364}
6365
6366STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6367 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6368{
6369 MediaData::AttachmentList atts;
6370
6371 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6372 if (FAILED(rc)) return rc;
6373
6374 SafeIfaceArray<IMediumAttachment> attachments(atts);
6375 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6376
6377 return S_OK;
6378}
6379
6380STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6381 LONG aControllerPort,
6382 LONG aDevice,
6383 IMediumAttachment **aAttachment)
6384{
6385 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6386 aControllerName, aControllerPort, aDevice));
6387
6388 CheckComArgStrNotEmptyOrNull(aControllerName);
6389 CheckComArgOutPointerValid(aAttachment);
6390
6391 AutoCaller autoCaller(this);
6392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6393
6394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6395
6396 *aAttachment = NULL;
6397
6398 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6399 aControllerName,
6400 aControllerPort,
6401 aDevice);
6402 if (pAttach.isNull())
6403 return setError(VBOX_E_OBJECT_NOT_FOUND,
6404 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6405 aDevice, aControllerPort, aControllerName);
6406
6407 pAttach.queryInterfaceTo(aAttachment);
6408
6409 return S_OK;
6410}
6411
6412STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6413 StorageBus_T aConnectionType,
6414 IStorageController **controller)
6415{
6416 CheckComArgStrNotEmptyOrNull(aName);
6417
6418 if ( (aConnectionType <= StorageBus_Null)
6419 || (aConnectionType > StorageBus_USB))
6420 return setError(E_INVALIDARG,
6421 tr("Invalid connection type: %d"),
6422 aConnectionType);
6423
6424 AutoCaller autoCaller(this);
6425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6426
6427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6428
6429 HRESULT rc = checkStateDependency(MutableStateDep);
6430 if (FAILED(rc)) return rc;
6431
6432 /* try to find one with the name first. */
6433 ComObjPtr<StorageController> ctrl;
6434
6435 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6436 if (SUCCEEDED(rc))
6437 return setError(VBOX_E_OBJECT_IN_USE,
6438 tr("Storage controller named '%ls' already exists"),
6439 aName);
6440
6441 ctrl.createObject();
6442
6443 /* get a new instance number for the storage controller */
6444 ULONG ulInstance = 0;
6445 bool fBootable = true;
6446 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6447 it != mStorageControllers->end();
6448 ++it)
6449 {
6450 if ((*it)->i_getStorageBus() == aConnectionType)
6451 {
6452 ULONG ulCurInst = (*it)->i_getInstance();
6453
6454 if (ulCurInst >= ulInstance)
6455 ulInstance = ulCurInst + 1;
6456
6457 /* Only one controller of each type can be marked as bootable. */
6458 if ((*it)->i_getBootable())
6459 fBootable = false;
6460 }
6461 }
6462
6463 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6464 if (FAILED(rc)) return rc;
6465
6466 setModified(IsModified_Storage);
6467 mStorageControllers.backup();
6468 mStorageControllers->push_back(ctrl);
6469
6470 ctrl.queryInterfaceTo(controller);
6471
6472 /* inform the direct session if any */
6473 alock.release();
6474 onStorageControllerChange();
6475
6476 return S_OK;
6477}
6478
6479STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6480 IStorageController **aStorageController)
6481{
6482 CheckComArgStrNotEmptyOrNull(aName);
6483
6484 AutoCaller autoCaller(this);
6485 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6486
6487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6488
6489 ComObjPtr<StorageController> ctrl;
6490
6491 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6492 if (SUCCEEDED(rc))
6493 ctrl.queryInterfaceTo(aStorageController);
6494
6495 return rc;
6496}
6497
6498STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6499 IStorageController **aStorageController)
6500{
6501 AutoCaller autoCaller(this);
6502 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6503
6504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6505
6506 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6507 it != mStorageControllers->end();
6508 ++it)
6509 {
6510 if ((*it)->i_getInstance() == aInstance)
6511 {
6512 (*it).queryInterfaceTo(aStorageController);
6513 return S_OK;
6514 }
6515 }
6516
6517 return setError(VBOX_E_OBJECT_NOT_FOUND,
6518 tr("Could not find a storage controller with instance number '%lu'"),
6519 aInstance);
6520}
6521
6522STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6523{
6524 AutoCaller autoCaller(this);
6525 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6526
6527 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6528
6529 HRESULT rc = checkStateDependency(MutableStateDep);
6530 if (FAILED(rc)) return rc;
6531
6532 ComObjPtr<StorageController> ctrl;
6533
6534 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6535 if (SUCCEEDED(rc))
6536 {
6537 /* Ensure that only one controller of each type is marked as bootable. */
6538 if (fBootable == TRUE)
6539 {
6540 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6541 it != mStorageControllers->end();
6542 ++it)
6543 {
6544 ComObjPtr<StorageController> aCtrl = (*it);
6545
6546 if ( (aCtrl->i_getName() != Utf8Str(aName))
6547 && aCtrl->i_getBootable() == TRUE
6548 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6549 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6550 {
6551 aCtrl->i_setBootable(FALSE);
6552 break;
6553 }
6554 }
6555 }
6556
6557 if (SUCCEEDED(rc))
6558 {
6559 ctrl->i_setBootable(fBootable);
6560 setModified(IsModified_Storage);
6561 }
6562 }
6563
6564 if (SUCCEEDED(rc))
6565 {
6566 /* inform the direct session if any */
6567 alock.release();
6568 onStorageControllerChange();
6569 }
6570
6571 return rc;
6572}
6573
6574STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6575{
6576 CheckComArgStrNotEmptyOrNull(aName);
6577
6578 AutoCaller autoCaller(this);
6579 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6580
6581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6582
6583 HRESULT rc = checkStateDependency(MutableStateDep);
6584 if (FAILED(rc)) return rc;
6585
6586 ComObjPtr<StorageController> ctrl;
6587 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6588 if (FAILED(rc)) return rc;
6589
6590 {
6591 /* find all attached devices to the appropriate storage controller and detach them all */
6592 // make a temporary list because detachDevice invalidates iterators into
6593 // mMediaData->mAttachments
6594 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6595
6596 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6597 it != llAttachments2.end();
6598 ++it)
6599 {
6600 MediumAttachment *pAttachTemp = *it;
6601
6602 AutoCaller localAutoCaller(pAttachTemp);
6603 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6604
6605 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6606
6607 if (pAttachTemp->i_getControllerName() == aName)
6608 {
6609 rc = detachDevice(pAttachTemp, alock, NULL);
6610 if (FAILED(rc)) return rc;
6611 }
6612 }
6613 }
6614
6615 /* We can remove it now. */
6616 setModified(IsModified_Storage);
6617 mStorageControllers.backup();
6618
6619 ctrl->i_unshare();
6620
6621 mStorageControllers->remove(ctrl);
6622
6623 /* inform the direct session if any */
6624 alock.release();
6625 onStorageControllerChange();
6626
6627 return S_OK;
6628}
6629
6630STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6631 IUSBController **controller)
6632{
6633 if ( (aType <= USBControllerType_Null)
6634 || (aType >= USBControllerType_Last))
6635 return setError(E_INVALIDARG,
6636 tr("Invalid USB controller type: %d"),
6637 aType);
6638
6639 AutoCaller autoCaller(this);
6640 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6641
6642 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6643
6644 HRESULT rc = checkStateDependency(MutableStateDep);
6645 if (FAILED(rc)) return rc;
6646
6647 /* try to find one with the same type first. */
6648 ComObjPtr<USBController> ctrl;
6649
6650 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6651 if (SUCCEEDED(rc))
6652 return setError(VBOX_E_OBJECT_IN_USE,
6653 tr("USB controller named '%ls' already exists"),
6654 aName);
6655
6656 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6657 ULONG maxInstances;
6658 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6659 if (FAILED(rc))
6660 return rc;
6661
6662 ULONG cInstances = getUSBControllerCountByType(aType);
6663 if (cInstances >= maxInstances)
6664 return setError(E_INVALIDARG,
6665 tr("Too many USB controllers of this type"));
6666
6667 ctrl.createObject();
6668
6669 rc = ctrl->init(this, aName, aType);
6670 if (FAILED(rc)) return rc;
6671
6672 setModified(IsModified_USB);
6673 mUSBControllers.backup();
6674 mUSBControllers->push_back(ctrl);
6675
6676 ctrl.queryInterfaceTo(controller);
6677
6678 /* inform the direct session if any */
6679 alock.release();
6680 onUSBControllerChange();
6681
6682 return S_OK;
6683}
6684
6685STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6686{
6687 CheckComArgStrNotEmptyOrNull(aName);
6688
6689 AutoCaller autoCaller(this);
6690 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6691
6692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6693
6694 ComObjPtr<USBController> ctrl;
6695
6696 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6697 if (SUCCEEDED(rc))
6698 ctrl.queryInterfaceTo(aUSBController);
6699
6700 return rc;
6701}
6702
6703STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6704 ULONG *aControllers)
6705{
6706 CheckComArgOutPointerValid(aControllers);
6707
6708 if ( (aType <= USBControllerType_Null)
6709 || (aType >= USBControllerType_Last))
6710 return setError(E_INVALIDARG,
6711 tr("Invalid USB controller type: %d"),
6712 aType);
6713
6714 AutoCaller autoCaller(this);
6715 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6716
6717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6718
6719 ComObjPtr<USBController> ctrl;
6720
6721 *aControllers = getUSBControllerCountByType(aType);
6722
6723 return S_OK;
6724}
6725
6726STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6727{
6728 CheckComArgStrNotEmptyOrNull(aName);
6729
6730 AutoCaller autoCaller(this);
6731 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6732
6733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6734
6735 HRESULT rc = checkStateDependency(MutableStateDep);
6736 if (FAILED(rc)) return rc;
6737
6738 ComObjPtr<USBController> ctrl;
6739 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6740 if (FAILED(rc)) return rc;
6741
6742 setModified(IsModified_USB);
6743 mUSBControllers.backup();
6744
6745 ctrl->i_unshare();
6746
6747 mUSBControllers->remove(ctrl);
6748
6749 /* inform the direct session if any */
6750 alock.release();
6751 onUSBControllerChange();
6752
6753 return S_OK;
6754}
6755
6756STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6757 ULONG *puOriginX,
6758 ULONG *puOriginY,
6759 ULONG *puWidth,
6760 ULONG *puHeight,
6761 BOOL *pfEnabled)
6762{
6763 LogFlowThisFunc(("\n"));
6764
6765 CheckComArgNotNull(puOriginX);
6766 CheckComArgNotNull(puOriginY);
6767 CheckComArgNotNull(puWidth);
6768 CheckComArgNotNull(puHeight);
6769 CheckComArgNotNull(pfEnabled);
6770
6771 uint32_t u32OriginX= 0;
6772 uint32_t u32OriginY= 0;
6773 uint32_t u32Width = 0;
6774 uint32_t u32Height = 0;
6775 uint16_t u16Flags = 0;
6776
6777 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6778 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6779 if (RT_FAILURE(vrc))
6780 {
6781#ifdef RT_OS_WINDOWS
6782 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6783 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6784 * So just assign fEnable to TRUE again.
6785 * The right fix would be to change GUI API wrappers to make sure that parameters
6786 * are changed only if API succeeds.
6787 */
6788 *pfEnabled = TRUE;
6789#endif
6790 return setError(VBOX_E_IPRT_ERROR,
6791 tr("Saved guest size is not available (%Rrc)"),
6792 vrc);
6793 }
6794
6795 *puOriginX = u32OriginX;
6796 *puOriginY = u32OriginY;
6797 *puWidth = u32Width;
6798 *puHeight = u32Height;
6799 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6800
6801 return S_OK;
6802}
6803
6804STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6805{
6806 LogFlowThisFunc(("\n"));
6807
6808 CheckComArgNotNull(aSize);
6809 CheckComArgNotNull(aWidth);
6810 CheckComArgNotNull(aHeight);
6811
6812 if (aScreenId != 0)
6813 return E_NOTIMPL;
6814
6815 AutoCaller autoCaller(this);
6816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6817
6818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6819
6820 uint8_t *pu8Data = NULL;
6821 uint32_t cbData = 0;
6822 uint32_t u32Width = 0;
6823 uint32_t u32Height = 0;
6824
6825 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6826
6827 if (RT_FAILURE(vrc))
6828 return setError(VBOX_E_IPRT_ERROR,
6829 tr("Saved screenshot data is not available (%Rrc)"),
6830 vrc);
6831
6832 *aSize = cbData;
6833 *aWidth = u32Width;
6834 *aHeight = u32Height;
6835
6836 freeSavedDisplayScreenshot(pu8Data);
6837
6838 return S_OK;
6839}
6840
6841STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6842{
6843 LogFlowThisFunc(("\n"));
6844
6845 CheckComArgNotNull(aWidth);
6846 CheckComArgNotNull(aHeight);
6847 CheckComArgOutSafeArrayPointerValid(aData);
6848
6849 if (aScreenId != 0)
6850 return E_NOTIMPL;
6851
6852 AutoCaller autoCaller(this);
6853 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6854
6855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6856
6857 uint8_t *pu8Data = NULL;
6858 uint32_t cbData = 0;
6859 uint32_t u32Width = 0;
6860 uint32_t u32Height = 0;
6861
6862 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6863
6864 if (RT_FAILURE(vrc))
6865 return setError(VBOX_E_IPRT_ERROR,
6866 tr("Saved screenshot data is not available (%Rrc)"),
6867 vrc);
6868
6869 *aWidth = u32Width;
6870 *aHeight = u32Height;
6871
6872 com::SafeArray<BYTE> bitmap(cbData);
6873 /* Convert pixels to format expected by the API caller. */
6874 if (aBGR)
6875 {
6876 /* [0] B, [1] G, [2] R, [3] A. */
6877 for (unsigned i = 0; i < cbData; i += 4)
6878 {
6879 bitmap[i] = pu8Data[i];
6880 bitmap[i + 1] = pu8Data[i + 1];
6881 bitmap[i + 2] = pu8Data[i + 2];
6882 bitmap[i + 3] = 0xff;
6883 }
6884 }
6885 else
6886 {
6887 /* [0] R, [1] G, [2] B, [3] A. */
6888 for (unsigned i = 0; i < cbData; i += 4)
6889 {
6890 bitmap[i] = pu8Data[i + 2];
6891 bitmap[i + 1] = pu8Data[i + 1];
6892 bitmap[i + 2] = pu8Data[i];
6893 bitmap[i + 3] = 0xff;
6894 }
6895 }
6896 bitmap.detachTo(ComSafeArrayOutArg(aData));
6897
6898 freeSavedDisplayScreenshot(pu8Data);
6899
6900 return S_OK;
6901}
6902
6903
6904STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6905{
6906 LogFlowThisFunc(("\n"));
6907
6908 CheckComArgNotNull(aWidth);
6909 CheckComArgNotNull(aHeight);
6910 CheckComArgOutSafeArrayPointerValid(aData);
6911
6912 if (aScreenId != 0)
6913 return E_NOTIMPL;
6914
6915 AutoCaller autoCaller(this);
6916 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6917
6918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6919
6920 uint8_t *pu8Data = NULL;
6921 uint32_t cbData = 0;
6922 uint32_t u32Width = 0;
6923 uint32_t u32Height = 0;
6924
6925 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6926
6927 if (RT_FAILURE(vrc))
6928 return setError(VBOX_E_IPRT_ERROR,
6929 tr("Saved screenshot data is not available (%Rrc)"),
6930 vrc);
6931
6932 *aWidth = u32Width;
6933 *aHeight = u32Height;
6934
6935 HRESULT rc = S_OK;
6936 uint8_t *pu8PNG = NULL;
6937 uint32_t cbPNG = 0;
6938 uint32_t cxPNG = 0;
6939 uint32_t cyPNG = 0;
6940
6941 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6942
6943 if (RT_SUCCESS(vrc))
6944 {
6945 com::SafeArray<BYTE> screenData(cbPNG);
6946 screenData.initFrom(pu8PNG, cbPNG);
6947 if (pu8PNG)
6948 RTMemFree(pu8PNG);
6949 screenData.detachTo(ComSafeArrayOutArg(aData));
6950 }
6951 else
6952 {
6953 if (pu8PNG)
6954 RTMemFree(pu8PNG);
6955 return setError(VBOX_E_IPRT_ERROR,
6956 tr("Could not convert screenshot to PNG (%Rrc)"),
6957 vrc);
6958 }
6959
6960 freeSavedDisplayScreenshot(pu8Data);
6961
6962 return rc;
6963}
6964
6965STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6966{
6967 LogFlowThisFunc(("\n"));
6968
6969 CheckComArgNotNull(aSize);
6970 CheckComArgNotNull(aWidth);
6971 CheckComArgNotNull(aHeight);
6972
6973 if (aScreenId != 0)
6974 return E_NOTIMPL;
6975
6976 AutoCaller autoCaller(this);
6977 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6978
6979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6980
6981 uint8_t *pu8Data = NULL;
6982 uint32_t cbData = 0;
6983 uint32_t u32Width = 0;
6984 uint32_t u32Height = 0;
6985
6986 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6987
6988 if (RT_FAILURE(vrc))
6989 return setError(VBOX_E_IPRT_ERROR,
6990 tr("Saved screenshot data is not available (%Rrc)"),
6991 vrc);
6992
6993 *aSize = cbData;
6994 *aWidth = u32Width;
6995 *aHeight = u32Height;
6996
6997 freeSavedDisplayScreenshot(pu8Data);
6998
6999 return S_OK;
7000}
7001
7002STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
7003{
7004 LogFlowThisFunc(("\n"));
7005
7006 CheckComArgNotNull(aWidth);
7007 CheckComArgNotNull(aHeight);
7008 CheckComArgOutSafeArrayPointerValid(aData);
7009
7010 if (aScreenId != 0)
7011 return E_NOTIMPL;
7012
7013 AutoCaller autoCaller(this);
7014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7015
7016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7017
7018 uint8_t *pu8Data = NULL;
7019 uint32_t cbData = 0;
7020 uint32_t u32Width = 0;
7021 uint32_t u32Height = 0;
7022
7023 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
7024
7025 if (RT_FAILURE(vrc))
7026 return setError(VBOX_E_IPRT_ERROR,
7027 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
7028 vrc);
7029
7030 *aWidth = u32Width;
7031 *aHeight = u32Height;
7032
7033 com::SafeArray<BYTE> png(cbData);
7034 png.initFrom(pu8Data, cbData);
7035 png.detachTo(ComSafeArrayOutArg(aData));
7036
7037 freeSavedDisplayScreenshot(pu8Data);
7038
7039 return S_OK;
7040}
7041
7042STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
7043{
7044 HRESULT rc = S_OK;
7045 LogFlowThisFunc(("\n"));
7046
7047 AutoCaller autoCaller(this);
7048 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7049
7050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7051
7052 if (!mHWData->mCPUHotPlugEnabled)
7053 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7054
7055 if (aCpu >= mHWData->mCPUCount)
7056 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
7057
7058 if (mHWData->mCPUAttached[aCpu])
7059 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
7060
7061 alock.release();
7062 rc = onCPUChange(aCpu, false);
7063 alock.acquire();
7064 if (FAILED(rc)) return rc;
7065
7066 setModified(IsModified_MachineData);
7067 mHWData.backup();
7068 mHWData->mCPUAttached[aCpu] = true;
7069
7070 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7071 if (Global::IsOnline(mData->mMachineState))
7072 saveSettings(NULL);
7073
7074 return S_OK;
7075}
7076
7077STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
7078{
7079 HRESULT rc = S_OK;
7080 LogFlowThisFunc(("\n"));
7081
7082 AutoCaller autoCaller(this);
7083 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7084
7085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7086
7087 if (!mHWData->mCPUHotPlugEnabled)
7088 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7089
7090 if (aCpu >= SchemaDefs::MaxCPUCount)
7091 return setError(E_INVALIDARG,
7092 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7093 SchemaDefs::MaxCPUCount);
7094
7095 if (!mHWData->mCPUAttached[aCpu])
7096 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7097
7098 /* CPU 0 can't be detached */
7099 if (aCpu == 0)
7100 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7101
7102 alock.release();
7103 rc = onCPUChange(aCpu, true);
7104 alock.acquire();
7105 if (FAILED(rc)) return rc;
7106
7107 setModified(IsModified_MachineData);
7108 mHWData.backup();
7109 mHWData->mCPUAttached[aCpu] = false;
7110
7111 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7112 if (Global::IsOnline(mData->mMachineState))
7113 saveSettings(NULL);
7114
7115 return S_OK;
7116}
7117
7118STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7119{
7120 LogFlowThisFunc(("\n"));
7121
7122 CheckComArgNotNull(aCpuAttached);
7123
7124 *aCpuAttached = false;
7125
7126 AutoCaller autoCaller(this);
7127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7128
7129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7130
7131 /* If hotplug is enabled the CPU is always enabled. */
7132 if (!mHWData->mCPUHotPlugEnabled)
7133 {
7134 if (aCpu < mHWData->mCPUCount)
7135 *aCpuAttached = true;
7136 }
7137 else
7138 {
7139 if (aCpu < SchemaDefs::MaxCPUCount)
7140 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7141 }
7142
7143 return S_OK;
7144}
7145
7146STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7147{
7148 CheckComArgOutPointerValid(aName);
7149
7150 AutoCaller autoCaller(this);
7151 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7152
7153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7154
7155 Utf8Str log = queryLogFilename(aIdx);
7156 if (!RTFileExists(log.c_str()))
7157 log.setNull();
7158 log.cloneTo(aName);
7159
7160 return S_OK;
7161}
7162
7163STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7164{
7165 LogFlowThisFunc(("\n"));
7166 CheckComArgOutSafeArrayPointerValid(aData);
7167 if (aSize < 0)
7168 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7169
7170 AutoCaller autoCaller(this);
7171 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7172
7173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7174
7175 HRESULT rc = S_OK;
7176 Utf8Str log = queryLogFilename(aIdx);
7177
7178 /* do not unnecessarily hold the lock while doing something which does
7179 * not need the lock and potentially takes a long time. */
7180 alock.release();
7181
7182 /* Limit the chunk size to 32K for now, as that gives better performance
7183 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7184 * One byte expands to approx. 25 bytes of breathtaking XML. */
7185 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7186 com::SafeArray<BYTE> logData(cbData);
7187
7188 RTFILE LogFile;
7189 int vrc = RTFileOpen(&LogFile, log.c_str(),
7190 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7191 if (RT_SUCCESS(vrc))
7192 {
7193 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7194 if (RT_SUCCESS(vrc))
7195 logData.resize(cbData);
7196 else
7197 rc = setError(VBOX_E_IPRT_ERROR,
7198 tr("Could not read log file '%s' (%Rrc)"),
7199 log.c_str(), vrc);
7200 RTFileClose(LogFile);
7201 }
7202 else
7203 rc = setError(VBOX_E_IPRT_ERROR,
7204 tr("Could not open log file '%s' (%Rrc)"),
7205 log.c_str(), vrc);
7206
7207 if (FAILED(rc))
7208 logData.resize(0);
7209 logData.detachTo(ComSafeArrayOutArg(aData));
7210
7211 return rc;
7212}
7213
7214
7215/**
7216 * Currently this method doesn't attach device to the running VM,
7217 * just makes sure it's plugged on next VM start.
7218 */
7219STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7220{
7221 AutoCaller autoCaller(this);
7222 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7223
7224 // lock scope
7225 {
7226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7227
7228 HRESULT rc = checkStateDependency(MutableStateDep);
7229 if (FAILED(rc)) return rc;
7230
7231 ChipsetType_T aChipset = ChipsetType_PIIX3;
7232 COMGETTER(ChipsetType)(&aChipset);
7233
7234 if (aChipset != ChipsetType_ICH9)
7235 {
7236 return setError(E_INVALIDARG,
7237 tr("Host PCI attachment only supported with ICH9 chipset"));
7238 }
7239
7240 // check if device with this host PCI address already attached
7241 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7242 it != mHWData->mPCIDeviceAssignments.end();
7243 ++it)
7244 {
7245 LONG iHostAddress = -1;
7246 ComPtr<PCIDeviceAttachment> pAttach;
7247 pAttach = *it;
7248 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7249 if (iHostAddress == hostAddress)
7250 return setError(E_INVALIDARG,
7251 tr("Device with host PCI address already attached to this VM"));
7252 }
7253
7254 ComObjPtr<PCIDeviceAttachment> pda;
7255 char name[32];
7256
7257 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7258 Bstr bname(name);
7259 pda.createObject();
7260 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7261 setModified(IsModified_MachineData);
7262 mHWData.backup();
7263 mHWData->mPCIDeviceAssignments.push_back(pda);
7264 }
7265
7266 return S_OK;
7267}
7268
7269/**
7270 * Currently this method doesn't detach device from the running VM,
7271 * just makes sure it's not plugged on next VM start.
7272 */
7273STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7274{
7275 AutoCaller autoCaller(this);
7276 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7277
7278 ComObjPtr<PCIDeviceAttachment> pAttach;
7279 bool fRemoved = false;
7280 HRESULT rc;
7281
7282 // lock scope
7283 {
7284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7285
7286 rc = checkStateDependency(MutableStateDep);
7287 if (FAILED(rc)) return rc;
7288
7289 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7290 it != mHWData->mPCIDeviceAssignments.end();
7291 ++it)
7292 {
7293 LONG iHostAddress = -1;
7294 pAttach = *it;
7295 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7296 if (iHostAddress != -1 && iHostAddress == hostAddress)
7297 {
7298 setModified(IsModified_MachineData);
7299 mHWData.backup();
7300 mHWData->mPCIDeviceAssignments.remove(pAttach);
7301 fRemoved = true;
7302 break;
7303 }
7304 }
7305 }
7306
7307
7308 /* Fire event outside of the lock */
7309 if (fRemoved)
7310 {
7311 Assert(!pAttach.isNull());
7312 ComPtr<IEventSource> es;
7313 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7314 Assert(SUCCEEDED(rc));
7315 Bstr mid;
7316 rc = this->COMGETTER(Id)(mid.asOutParam());
7317 Assert(SUCCEEDED(rc));
7318 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7319 }
7320
7321 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7322 tr("No host PCI device %08x attached"),
7323 hostAddress
7324 );
7325}
7326
7327STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7328{
7329 CheckComArgOutSafeArrayPointerValid(aAssignments);
7330
7331 AutoCaller autoCaller(this);
7332 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7333
7334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7335
7336 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7337 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7338
7339 return S_OK;
7340}
7341
7342STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7343{
7344 CheckComArgOutPointerValid(aBandwidthControl);
7345
7346 AutoCaller autoCaller(this);
7347 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7348
7349 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7350
7351 return S_OK;
7352}
7353
7354STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7355{
7356 CheckComArgOutPointerValid(pfEnabled);
7357 AutoCaller autoCaller(this);
7358 HRESULT hrc = autoCaller.rc();
7359 if (SUCCEEDED(hrc))
7360 {
7361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7362 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7363 }
7364 return hrc;
7365}
7366
7367STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7368{
7369 AutoCaller autoCaller(this);
7370 HRESULT hrc = autoCaller.rc();
7371 if (SUCCEEDED(hrc))
7372 {
7373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7374 hrc = checkStateDependency(MutableStateDep);
7375 if (SUCCEEDED(hrc))
7376 {
7377 hrc = mHWData.backupEx();
7378 if (SUCCEEDED(hrc))
7379 {
7380 setModified(IsModified_MachineData);
7381 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7382 }
7383 }
7384 }
7385 return hrc;
7386}
7387
7388STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7389{
7390 CheckComArgOutPointerValid(pbstrConfig);
7391 AutoCaller autoCaller(this);
7392 HRESULT hrc = autoCaller.rc();
7393 if (SUCCEEDED(hrc))
7394 {
7395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7396 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7397 }
7398 return hrc;
7399}
7400
7401STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7402{
7403 CheckComArgStr(bstrConfig);
7404 AutoCaller autoCaller(this);
7405 HRESULT hrc = autoCaller.rc();
7406 if (SUCCEEDED(hrc))
7407 {
7408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7409 hrc = checkStateDependency(MutableStateDep);
7410 if (SUCCEEDED(hrc))
7411 {
7412 hrc = mHWData.backupEx();
7413 if (SUCCEEDED(hrc))
7414 {
7415 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7416 if (SUCCEEDED(hrc))
7417 setModified(IsModified_MachineData);
7418 }
7419 }
7420 }
7421 return hrc;
7422
7423}
7424
7425STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7426{
7427 CheckComArgOutPointerValid(pfAllow);
7428 AutoCaller autoCaller(this);
7429 HRESULT hrc = autoCaller.rc();
7430 if (SUCCEEDED(hrc))
7431 {
7432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7433 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7434 }
7435 return hrc;
7436}
7437
7438STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7439{
7440 AutoCaller autoCaller(this);
7441 HRESULT hrc = autoCaller.rc();
7442 if (SUCCEEDED(hrc))
7443 {
7444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7445 hrc = checkStateDependency(MutableStateDep);
7446 if (SUCCEEDED(hrc))
7447 {
7448 hrc = mHWData.backupEx();
7449 if (SUCCEEDED(hrc))
7450 {
7451 setModified(IsModified_MachineData);
7452 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7453 }
7454 }
7455 }
7456 return hrc;
7457}
7458
7459STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7460{
7461 CheckComArgOutPointerValid(pfEnabled);
7462 AutoCaller autoCaller(this);
7463 HRESULT hrc = autoCaller.rc();
7464 if (SUCCEEDED(hrc))
7465 {
7466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7467 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7468 }
7469 return hrc;
7470}
7471
7472STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7473{
7474 AutoCaller autoCaller(this);
7475 HRESULT hrc = autoCaller.rc();
7476 if (SUCCEEDED(hrc))
7477 {
7478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7479 hrc = checkStateDependency(MutableStateDep);
7480 if ( SUCCEEDED(hrc)
7481 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7482 {
7483 AutostartDb *autostartDb = mParent->getAutostartDb();
7484 int vrc;
7485
7486 if (fEnabled)
7487 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7488 else
7489 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7490
7491 if (RT_SUCCESS(vrc))
7492 {
7493 hrc = mHWData.backupEx();
7494 if (SUCCEEDED(hrc))
7495 {
7496 setModified(IsModified_MachineData);
7497 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7498 }
7499 }
7500 else if (vrc == VERR_NOT_SUPPORTED)
7501 hrc = setError(VBOX_E_NOT_SUPPORTED,
7502 tr("The VM autostart feature is not supported on this platform"));
7503 else if (vrc == VERR_PATH_NOT_FOUND)
7504 hrc = setError(E_FAIL,
7505 tr("The path to the autostart database is not set"));
7506 else
7507 hrc = setError(E_UNEXPECTED,
7508 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7509 fEnabled ? "Adding" : "Removing",
7510 mUserData->s.strName.c_str(), vrc);
7511 }
7512 }
7513 return hrc;
7514}
7515
7516STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7517{
7518 CheckComArgOutPointerValid(puDelay);
7519 AutoCaller autoCaller(this);
7520 HRESULT hrc = autoCaller.rc();
7521 if (SUCCEEDED(hrc))
7522 {
7523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7524 *puDelay = mHWData->mAutostart.uAutostartDelay;
7525 }
7526 return hrc;
7527}
7528
7529STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7530{
7531 AutoCaller autoCaller(this);
7532 HRESULT hrc = autoCaller.rc();
7533 if (SUCCEEDED(hrc))
7534 {
7535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7536 hrc = checkStateDependency(MutableStateDep);
7537 if (SUCCEEDED(hrc))
7538 {
7539 hrc = mHWData.backupEx();
7540 if (SUCCEEDED(hrc))
7541 {
7542 setModified(IsModified_MachineData);
7543 mHWData->mAutostart.uAutostartDelay = uDelay;
7544 }
7545 }
7546 }
7547 return hrc;
7548}
7549
7550STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7551{
7552 CheckComArgOutPointerValid(penmAutostopType);
7553 AutoCaller autoCaller(this);
7554 HRESULT hrc = autoCaller.rc();
7555 if (SUCCEEDED(hrc))
7556 {
7557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7558 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7559 }
7560 return hrc;
7561}
7562
7563STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7564{
7565 AutoCaller autoCaller(this);
7566 HRESULT hrc = autoCaller.rc();
7567 if (SUCCEEDED(hrc))
7568 {
7569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7570 hrc = checkStateDependency(MutableStateDep);
7571 if ( SUCCEEDED(hrc)
7572 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7573 {
7574 AutostartDb *autostartDb = mParent->getAutostartDb();
7575 int vrc;
7576
7577 if (enmAutostopType != AutostopType_Disabled)
7578 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7579 else
7580 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7581
7582 if (RT_SUCCESS(vrc))
7583 {
7584 hrc = mHWData.backupEx();
7585 if (SUCCEEDED(hrc))
7586 {
7587 setModified(IsModified_MachineData);
7588 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7589 }
7590 }
7591 else if (vrc == VERR_NOT_SUPPORTED)
7592 hrc = setError(VBOX_E_NOT_SUPPORTED,
7593 tr("The VM autostop feature is not supported on this platform"));
7594 else if (vrc == VERR_PATH_NOT_FOUND)
7595 hrc = setError(E_FAIL,
7596 tr("The path to the autostart database is not set"));
7597 else
7598 hrc = setError(E_UNEXPECTED,
7599 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7600 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7601 mUserData->s.strName.c_str(), vrc);
7602 }
7603 }
7604 return hrc;
7605}
7606
7607STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7608{
7609 CheckComArgOutPointerValid(aDefaultFrontend);
7610 AutoCaller autoCaller(this);
7611 HRESULT hrc = autoCaller.rc();
7612 if (SUCCEEDED(hrc))
7613 {
7614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7615 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7616 }
7617 return hrc;
7618}
7619
7620STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7621{
7622 CheckComArgStr(aDefaultFrontend);
7623 AutoCaller autoCaller(this);
7624 HRESULT hrc = autoCaller.rc();
7625 if (SUCCEEDED(hrc))
7626 {
7627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7628 hrc = checkStateDependency(MutableOrSavedStateDep);
7629 if (SUCCEEDED(hrc))
7630 {
7631 hrc = mHWData.backupEx();
7632 if (SUCCEEDED(hrc))
7633 {
7634 setModified(IsModified_MachineData);
7635 mHWData->mDefaultFrontend = aDefaultFrontend;
7636 }
7637 }
7638 }
7639 return hrc;
7640}
7641
7642STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7643{
7644 CheckComArgSafeArrayNotNull(aIcon);
7645 CheckComArgOutSafeArrayPointerValid(aIcon);
7646 AutoCaller autoCaller(this);
7647 HRESULT hrc = autoCaller.rc();
7648 if (SUCCEEDED(hrc))
7649 {
7650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7651 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7652 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7653 icon.detachTo(ComSafeArrayOutArg(aIcon));
7654 }
7655 return hrc;
7656}
7657
7658STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7659{
7660 CheckComArgSafeArrayNotNull(aIcon);
7661 AutoCaller autoCaller(this);
7662 HRESULT hrc = autoCaller.rc();
7663 if (SUCCEEDED(hrc))
7664 {
7665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7666 hrc = checkStateDependency(MutableOrSavedStateDep);
7667 if (SUCCEEDED(hrc))
7668 {
7669 setModified(IsModified_MachineData);
7670 mUserData.backup();
7671 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7672 mUserData->mIcon.resize(icon.size());
7673 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7674 }
7675 }
7676 return hrc;
7677}
7678
7679STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7680{
7681 CheckComArgOutPointerValid(aAvailable);
7682
7683 AutoCaller autoCaller(this);
7684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7685
7686#ifdef VBOX_WITH_USB
7687 *aAvailable = true;
7688#else
7689 *aAvailable = false;
7690#endif
7691 return S_OK;
7692}
7693
7694STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7695{
7696 LogFlowFuncEnter();
7697
7698 CheckComArgNotNull(pTarget);
7699 CheckComArgOutPointerValid(pProgress);
7700
7701 /* Convert the options. */
7702 RTCList<CloneOptions_T> optList;
7703 if (options != NULL)
7704 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7705
7706 if (optList.contains(CloneOptions_Link))
7707 {
7708 if (!isSnapshotMachine())
7709 return setError(E_INVALIDARG,
7710 tr("Linked clone can only be created from a snapshot"));
7711 if (mode != CloneMode_MachineState)
7712 return setError(E_INVALIDARG,
7713 tr("Linked clone can only be created for a single machine state"));
7714 }
7715 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7716
7717 AutoCaller autoCaller(this);
7718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7719
7720
7721 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7722
7723 HRESULT rc = pWorker->start(pProgress);
7724
7725 LogFlowFuncLeave();
7726
7727 return rc;
7728}
7729
7730// public methods for internal purposes
7731/////////////////////////////////////////////////////////////////////////////
7732
7733/**
7734 * Adds the given IsModified_* flag to the dirty flags of the machine.
7735 * This must be called either during loadSettings or under the machine write lock.
7736 * @param fl
7737 */
7738void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7739{
7740 mData->flModifications |= fl;
7741 if (fAllowStateModification && isStateModificationAllowed())
7742 mData->mCurrentStateModified = true;
7743}
7744
7745/**
7746 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7747 * care of the write locking.
7748 *
7749 * @param fModifications The flag to add.
7750 */
7751void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7752{
7753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7754 setModified(fModification, fAllowStateModification);
7755}
7756
7757/**
7758 * Saves the registry entry of this machine to the given configuration node.
7759 *
7760 * @param aEntryNode Node to save the registry entry to.
7761 *
7762 * @note locks this object for reading.
7763 */
7764HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7765{
7766 AutoLimitedCaller autoCaller(this);
7767 AssertComRCReturnRC(autoCaller.rc());
7768
7769 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7770
7771 data.uuid = mData->mUuid;
7772 data.strSettingsFile = mData->m_strConfigFile;
7773
7774 return S_OK;
7775}
7776
7777/**
7778 * Calculates the absolute path of the given path taking the directory of the
7779 * machine settings file as the current directory.
7780 *
7781 * @param aPath Path to calculate the absolute path for.
7782 * @param aResult Where to put the result (used only on success, can be the
7783 * same Utf8Str instance as passed in @a aPath).
7784 * @return IPRT result.
7785 *
7786 * @note Locks this object for reading.
7787 */
7788int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7789{
7790 AutoCaller autoCaller(this);
7791 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7792
7793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7794
7795 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7796
7797 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7798
7799 strSettingsDir.stripFilename();
7800 char folder[RTPATH_MAX];
7801 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7802 if (RT_SUCCESS(vrc))
7803 aResult = folder;
7804
7805 return vrc;
7806}
7807
7808/**
7809 * Copies strSource to strTarget, making it relative to the machine folder
7810 * if it is a subdirectory thereof, or simply copying it otherwise.
7811 *
7812 * @param strSource Path to evaluate and copy.
7813 * @param strTarget Buffer to receive target path.
7814 *
7815 * @note Locks this object for reading.
7816 */
7817void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7818 Utf8Str &strTarget)
7819{
7820 AutoCaller autoCaller(this);
7821 AssertComRCReturn(autoCaller.rc(), (void)0);
7822
7823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7824
7825 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7826 // use strTarget as a temporary buffer to hold the machine settings dir
7827 strTarget = mData->m_strConfigFileFull;
7828 strTarget.stripFilename();
7829 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7830 {
7831 // is relative: then append what's left
7832 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7833 // for empty paths (only possible for subdirs) use "." to avoid
7834 // triggering default settings for not present config attributes.
7835 if (strTarget.isEmpty())
7836 strTarget = ".";
7837 }
7838 else
7839 // is not relative: then overwrite
7840 strTarget = strSource;
7841}
7842
7843/**
7844 * Returns the full path to the machine's log folder in the
7845 * \a aLogFolder argument.
7846 */
7847void Machine::getLogFolder(Utf8Str &aLogFolder)
7848{
7849 AutoCaller autoCaller(this);
7850 AssertComRCReturnVoid(autoCaller.rc());
7851
7852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7853
7854 char szTmp[RTPATH_MAX];
7855 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7856 if (RT_SUCCESS(vrc))
7857 {
7858 if (szTmp[0] && !mUserData.isNull())
7859 {
7860 char szTmp2[RTPATH_MAX];
7861 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7862 if (RT_SUCCESS(vrc))
7863 aLogFolder = BstrFmt("%s%c%s",
7864 szTmp2,
7865 RTPATH_DELIMITER,
7866 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7867 }
7868 else
7869 vrc = VERR_PATH_IS_RELATIVE;
7870 }
7871
7872 if (RT_FAILURE(vrc))
7873 {
7874 // fallback if VBOX_USER_LOGHOME is not set or invalid
7875 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7876 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7877 aLogFolder.append(RTPATH_DELIMITER);
7878 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7879 }
7880}
7881
7882/**
7883 * Returns the full path to the machine's log file for an given index.
7884 */
7885Utf8Str Machine::queryLogFilename(ULONG idx)
7886{
7887 Utf8Str logFolder;
7888 getLogFolder(logFolder);
7889 Assert(logFolder.length());
7890 Utf8Str log;
7891 if (idx == 0)
7892 log = Utf8StrFmt("%s%cVBox.log",
7893 logFolder.c_str(), RTPATH_DELIMITER);
7894 else
7895 log = Utf8StrFmt("%s%cVBox.log.%d",
7896 logFolder.c_str(), RTPATH_DELIMITER, idx);
7897 return log;
7898}
7899
7900/**
7901 * Composes a unique saved state filename based on the current system time. The filename is
7902 * granular to the second so this will work so long as no more than one snapshot is taken on
7903 * a machine per second.
7904 *
7905 * Before version 4.1, we used this formula for saved state files:
7906 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7907 * which no longer works because saved state files can now be shared between the saved state of the
7908 * "saved" machine and an online snapshot, and the following would cause problems:
7909 * 1) save machine
7910 * 2) create online snapshot from that machine state --> reusing saved state file
7911 * 3) save machine again --> filename would be reused, breaking the online snapshot
7912 *
7913 * So instead we now use a timestamp.
7914 *
7915 * @param str
7916 */
7917void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7918{
7919 AutoCaller autoCaller(this);
7920 AssertComRCReturnVoid(autoCaller.rc());
7921
7922 {
7923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7924 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7925 }
7926
7927 RTTIMESPEC ts;
7928 RTTimeNow(&ts);
7929 RTTIME time;
7930 RTTimeExplode(&time, &ts);
7931
7932 strStateFilePath += RTPATH_DELIMITER;
7933 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7934 time.i32Year, time.u8Month, time.u8MonthDay,
7935 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7936}
7937
7938/**
7939 * Returns the full path to the default video capture file.
7940 */
7941void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7942{
7943 AutoCaller autoCaller(this);
7944 AssertComRCReturnVoid(autoCaller.rc());
7945
7946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7947
7948 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7949 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7950 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7951}
7952
7953/**
7954 * Returns whether at least one USB controller is present for the VM.
7955 */
7956bool Machine::isUSBControllerPresent()
7957{
7958 AutoCaller autoCaller(this);
7959 AssertComRCReturn(autoCaller.rc(), false);
7960
7961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7962
7963 return (mUSBControllers->size() > 0);
7964}
7965
7966/**
7967 * @note Locks this object for writing, calls the client process
7968 * (inside the lock).
7969 */
7970HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7971 const Utf8Str &strFrontend,
7972 const Utf8Str &strEnvironment,
7973 ProgressProxy *aProgress)
7974{
7975 LogFlowThisFuncEnter();
7976
7977 AssertReturn(aControl, E_FAIL);
7978 AssertReturn(aProgress, E_FAIL);
7979 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7980
7981 AutoCaller autoCaller(this);
7982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7983
7984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7985
7986 if (!mData->mRegistered)
7987 return setError(E_UNEXPECTED,
7988 tr("The machine '%s' is not registered"),
7989 mUserData->s.strName.c_str());
7990
7991 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7992
7993 if ( mData->mSession.mState == SessionState_Locked
7994 || mData->mSession.mState == SessionState_Spawning
7995 || mData->mSession.mState == SessionState_Unlocking)
7996 return setError(VBOX_E_INVALID_OBJECT_STATE,
7997 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7998 mUserData->s.strName.c_str());
7999
8000 /* may not be busy */
8001 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
8002
8003 /* get the path to the executable */
8004 char szPath[RTPATH_MAX];
8005 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
8006 size_t sz = strlen(szPath);
8007 szPath[sz++] = RTPATH_DELIMITER;
8008 szPath[sz] = 0;
8009 char *cmd = szPath + sz;
8010 sz = sizeof(szPath) - sz;
8011
8012 int vrc = VINF_SUCCESS;
8013 RTPROCESS pid = NIL_RTPROCESS;
8014
8015 RTENV env = RTENV_DEFAULT;
8016
8017 if (!strEnvironment.isEmpty())
8018 {
8019 char *newEnvStr = NULL;
8020
8021 do
8022 {
8023 /* clone the current environment */
8024 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
8025 AssertRCBreakStmt(vrc2, vrc = vrc2);
8026
8027 newEnvStr = RTStrDup(strEnvironment.c_str());
8028 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
8029
8030 /* put new variables to the environment
8031 * (ignore empty variable names here since RTEnv API
8032 * intentionally doesn't do that) */
8033 char *var = newEnvStr;
8034 for (char *p = newEnvStr; *p; ++p)
8035 {
8036 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
8037 {
8038 *p = '\0';
8039 if (*var)
8040 {
8041 char *val = strchr(var, '=');
8042 if (val)
8043 {
8044 *val++ = '\0';
8045 vrc2 = RTEnvSetEx(env, var, val);
8046 }
8047 else
8048 vrc2 = RTEnvUnsetEx(env, var);
8049 if (RT_FAILURE(vrc2))
8050 break;
8051 }
8052 var = p + 1;
8053 }
8054 }
8055 if (RT_SUCCESS(vrc2) && *var)
8056 vrc2 = RTEnvPutEx(env, var);
8057
8058 AssertRCBreakStmt(vrc2, vrc = vrc2);
8059 }
8060 while (0);
8061
8062 if (newEnvStr != NULL)
8063 RTStrFree(newEnvStr);
8064 }
8065
8066#ifdef VBOX_WITH_QTGUI
8067 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
8068 {
8069# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
8070 /* Modify the base path so that we don't need to use ".." below. */
8071 RTPathStripTrailingSlash(szPath);
8072 RTPathStripFilename(szPath);
8073 sz = strlen(szPath);
8074 cmd = szPath + sz;
8075 sz = sizeof(szPath) - sz;
8076
8077#define OSX_APP_NAME "VirtualBoxVM"
8078#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
8079
8080 Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
8081 if ( strAppOverride.contains(".")
8082 || strAppOverride.contains("/")
8083 || strAppOverride.contains("\\")
8084 || strAppOverride.contains(":"))
8085 strAppOverride.setNull();
8086 Utf8Str strAppPath;
8087 if (!strAppOverride.isEmpty())
8088 {
8089 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
8090 Utf8Str strFullPath(szPath);
8091 strFullPath.append(strAppPath);
8092 /* there is a race, but people using this deserve the failure */
8093 if (!RTFileExists(strFullPath.c_str()))
8094 strAppOverride.setNull();
8095 }
8096 if (strAppOverride.isEmpty())
8097 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
8098 const char *VirtualBox_exe = strAppPath.c_str();
8099 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
8100# else
8101 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8102 Assert(sz >= sizeof(VirtualBox_exe));
8103# endif
8104 strcpy(cmd, VirtualBox_exe);
8105
8106 Utf8Str idStr = mData->mUuid.toString();
8107 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8108 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8109 }
8110#else /* !VBOX_WITH_QTGUI */
8111 if (0)
8112 ;
8113#endif /* VBOX_WITH_QTGUI */
8114
8115 else
8116
8117#ifdef VBOX_WITH_VBOXSDL
8118 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8119 {
8120 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8121 Assert(sz >= sizeof(VBoxSDL_exe));
8122 strcpy(cmd, VBoxSDL_exe);
8123
8124 Utf8Str idStr = mData->mUuid.toString();
8125 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8126 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8127 }
8128#else /* !VBOX_WITH_VBOXSDL */
8129 if (0)
8130 ;
8131#endif /* !VBOX_WITH_VBOXSDL */
8132
8133 else
8134
8135#ifdef VBOX_WITH_HEADLESS
8136 if ( strFrontend == "headless"
8137 || strFrontend == "capture"
8138 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8139 )
8140 {
8141 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8142 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8143 * and a VM works even if the server has not been installed.
8144 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8145 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8146 * differently in 4.0 and 3.x.
8147 */
8148 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8149 Assert(sz >= sizeof(VBoxHeadless_exe));
8150 strcpy(cmd, VBoxHeadless_exe);
8151
8152 Utf8Str idStr = mData->mUuid.toString();
8153 /* Leave space for "--capture" arg. */
8154 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8155 "--startvm", idStr.c_str(),
8156 "--vrde", "config",
8157 0, /* For "--capture". */
8158 0 };
8159 if (strFrontend == "capture")
8160 {
8161 unsigned pos = RT_ELEMENTS(args) - 2;
8162 args[pos] = "--capture";
8163 }
8164 vrc = RTProcCreate(szPath, args, env,
8165#ifdef RT_OS_WINDOWS
8166 RTPROC_FLAGS_NO_WINDOW
8167#else
8168 0
8169#endif
8170 , &pid);
8171 }
8172#else /* !VBOX_WITH_HEADLESS */
8173 if (0)
8174 ;
8175#endif /* !VBOX_WITH_HEADLESS */
8176 else
8177 {
8178 RTEnvDestroy(env);
8179 return setError(E_INVALIDARG,
8180 tr("Invalid frontend name: '%s'"),
8181 strFrontend.c_str());
8182 }
8183
8184 RTEnvDestroy(env);
8185
8186 if (RT_FAILURE(vrc))
8187 return setError(VBOX_E_IPRT_ERROR,
8188 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8189 mUserData->s.strName.c_str(), vrc);
8190
8191 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8192
8193 /*
8194 * Note that we don't release the lock here before calling the client,
8195 * because it doesn't need to call us back if called with a NULL argument.
8196 * Releasing the lock here is dangerous because we didn't prepare the
8197 * launch data yet, but the client we've just started may happen to be
8198 * too fast and call LockMachine() that will fail (because of PID, etc.),
8199 * so that the Machine will never get out of the Spawning session state.
8200 */
8201
8202 /* inform the session that it will be a remote one */
8203 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8204#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8205 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8206#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8207 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8208#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8209 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8210
8211 if (FAILED(rc))
8212 {
8213 /* restore the session state */
8214 mData->mSession.mState = SessionState_Unlocked;
8215 /* The failure may occur w/o any error info (from RPC), so provide one */
8216 return setError(VBOX_E_VM_ERROR,
8217 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8218 }
8219
8220 /* attach launch data to the machine */
8221 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8222 mData->mSession.mRemoteControls.push_back(aControl);
8223 mData->mSession.mProgress = aProgress;
8224 mData->mSession.mPID = pid;
8225 mData->mSession.mState = SessionState_Spawning;
8226 mData->mSession.mType = strFrontend;
8227
8228 LogFlowThisFuncLeave();
8229 return S_OK;
8230}
8231
8232/**
8233 * Returns @c true if the given session machine instance has an open direct
8234 * session (and optionally also for direct sessions which are closing) and
8235 * returns the session control machine instance if so.
8236 *
8237 * Note that when the method returns @c false, the arguments remain unchanged.
8238 *
8239 * @param aMachine Session machine object.
8240 * @param aControl Direct session control object (optional).
8241 * @param aAllowClosing If true then additionally a session which is currently
8242 * being closed will also be allowed.
8243 *
8244 * @note locks this object for reading.
8245 */
8246bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8247 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8248 bool aAllowClosing /*= false*/)
8249{
8250 AutoLimitedCaller autoCaller(this);
8251 AssertComRCReturn(autoCaller.rc(), false);
8252
8253 /* just return false for inaccessible machines */
8254 if (autoCaller.state() != Ready)
8255 return false;
8256
8257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8258
8259 if ( mData->mSession.mState == SessionState_Locked
8260 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8261 )
8262 {
8263 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8264
8265 aMachine = mData->mSession.mMachine;
8266
8267 if (aControl != NULL)
8268 *aControl = mData->mSession.mDirectControl;
8269
8270 return true;
8271 }
8272
8273 return false;
8274}
8275
8276/**
8277 * Returns @c true if the given machine has an spawning direct session.
8278 *
8279 * @note locks this object for reading.
8280 */
8281bool Machine::isSessionSpawning()
8282{
8283 AutoLimitedCaller autoCaller(this);
8284 AssertComRCReturn(autoCaller.rc(), false);
8285
8286 /* just return false for inaccessible machines */
8287 if (autoCaller.state() != Ready)
8288 return false;
8289
8290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8291
8292 if (mData->mSession.mState == SessionState_Spawning)
8293 return true;
8294
8295 return false;
8296}
8297
8298#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8299/**
8300 * Called from the client watcher thread to check for unexpected client process
8301 * death during Session_Spawning state (e.g. before it successfully opened a
8302 * direct session).
8303 *
8304 * On Win32 and on OS/2, this method is called only when we've got the
8305 * direct client's process termination notification, so it always returns @c
8306 * true.
8307 *
8308 * On other platforms, this method returns @c true if the client process is
8309 * terminated and @c false if it's still alive.
8310 *
8311 * @note Locks this object for writing.
8312 */
8313bool Machine::checkForSpawnFailure()
8314{
8315 AutoCaller autoCaller(this);
8316 if (!autoCaller.isOk())
8317 {
8318 /* nothing to do */
8319 LogFlowThisFunc(("Already uninitialized!\n"));
8320 return true;
8321 }
8322
8323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8324
8325 if (mData->mSession.mState != SessionState_Spawning)
8326 {
8327 /* nothing to do */
8328 LogFlowThisFunc(("Not spawning any more!\n"));
8329 return true;
8330 }
8331
8332 HRESULT rc = S_OK;
8333
8334 /* PID not yet initialized, skip check. */
8335 if (mData->mSession.mPID == NIL_RTPROCESS)
8336 return false;
8337
8338 RTPROCSTATUS status;
8339 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8340
8341 if (vrc != VERR_PROCESS_RUNNING)
8342 {
8343 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8344 rc = setError(E_FAIL,
8345 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8346 getName().c_str(), status.iStatus);
8347 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8348 rc = setError(E_FAIL,
8349 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8350 getName().c_str(), status.iStatus);
8351 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8352 rc = setError(E_FAIL,
8353 tr("The virtual machine '%s' has terminated abnormally"),
8354 getName().c_str(), status.iStatus);
8355 else
8356 rc = setError(E_FAIL,
8357 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8358 getName().c_str(), vrc);
8359 }
8360
8361 if (FAILED(rc))
8362 {
8363 /* Close the remote session, remove the remote control from the list
8364 * and reset session state to Closed (@note keep the code in sync with
8365 * the relevant part in LockMachine()). */
8366
8367 Assert(mData->mSession.mRemoteControls.size() == 1);
8368 if (mData->mSession.mRemoteControls.size() == 1)
8369 {
8370 ErrorInfoKeeper eik;
8371 mData->mSession.mRemoteControls.front()->Uninitialize();
8372 }
8373
8374 mData->mSession.mRemoteControls.clear();
8375 mData->mSession.mState = SessionState_Unlocked;
8376
8377 /* finalize the progress after setting the state */
8378 if (!mData->mSession.mProgress.isNull())
8379 {
8380 mData->mSession.mProgress->notifyComplete(rc);
8381 mData->mSession.mProgress.setNull();
8382 }
8383
8384 mData->mSession.mPID = NIL_RTPROCESS;
8385
8386 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8387 return true;
8388 }
8389
8390 return false;
8391}
8392#endif /* !VBOX_WITH_GENERIC_SESSION_WATCHER */
8393
8394/**
8395 * Checks whether the machine can be registered. If so, commits and saves
8396 * all settings.
8397 *
8398 * @note Must be called from mParent's write lock. Locks this object and
8399 * children for writing.
8400 */
8401HRESULT Machine::prepareRegister()
8402{
8403 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8404
8405 AutoLimitedCaller autoCaller(this);
8406 AssertComRCReturnRC(autoCaller.rc());
8407
8408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8409
8410 /* wait for state dependents to drop to zero */
8411 ensureNoStateDependencies();
8412
8413 if (!mData->mAccessible)
8414 return setError(VBOX_E_INVALID_OBJECT_STATE,
8415 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8416 mUserData->s.strName.c_str(),
8417 mData->mUuid.toString().c_str());
8418
8419 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8420
8421 if (mData->mRegistered)
8422 return setError(VBOX_E_INVALID_OBJECT_STATE,
8423 tr("The machine '%s' with UUID {%s} is already registered"),
8424 mUserData->s.strName.c_str(),
8425 mData->mUuid.toString().c_str());
8426
8427 HRESULT rc = S_OK;
8428
8429 // Ensure the settings are saved. If we are going to be registered and
8430 // no config file exists yet, create it by calling saveSettings() too.
8431 if ( (mData->flModifications)
8432 || (!mData->pMachineConfigFile->fileExists())
8433 )
8434 {
8435 rc = saveSettings(NULL);
8436 // no need to check whether VirtualBox.xml needs saving too since
8437 // we can't have a machine XML file rename pending
8438 if (FAILED(rc)) return rc;
8439 }
8440
8441 /* more config checking goes here */
8442
8443 if (SUCCEEDED(rc))
8444 {
8445 /* we may have had implicit modifications we want to fix on success */
8446 commit();
8447
8448 mData->mRegistered = true;
8449 }
8450 else
8451 {
8452 /* we may have had implicit modifications we want to cancel on failure*/
8453 rollback(false /* aNotify */);
8454 }
8455
8456 return rc;
8457}
8458
8459/**
8460 * Increases the number of objects dependent on the machine state or on the
8461 * registered state. Guarantees that these two states will not change at least
8462 * until #releaseStateDependency() is called.
8463 *
8464 * Depending on the @a aDepType value, additional state checks may be made.
8465 * These checks will set extended error info on failure. See
8466 * #checkStateDependency() for more info.
8467 *
8468 * If this method returns a failure, the dependency is not added and the caller
8469 * is not allowed to rely on any particular machine state or registration state
8470 * value and may return the failed result code to the upper level.
8471 *
8472 * @param aDepType Dependency type to add.
8473 * @param aState Current machine state (NULL if not interested).
8474 * @param aRegistered Current registered state (NULL if not interested).
8475 *
8476 * @note Locks this object for writing.
8477 */
8478HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8479 MachineState_T *aState /* = NULL */,
8480 BOOL *aRegistered /* = NULL */)
8481{
8482 AutoCaller autoCaller(this);
8483 AssertComRCReturnRC(autoCaller.rc());
8484
8485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8486
8487 HRESULT rc = checkStateDependency(aDepType);
8488 if (FAILED(rc)) return rc;
8489
8490 {
8491 if (mData->mMachineStateChangePending != 0)
8492 {
8493 /* ensureNoStateDependencies() is waiting for state dependencies to
8494 * drop to zero so don't add more. It may make sense to wait a bit
8495 * and retry before reporting an error (since the pending state
8496 * transition should be really quick) but let's just assert for
8497 * now to see if it ever happens on practice. */
8498
8499 AssertFailed();
8500
8501 return setError(E_ACCESSDENIED,
8502 tr("Machine state change is in progress. Please retry the operation later."));
8503 }
8504
8505 ++mData->mMachineStateDeps;
8506 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8507 }
8508
8509 if (aState)
8510 *aState = mData->mMachineState;
8511 if (aRegistered)
8512 *aRegistered = mData->mRegistered;
8513
8514 return S_OK;
8515}
8516
8517/**
8518 * Decreases the number of objects dependent on the machine state.
8519 * Must always complete the #addStateDependency() call after the state
8520 * dependency is no more necessary.
8521 */
8522void Machine::releaseStateDependency()
8523{
8524 AutoCaller autoCaller(this);
8525 AssertComRCReturnVoid(autoCaller.rc());
8526
8527 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8528
8529 /* releaseStateDependency() w/o addStateDependency()? */
8530 AssertReturnVoid(mData->mMachineStateDeps != 0);
8531 -- mData->mMachineStateDeps;
8532
8533 if (mData->mMachineStateDeps == 0)
8534 {
8535 /* inform ensureNoStateDependencies() that there are no more deps */
8536 if (mData->mMachineStateChangePending != 0)
8537 {
8538 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8539 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8540 }
8541 }
8542}
8543
8544Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8545{
8546 /* start with nothing found */
8547 Utf8Str strResult("");
8548
8549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8550
8551 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8552 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8553 // found:
8554 strResult = it->second; // source is a Utf8Str
8555
8556 return strResult;
8557}
8558
8559// protected methods
8560/////////////////////////////////////////////////////////////////////////////
8561
8562/**
8563 * Performs machine state checks based on the @a aDepType value. If a check
8564 * fails, this method will set extended error info, otherwise it will return
8565 * S_OK. It is supposed, that on failure, the caller will immediately return
8566 * the return value of this method to the upper level.
8567 *
8568 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8569 *
8570 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8571 * current state of this machine object allows to change settings of the
8572 * machine (i.e. the machine is not registered, or registered but not running
8573 * and not saved). It is useful to call this method from Machine setters
8574 * before performing any change.
8575 *
8576 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8577 * as for MutableStateDep except that if the machine is saved, S_OK is also
8578 * returned. This is useful in setters which allow changing machine
8579 * properties when it is in the saved state.
8580 *
8581 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8582 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8583 * Aborted).
8584 *
8585 * @param aDepType Dependency type to check.
8586 *
8587 * @note Non Machine based classes should use #addStateDependency() and
8588 * #releaseStateDependency() methods or the smart AutoStateDependency
8589 * template.
8590 *
8591 * @note This method must be called from under this object's read or write
8592 * lock.
8593 */
8594HRESULT Machine::checkStateDependency(StateDependency aDepType)
8595{
8596 switch (aDepType)
8597 {
8598 case AnyStateDep:
8599 {
8600 break;
8601 }
8602 case MutableStateDep:
8603 {
8604 if ( mData->mRegistered
8605 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8606 || ( mData->mMachineState != MachineState_Paused
8607 && mData->mMachineState != MachineState_Running
8608 && mData->mMachineState != MachineState_Aborted
8609 && mData->mMachineState != MachineState_Teleported
8610 && mData->mMachineState != MachineState_PoweredOff
8611 )
8612 )
8613 )
8614 return setError(VBOX_E_INVALID_VM_STATE,
8615 tr("The machine is not mutable (state is %s)"),
8616 Global::stringifyMachineState(mData->mMachineState));
8617 break;
8618 }
8619 case MutableOrSavedStateDep:
8620 {
8621 if ( mData->mRegistered
8622 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8623 || ( mData->mMachineState != MachineState_Paused
8624 && mData->mMachineState != MachineState_Running
8625 && mData->mMachineState != MachineState_Aborted
8626 && mData->mMachineState != MachineState_Teleported
8627 && mData->mMachineState != MachineState_Saved
8628 && mData->mMachineState != MachineState_PoweredOff
8629 )
8630 )
8631 )
8632 return setError(VBOX_E_INVALID_VM_STATE,
8633 tr("The machine is not mutable (state is %s)"),
8634 Global::stringifyMachineState(mData->mMachineState));
8635 break;
8636 }
8637 case OfflineStateDep:
8638 {
8639 if ( mData->mRegistered
8640 && ( !isSessionMachine()
8641 || ( mData->mMachineState != MachineState_PoweredOff
8642 && mData->mMachineState != MachineState_Saved
8643 && mData->mMachineState != MachineState_Aborted
8644 && mData->mMachineState != MachineState_Teleported
8645 )
8646 )
8647 )
8648 return setError(VBOX_E_INVALID_VM_STATE,
8649 tr("The machine is not offline (state is %s)"),
8650 Global::stringifyMachineState(mData->mMachineState));
8651 break;
8652 }
8653 }
8654
8655 return S_OK;
8656}
8657
8658/**
8659 * Helper to initialize all associated child objects and allocate data
8660 * structures.
8661 *
8662 * This method must be called as a part of the object's initialization procedure
8663 * (usually done in the #init() method).
8664 *
8665 * @note Must be called only from #init() or from #registeredInit().
8666 */
8667HRESULT Machine::initDataAndChildObjects()
8668{
8669 AutoCaller autoCaller(this);
8670 AssertComRCReturnRC(autoCaller.rc());
8671 AssertComRCReturn(autoCaller.state() == InInit ||
8672 autoCaller.state() == Limited, E_FAIL);
8673
8674 AssertReturn(!mData->mAccessible, E_FAIL);
8675
8676 /* allocate data structures */
8677 mSSData.allocate();
8678 mUserData.allocate();
8679 mHWData.allocate();
8680 mMediaData.allocate();
8681 mStorageControllers.allocate();
8682 mUSBControllers.allocate();
8683
8684 /* initialize mOSTypeId */
8685 mUserData->s.strOsType = mParent->getUnknownOSType()->i_id();
8686
8687 /* create associated BIOS settings object */
8688 unconst(mBIOSSettings).createObject();
8689 mBIOSSettings->init(this);
8690
8691 /* create an associated VRDE object (default is disabled) */
8692 unconst(mVRDEServer).createObject();
8693 mVRDEServer->init(this);
8694
8695 /* create associated serial port objects */
8696 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8697 {
8698 unconst(mSerialPorts[slot]).createObject();
8699 mSerialPorts[slot]->init(this, slot);
8700 }
8701
8702 /* create associated parallel port objects */
8703 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8704 {
8705 unconst(mParallelPorts[slot]).createObject();
8706 mParallelPorts[slot]->init(this, slot);
8707 }
8708
8709 /* create the audio adapter object (always present, default is disabled) */
8710 unconst(mAudioAdapter).createObject();
8711 mAudioAdapter->init(this);
8712
8713 /* create the USB device filters object (always present) */
8714 unconst(mUSBDeviceFilters).createObject();
8715 mUSBDeviceFilters->init(this);
8716
8717 /* create associated network adapter objects */
8718 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8719 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8720 {
8721 unconst(mNetworkAdapters[slot]).createObject();
8722 mNetworkAdapters[slot]->init(this, slot);
8723 }
8724
8725 /* create the bandwidth control */
8726 unconst(mBandwidthControl).createObject();
8727 mBandwidthControl->init(this);
8728
8729 return S_OK;
8730}
8731
8732/**
8733 * Helper to uninitialize all associated child objects and to free all data
8734 * structures.
8735 *
8736 * This method must be called as a part of the object's uninitialization
8737 * procedure (usually done in the #uninit() method).
8738 *
8739 * @note Must be called only from #uninit() or from #registeredInit().
8740 */
8741void Machine::uninitDataAndChildObjects()
8742{
8743 AutoCaller autoCaller(this);
8744 AssertComRCReturnVoid(autoCaller.rc());
8745 AssertComRCReturnVoid( autoCaller.state() == InUninit
8746 || autoCaller.state() == Limited);
8747
8748 /* tell all our other child objects we've been uninitialized */
8749 if (mBandwidthControl)
8750 {
8751 mBandwidthControl->uninit();
8752 unconst(mBandwidthControl).setNull();
8753 }
8754
8755 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8756 {
8757 if (mNetworkAdapters[slot])
8758 {
8759 mNetworkAdapters[slot]->uninit();
8760 unconst(mNetworkAdapters[slot]).setNull();
8761 }
8762 }
8763
8764 if (mUSBDeviceFilters)
8765 {
8766 mUSBDeviceFilters->uninit();
8767 unconst(mUSBDeviceFilters).setNull();
8768 }
8769
8770 if (mAudioAdapter)
8771 {
8772 mAudioAdapter->uninit();
8773 unconst(mAudioAdapter).setNull();
8774 }
8775
8776 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8777 {
8778 if (mParallelPorts[slot])
8779 {
8780 mParallelPorts[slot]->uninit();
8781 unconst(mParallelPorts[slot]).setNull();
8782 }
8783 }
8784
8785 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8786 {
8787 if (mSerialPorts[slot])
8788 {
8789 mSerialPorts[slot]->uninit();
8790 unconst(mSerialPorts[slot]).setNull();
8791 }
8792 }
8793
8794 if (mVRDEServer)
8795 {
8796 mVRDEServer->uninit();
8797 unconst(mVRDEServer).setNull();
8798 }
8799
8800 if (mBIOSSettings)
8801 {
8802 mBIOSSettings->uninit();
8803 unconst(mBIOSSettings).setNull();
8804 }
8805
8806 /* Deassociate media (only when a real Machine or a SnapshotMachine
8807 * instance is uninitialized; SessionMachine instances refer to real
8808 * Machine media). This is necessary for a clean re-initialization of
8809 * the VM after successfully re-checking the accessibility state. Note
8810 * that in case of normal Machine or SnapshotMachine uninitialization (as
8811 * a result of unregistering or deleting the snapshot), outdated media
8812 * attachments will already be uninitialized and deleted, so this
8813 * code will not affect them. */
8814 if ( !!mMediaData
8815 && (!isSessionMachine())
8816 )
8817 {
8818 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8819 it != mMediaData->mAttachments.end();
8820 ++it)
8821 {
8822 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8823 if (pMedium.isNull())
8824 continue;
8825 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, getSnapshotId());
8826 AssertComRC(rc);
8827 }
8828 }
8829
8830 if (!isSessionMachine() && !isSnapshotMachine())
8831 {
8832 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8833 if (mData->mFirstSnapshot)
8834 {
8835 // snapshots tree is protected by machine write lock; strictly
8836 // this isn't necessary here since we're deleting the entire
8837 // machine, but otherwise we assert in Snapshot::uninit()
8838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8839 mData->mFirstSnapshot->uninit();
8840 mData->mFirstSnapshot.setNull();
8841 }
8842
8843 mData->mCurrentSnapshot.setNull();
8844 }
8845
8846 /* free data structures (the essential mData structure is not freed here
8847 * since it may be still in use) */
8848 mMediaData.free();
8849 mStorageControllers.free();
8850 mUSBControllers.free();
8851 mHWData.free();
8852 mUserData.free();
8853 mSSData.free();
8854}
8855
8856/**
8857 * Returns a pointer to the Machine object for this machine that acts like a
8858 * parent for complex machine data objects such as shared folders, etc.
8859 *
8860 * For primary Machine objects and for SnapshotMachine objects, returns this
8861 * object's pointer itself. For SessionMachine objects, returns the peer
8862 * (primary) machine pointer.
8863 */
8864Machine* Machine::getMachine()
8865{
8866 if (isSessionMachine())
8867 return (Machine*)mPeer;
8868 return this;
8869}
8870
8871/**
8872 * Makes sure that there are no machine state dependents. If necessary, waits
8873 * for the number of dependents to drop to zero.
8874 *
8875 * Make sure this method is called from under this object's write lock to
8876 * guarantee that no new dependents may be added when this method returns
8877 * control to the caller.
8878 *
8879 * @note Locks this object for writing. The lock will be released while waiting
8880 * (if necessary).
8881 *
8882 * @warning To be used only in methods that change the machine state!
8883 */
8884void Machine::ensureNoStateDependencies()
8885{
8886 AssertReturnVoid(isWriteLockOnCurrentThread());
8887
8888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8889
8890 /* Wait for all state dependents if necessary */
8891 if (mData->mMachineStateDeps != 0)
8892 {
8893 /* lazy semaphore creation */
8894 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8895 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8896
8897 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8898 mData->mMachineStateDeps));
8899
8900 ++mData->mMachineStateChangePending;
8901
8902 /* reset the semaphore before waiting, the last dependent will signal
8903 * it */
8904 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8905
8906 alock.release();
8907
8908 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8909
8910 alock.acquire();
8911
8912 -- mData->mMachineStateChangePending;
8913 }
8914}
8915
8916/**
8917 * Changes the machine state and informs callbacks.
8918 *
8919 * This method is not intended to fail so it either returns S_OK or asserts (and
8920 * returns a failure).
8921 *
8922 * @note Locks this object for writing.
8923 */
8924HRESULT Machine::setMachineState(MachineState_T aMachineState)
8925{
8926 LogFlowThisFuncEnter();
8927 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8928
8929 AutoCaller autoCaller(this);
8930 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8931
8932 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8933
8934 /* wait for state dependents to drop to zero */
8935 ensureNoStateDependencies();
8936
8937 if (mData->mMachineState != aMachineState)
8938 {
8939 mData->mMachineState = aMachineState;
8940
8941 RTTimeNow(&mData->mLastStateChange);
8942
8943 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8944 }
8945
8946 LogFlowThisFuncLeave();
8947 return S_OK;
8948}
8949
8950/**
8951 * Searches for a shared folder with the given logical name
8952 * in the collection of shared folders.
8953 *
8954 * @param aName logical name of the shared folder
8955 * @param aSharedFolder where to return the found object
8956 * @param aSetError whether to set the error info if the folder is
8957 * not found
8958 * @return
8959 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8960 *
8961 * @note
8962 * must be called from under the object's lock!
8963 */
8964HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8965 ComObjPtr<SharedFolder> &aSharedFolder,
8966 bool aSetError /* = false */)
8967{
8968 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8969 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8970 it != mHWData->mSharedFolders.end();
8971 ++it)
8972 {
8973 SharedFolder *pSF = *it;
8974 AutoCaller autoCaller(pSF);
8975 if (pSF->getName() == aName)
8976 {
8977 aSharedFolder = pSF;
8978 rc = S_OK;
8979 break;
8980 }
8981 }
8982
8983 if (aSetError && FAILED(rc))
8984 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8985
8986 return rc;
8987}
8988
8989/**
8990 * Initializes all machine instance data from the given settings structures
8991 * from XML. The exception is the machine UUID which needs special handling
8992 * depending on the caller's use case, so the caller needs to set that herself.
8993 *
8994 * This gets called in several contexts during machine initialization:
8995 *
8996 * -- When machine XML exists on disk already and needs to be loaded into memory,
8997 * for example, from registeredInit() to load all registered machines on
8998 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8999 * attached to the machine should be part of some media registry already.
9000 *
9001 * -- During OVF import, when a machine config has been constructed from an
9002 * OVF file. In this case, puuidRegistry is set to the machine UUID to
9003 * ensure that the media listed as attachments in the config (which have
9004 * been imported from the OVF) receive the correct registry ID.
9005 *
9006 * -- During VM cloning.
9007 *
9008 * @param config Machine settings from XML.
9009 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
9010 * @return
9011 */
9012HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9013 const Guid *puuidRegistry)
9014{
9015 // copy name, description, OS type, teleporter, UTC etc.
9016 mUserData->s = config.machineUserData;
9017
9018 // Decode the Icon overide data from config userdata and set onto Machine.
9019 #define DECODE_STR_MAX _1M
9020 const char* pszStr = config.machineUserData.ovIcon.c_str();
9021 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
9022 if (cbOut > DECODE_STR_MAX)
9023 return setError(E_FAIL,
9024 tr("Icon Data too long.'%d' > '%d'"),
9025 cbOut,
9026 DECODE_STR_MAX);
9027 com::SafeArray<BYTE> iconByte(cbOut);
9028 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
9029 if (FAILED(rc))
9030 return setError(E_FAIL,
9031 tr("Failure to Decode Icon Data. '%s' (%d)"),
9032 pszStr,
9033 rc);
9034 mUserData->mIcon.resize(iconByte.size());
9035 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
9036
9037 // look up the object by Id to check it is valid
9038 ComPtr<IGuestOSType> guestOSType;
9039 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
9040 guestOSType.asOutParam());
9041 if (FAILED(rc)) return rc;
9042
9043 // stateFile (optional)
9044 if (config.strStateFile.isEmpty())
9045 mSSData->strStateFilePath.setNull();
9046 else
9047 {
9048 Utf8Str stateFilePathFull(config.strStateFile);
9049 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
9050 if (RT_FAILURE(vrc))
9051 return setError(E_FAIL,
9052 tr("Invalid saved state file path '%s' (%Rrc)"),
9053 config.strStateFile.c_str(),
9054 vrc);
9055 mSSData->strStateFilePath = stateFilePathFull;
9056 }
9057
9058 // snapshot folder needs special processing so set it again
9059 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9060 if (FAILED(rc)) return rc;
9061
9062 /* Copy the extra data items (Not in any case config is already the same as
9063 * mData->pMachineConfigFile, like when the xml files are read from disk. So
9064 * make sure the extra data map is copied). */
9065 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9066
9067 /* currentStateModified (optional, default is true) */
9068 mData->mCurrentStateModified = config.fCurrentStateModified;
9069
9070 mData->mLastStateChange = config.timeLastStateChange;
9071
9072 /*
9073 * note: all mUserData members must be assigned prior this point because
9074 * we need to commit changes in order to let mUserData be shared by all
9075 * snapshot machine instances.
9076 */
9077 mUserData.commitCopy();
9078
9079 // machine registry, if present (must be loaded before snapshots)
9080 if (config.canHaveOwnMediaRegistry())
9081 {
9082 // determine machine folder
9083 Utf8Str strMachineFolder = getSettingsFileFull();
9084 strMachineFolder.stripFilename();
9085 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
9086 config.mediaRegistry,
9087 strMachineFolder);
9088 if (FAILED(rc)) return rc;
9089 }
9090
9091 /* Snapshot node (optional) */
9092 size_t cRootSnapshots;
9093 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9094 {
9095 // there must be only one root snapshot
9096 Assert(cRootSnapshots == 1);
9097
9098 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9099
9100 rc = loadSnapshot(snap,
9101 config.uuidCurrentSnapshot,
9102 NULL); // no parent == first snapshot
9103 if (FAILED(rc)) return rc;
9104 }
9105
9106 // hardware data
9107 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9108 if (FAILED(rc)) return rc;
9109
9110 // load storage controllers
9111 rc = loadStorageControllers(config.storageMachine,
9112 puuidRegistry,
9113 NULL /* puuidSnapshot */);
9114 if (FAILED(rc)) return rc;
9115
9116 /*
9117 * NOTE: the assignment below must be the last thing to do,
9118 * otherwise it will be not possible to change the settings
9119 * somewhere in the code above because all setters will be
9120 * blocked by checkStateDependency(MutableStateDep).
9121 */
9122
9123 /* set the machine state to Aborted or Saved when appropriate */
9124 if (config.fAborted)
9125 {
9126 mSSData->strStateFilePath.setNull();
9127
9128 /* no need to use setMachineState() during init() */
9129 mData->mMachineState = MachineState_Aborted;
9130 }
9131 else if (!mSSData->strStateFilePath.isEmpty())
9132 {
9133 /* no need to use setMachineState() during init() */
9134 mData->mMachineState = MachineState_Saved;
9135 }
9136
9137 // after loading settings, we are no longer different from the XML on disk
9138 mData->flModifications = 0;
9139
9140 return S_OK;
9141}
9142
9143/**
9144 * Recursively loads all snapshots starting from the given.
9145 *
9146 * @param aNode <Snapshot> node.
9147 * @param aCurSnapshotId Current snapshot ID from the settings file.
9148 * @param aParentSnapshot Parent snapshot.
9149 */
9150HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9151 const Guid &aCurSnapshotId,
9152 Snapshot *aParentSnapshot)
9153{
9154 AssertReturn(!isSnapshotMachine(), E_FAIL);
9155 AssertReturn(!isSessionMachine(), E_FAIL);
9156
9157 HRESULT rc = S_OK;
9158
9159 Utf8Str strStateFile;
9160 if (!data.strStateFile.isEmpty())
9161 {
9162 /* optional */
9163 strStateFile = data.strStateFile;
9164 int vrc = calculateFullPath(strStateFile, strStateFile);
9165 if (RT_FAILURE(vrc))
9166 return setError(E_FAIL,
9167 tr("Invalid saved state file path '%s' (%Rrc)"),
9168 strStateFile.c_str(),
9169 vrc);
9170 }
9171
9172 /* create a snapshot machine object */
9173 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9174 pSnapshotMachine.createObject();
9175 rc = pSnapshotMachine->initFromSettings(this,
9176 data.hardware,
9177 &data.debugging,
9178 &data.autostart,
9179 data.storage,
9180 data.uuid.ref(),
9181 strStateFile);
9182 if (FAILED(rc)) return rc;
9183
9184 /* create a snapshot object */
9185 ComObjPtr<Snapshot> pSnapshot;
9186 pSnapshot.createObject();
9187 /* initialize the snapshot */
9188 rc = pSnapshot->init(mParent, // VirtualBox object
9189 data.uuid,
9190 data.strName,
9191 data.strDescription,
9192 data.timestamp,
9193 pSnapshotMachine,
9194 aParentSnapshot);
9195 if (FAILED(rc)) return rc;
9196
9197 /* memorize the first snapshot if necessary */
9198 if (!mData->mFirstSnapshot)
9199 mData->mFirstSnapshot = pSnapshot;
9200
9201 /* memorize the current snapshot when appropriate */
9202 if ( !mData->mCurrentSnapshot
9203 && pSnapshot->getId() == aCurSnapshotId
9204 )
9205 mData->mCurrentSnapshot = pSnapshot;
9206
9207 // now create the children
9208 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9209 it != data.llChildSnapshots.end();
9210 ++it)
9211 {
9212 const settings::Snapshot &childData = *it;
9213 // recurse
9214 rc = loadSnapshot(childData,
9215 aCurSnapshotId,
9216 pSnapshot); // parent = the one we created above
9217 if (FAILED(rc)) return rc;
9218 }
9219
9220 return rc;
9221}
9222
9223/**
9224 * Loads settings into mHWData.
9225 *
9226 * @param data Reference to the hardware settings.
9227 * @param pDbg Pointer to the debugging settings.
9228 * @param pAutostart Pointer to the autostart settings.
9229 */
9230HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9231 const settings::Autostart *pAutostart)
9232{
9233 AssertReturn(!isSessionMachine(), E_FAIL);
9234
9235 HRESULT rc = S_OK;
9236
9237 try
9238 {
9239 /* The hardware version attribute (optional). */
9240 mHWData->mHWVersion = data.strVersion;
9241 mHWData->mHardwareUUID = data.uuid;
9242
9243 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9244 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9245 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9246 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9247 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9248 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9249 mHWData->mPAEEnabled = data.fPAE;
9250 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9251 mHWData->mLongMode = data.enmLongMode;
9252 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9253 mHWData->mCPUCount = data.cCPUs;
9254 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9255 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9256
9257 // cpu
9258 if (mHWData->mCPUHotPlugEnabled)
9259 {
9260 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9261 it != data.llCpus.end();
9262 ++it)
9263 {
9264 const settings::Cpu &cpu = *it;
9265
9266 mHWData->mCPUAttached[cpu.ulId] = true;
9267 }
9268 }
9269
9270 // cpuid leafs
9271 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9272 it != data.llCpuIdLeafs.end();
9273 ++it)
9274 {
9275 const settings::CpuIdLeaf &leaf = *it;
9276
9277 switch (leaf.ulId)
9278 {
9279 case 0x0:
9280 case 0x1:
9281 case 0x2:
9282 case 0x3:
9283 case 0x4:
9284 case 0x5:
9285 case 0x6:
9286 case 0x7:
9287 case 0x8:
9288 case 0x9:
9289 case 0xA:
9290 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9291 break;
9292
9293 case 0x80000000:
9294 case 0x80000001:
9295 case 0x80000002:
9296 case 0x80000003:
9297 case 0x80000004:
9298 case 0x80000005:
9299 case 0x80000006:
9300 case 0x80000007:
9301 case 0x80000008:
9302 case 0x80000009:
9303 case 0x8000000A:
9304 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9305 break;
9306
9307 default:
9308 /* just ignore */
9309 break;
9310 }
9311 }
9312
9313 mHWData->mMemorySize = data.ulMemorySizeMB;
9314 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9315
9316 // boot order
9317 for (size_t i = 0;
9318 i < RT_ELEMENTS(mHWData->mBootOrder);
9319 i++)
9320 {
9321 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9322 if (it == data.mapBootOrder.end())
9323 mHWData->mBootOrder[i] = DeviceType_Null;
9324 else
9325 mHWData->mBootOrder[i] = it->second;
9326 }
9327
9328 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9329 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9330 mHWData->mMonitorCount = data.cMonitors;
9331 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9332 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9333 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9334 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9335 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9336 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9337 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9338 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9339 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9340 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9341 if (!data.strVideoCaptureFile.isEmpty())
9342 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9343 else
9344 mHWData->mVideoCaptureFile.setNull();
9345 mHWData->mFirmwareType = data.firmwareType;
9346 mHWData->mPointingHIDType = data.pointingHIDType;
9347 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9348 mHWData->mChipsetType = data.chipsetType;
9349 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9350 mHWData->mHPETEnabled = data.fHPETEnabled;
9351
9352 /* VRDEServer */
9353 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9354 if (FAILED(rc)) return rc;
9355
9356 /* BIOS */
9357 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9358 if (FAILED(rc)) return rc;
9359
9360 // Bandwidth control (must come before network adapters)
9361 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9362 if (FAILED(rc)) return rc;
9363
9364 /* Shared folders */
9365 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9366 it != data.usbSettings.llUSBControllers.end();
9367 ++it)
9368 {
9369 const settings::USBController &settingsCtrl = *it;
9370 ComObjPtr<USBController> newCtrl;
9371
9372 newCtrl.createObject();
9373 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9374 mUSBControllers->push_back(newCtrl);
9375 }
9376
9377 /* USB device filters */
9378 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9379 if (FAILED(rc)) return rc;
9380
9381 // network adapters
9382 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9383 uint32_t oldCount = mNetworkAdapters.size();
9384 if (newCount > oldCount)
9385 {
9386 mNetworkAdapters.resize(newCount);
9387 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9388 {
9389 unconst(mNetworkAdapters[slot]).createObject();
9390 mNetworkAdapters[slot]->init(this, slot);
9391 }
9392 }
9393 else if (newCount < oldCount)
9394 mNetworkAdapters.resize(newCount);
9395 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9396 it != data.llNetworkAdapters.end();
9397 ++it)
9398 {
9399 const settings::NetworkAdapter &nic = *it;
9400
9401 /* slot unicity is guaranteed by XML Schema */
9402 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9403 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9404 if (FAILED(rc)) return rc;
9405 }
9406
9407 // serial ports
9408 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9409 it != data.llSerialPorts.end();
9410 ++it)
9411 {
9412 const settings::SerialPort &s = *it;
9413
9414 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9415 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9416 if (FAILED(rc)) return rc;
9417 }
9418
9419 // parallel ports (optional)
9420 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9421 it != data.llParallelPorts.end();
9422 ++it)
9423 {
9424 const settings::ParallelPort &p = *it;
9425
9426 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9427 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9428 if (FAILED(rc)) return rc;
9429 }
9430
9431 /* AudioAdapter */
9432 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9433 if (FAILED(rc)) return rc;
9434
9435 /* Shared folders */
9436 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9437 it != data.llSharedFolders.end();
9438 ++it)
9439 {
9440 const settings::SharedFolder &sf = *it;
9441
9442 ComObjPtr<SharedFolder> sharedFolder;
9443 /* Check for double entries. Not allowed! */
9444 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9445 if (SUCCEEDED(rc))
9446 return setError(VBOX_E_OBJECT_IN_USE,
9447 tr("Shared folder named '%s' already exists"),
9448 sf.strName.c_str());
9449
9450 /* Create the new shared folder. Don't break on error. This will be
9451 * reported when the machine starts. */
9452 sharedFolder.createObject();
9453 rc = sharedFolder->init(getMachine(),
9454 sf.strName,
9455 sf.strHostPath,
9456 RT_BOOL(sf.fWritable),
9457 RT_BOOL(sf.fAutoMount),
9458 false /* fFailOnError */);
9459 if (FAILED(rc)) return rc;
9460 mHWData->mSharedFolders.push_back(sharedFolder);
9461 }
9462
9463 // Clipboard
9464 mHWData->mClipboardMode = data.clipboardMode;
9465
9466 // drag'n'drop
9467 mHWData->mDragAndDropMode = data.dragAndDropMode;
9468
9469 // guest settings
9470 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9471
9472 // IO settings
9473 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9474 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9475
9476 // Host PCI devices
9477 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9478 it != data.pciAttachments.end();
9479 ++it)
9480 {
9481 const settings::HostPCIDeviceAttachment &hpda = *it;
9482 ComObjPtr<PCIDeviceAttachment> pda;
9483
9484 pda.createObject();
9485 pda->loadSettings(this, hpda);
9486 mHWData->mPCIDeviceAssignments.push_back(pda);
9487 }
9488
9489 /*
9490 * (The following isn't really real hardware, but it lives in HWData
9491 * for reasons of convenience.)
9492 */
9493
9494#ifdef VBOX_WITH_GUEST_PROPS
9495 /* Guest properties (optional) */
9496 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9497 it != data.llGuestProperties.end();
9498 ++it)
9499 {
9500 const settings::GuestProperty &prop = *it;
9501 uint32_t fFlags = guestProp::NILFLAG;
9502 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9503 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9504 mHWData->mGuestProperties[prop.strName] = property;
9505 }
9506
9507 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9508#endif /* VBOX_WITH_GUEST_PROPS defined */
9509
9510 rc = loadDebugging(pDbg);
9511 if (FAILED(rc))
9512 return rc;
9513
9514 mHWData->mAutostart = *pAutostart;
9515
9516 /* default frontend */
9517 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9518 }
9519 catch(std::bad_alloc &)
9520 {
9521 return E_OUTOFMEMORY;
9522 }
9523
9524 AssertComRC(rc);
9525 return rc;
9526}
9527
9528/**
9529 * Called from Machine::loadHardware() to load the debugging settings of the
9530 * machine.
9531 *
9532 * @param pDbg Pointer to the settings.
9533 */
9534HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9535{
9536 mHWData->mDebugging = *pDbg;
9537 /* no more processing currently required, this will probably change. */
9538 return S_OK;
9539}
9540
9541/**
9542 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9543 *
9544 * @param data
9545 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9546 * @param puuidSnapshot
9547 * @return
9548 */
9549HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9550 const Guid *puuidRegistry,
9551 const Guid *puuidSnapshot)
9552{
9553 AssertReturn(!isSessionMachine(), E_FAIL);
9554
9555 HRESULT rc = S_OK;
9556
9557 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9558 it != data.llStorageControllers.end();
9559 ++it)
9560 {
9561 const settings::StorageController &ctlData = *it;
9562
9563 ComObjPtr<StorageController> pCtl;
9564 /* Try to find one with the name first. */
9565 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9566 if (SUCCEEDED(rc))
9567 return setError(VBOX_E_OBJECT_IN_USE,
9568 tr("Storage controller named '%s' already exists"),
9569 ctlData.strName.c_str());
9570
9571 pCtl.createObject();
9572 rc = pCtl->init(this,
9573 ctlData.strName,
9574 ctlData.storageBus,
9575 ctlData.ulInstance,
9576 ctlData.fBootable);
9577 if (FAILED(rc)) return rc;
9578
9579 mStorageControllers->push_back(pCtl);
9580
9581 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9582 if (FAILED(rc)) return rc;
9583
9584 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9585 if (FAILED(rc)) return rc;
9586
9587 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9588 if (FAILED(rc)) return rc;
9589
9590 /* Set IDE emulation settings (only for AHCI controller). */
9591 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9592 {
9593 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9594 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9595 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9596 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9597 )
9598 return rc;
9599 }
9600
9601 /* Load the attached devices now. */
9602 rc = loadStorageDevices(pCtl,
9603 ctlData,
9604 puuidRegistry,
9605 puuidSnapshot);
9606 if (FAILED(rc)) return rc;
9607 }
9608
9609 return S_OK;
9610}
9611
9612/**
9613 * Called from loadStorageControllers for a controller's devices.
9614 *
9615 * @param aStorageController
9616 * @param data
9617 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9618 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9619 * @return
9620 */
9621HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9622 const settings::StorageController &data,
9623 const Guid *puuidRegistry,
9624 const Guid *puuidSnapshot)
9625{
9626 HRESULT rc = S_OK;
9627
9628 /* paranoia: detect duplicate attachments */
9629 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9630 it != data.llAttachedDevices.end();
9631 ++it)
9632 {
9633 const settings::AttachedDevice &ad = *it;
9634
9635 for (settings::AttachedDevicesList::const_iterator it2 = it;
9636 it2 != data.llAttachedDevices.end();
9637 ++it2)
9638 {
9639 if (it == it2)
9640 continue;
9641
9642 const settings::AttachedDevice &ad2 = *it2;
9643
9644 if ( ad.lPort == ad2.lPort
9645 && ad.lDevice == ad2.lDevice)
9646 {
9647 return setError(E_FAIL,
9648 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9649 aStorageController->i_getName().c_str(),
9650 ad.lPort,
9651 ad.lDevice,
9652 mUserData->s.strName.c_str());
9653 }
9654 }
9655 }
9656
9657 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9658 it != data.llAttachedDevices.end();
9659 ++it)
9660 {
9661 const settings::AttachedDevice &dev = *it;
9662 ComObjPtr<Medium> medium;
9663
9664 switch (dev.deviceType)
9665 {
9666 case DeviceType_Floppy:
9667 case DeviceType_DVD:
9668 if (dev.strHostDriveSrc.isNotEmpty())
9669 rc = mParent->host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9670 else
9671 rc = mParent->findRemoveableMedium(dev.deviceType,
9672 dev.uuid,
9673 false /* fRefresh */,
9674 false /* aSetError */,
9675 medium);
9676 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9677 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9678 rc = S_OK;
9679 break;
9680
9681 case DeviceType_HardDisk:
9682 {
9683 /* find a hard disk by UUID */
9684 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9685 if (FAILED(rc))
9686 {
9687 if (isSnapshotMachine())
9688 {
9689 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9690 // so the user knows that the bad disk is in a snapshot somewhere
9691 com::ErrorInfo info;
9692 return setError(E_FAIL,
9693 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9694 puuidSnapshot->raw(),
9695 info.getText().raw());
9696 }
9697 else
9698 return rc;
9699 }
9700
9701 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9702
9703 if (medium->i_getType() == MediumType_Immutable)
9704 {
9705 if (isSnapshotMachine())
9706 return setError(E_FAIL,
9707 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9708 "of the virtual machine '%s' ('%s')"),
9709 medium->i_getLocationFull().c_str(),
9710 dev.uuid.raw(),
9711 puuidSnapshot->raw(),
9712 mUserData->s.strName.c_str(),
9713 mData->m_strConfigFileFull.c_str());
9714
9715 return setError(E_FAIL,
9716 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9717 medium->i_getLocationFull().c_str(),
9718 dev.uuid.raw(),
9719 mUserData->s.strName.c_str(),
9720 mData->m_strConfigFileFull.c_str());
9721 }
9722
9723 if (medium->i_getType() == MediumType_MultiAttach)
9724 {
9725 if (isSnapshotMachine())
9726 return setError(E_FAIL,
9727 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9728 "of the virtual machine '%s' ('%s')"),
9729 medium->i_getLocationFull().c_str(),
9730 dev.uuid.raw(),
9731 puuidSnapshot->raw(),
9732 mUserData->s.strName.c_str(),
9733 mData->m_strConfigFileFull.c_str());
9734
9735 return setError(E_FAIL,
9736 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9737 medium->i_getLocationFull().c_str(),
9738 dev.uuid.raw(),
9739 mUserData->s.strName.c_str(),
9740 mData->m_strConfigFileFull.c_str());
9741 }
9742
9743 if ( !isSnapshotMachine()
9744 && medium->i_getChildren().size() != 0
9745 )
9746 return setError(E_FAIL,
9747 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9748 "because it has %d differencing child hard disks"),
9749 medium->i_getLocationFull().c_str(),
9750 dev.uuid.raw(),
9751 mUserData->s.strName.c_str(),
9752 mData->m_strConfigFileFull.c_str(),
9753 medium->i_getChildren().size());
9754
9755 if (findAttachment(mMediaData->mAttachments,
9756 medium))
9757 return setError(E_FAIL,
9758 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9759 medium->i_getLocationFull().c_str(),
9760 dev.uuid.raw(),
9761 mUserData->s.strName.c_str(),
9762 mData->m_strConfigFileFull.c_str());
9763
9764 break;
9765 }
9766
9767 default:
9768 return setError(E_FAIL,
9769 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9770 medium->i_getLocationFull().c_str(),
9771 mUserData->s.strName.c_str(),
9772 mData->m_strConfigFileFull.c_str());
9773 }
9774
9775 if (FAILED(rc))
9776 break;
9777
9778 /* Bandwidth groups are loaded at this point. */
9779 ComObjPtr<BandwidthGroup> pBwGroup;
9780
9781 if (!dev.strBwGroup.isEmpty())
9782 {
9783 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9784 if (FAILED(rc))
9785 return setError(E_FAIL,
9786 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9787 medium->i_getLocationFull().c_str(),
9788 dev.strBwGroup.c_str(),
9789 mUserData->s.strName.c_str(),
9790 mData->m_strConfigFileFull.c_str());
9791 pBwGroup->i_reference();
9792 }
9793
9794 const Bstr controllerName = aStorageController->i_getName();
9795 ComObjPtr<MediumAttachment> pAttachment;
9796 pAttachment.createObject();
9797 rc = pAttachment->init(this,
9798 medium,
9799 controllerName,
9800 dev.lPort,
9801 dev.lDevice,
9802 dev.deviceType,
9803 false,
9804 dev.fPassThrough,
9805 dev.fTempEject,
9806 dev.fNonRotational,
9807 dev.fDiscard,
9808 dev.fHotPluggable,
9809 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9810 if (FAILED(rc)) break;
9811
9812 /* associate the medium with this machine and snapshot */
9813 if (!medium.isNull())
9814 {
9815 AutoCaller medCaller(medium);
9816 if (FAILED(medCaller.rc())) return medCaller.rc();
9817 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9818
9819 if (isSnapshotMachine())
9820 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9821 else
9822 rc = medium->i_addBackReference(mData->mUuid);
9823 /* If the medium->addBackReference fails it sets an appropriate
9824 * error message, so no need to do any guesswork here. */
9825
9826 if (puuidRegistry)
9827 // caller wants registry ID to be set on all attached media (OVF import case)
9828 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9829 }
9830
9831 if (FAILED(rc))
9832 break;
9833
9834 /* back up mMediaData to let registeredInit() properly rollback on failure
9835 * (= limited accessibility) */
9836 setModified(IsModified_Storage);
9837 mMediaData.backup();
9838 mMediaData->mAttachments.push_back(pAttachment);
9839 }
9840
9841 return rc;
9842}
9843
9844/**
9845 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9846 *
9847 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9848 * @param aSnapshot where to return the found snapshot
9849 * @param aSetError true to set extended error info on failure
9850 */
9851HRESULT Machine::findSnapshotById(const Guid &aId,
9852 ComObjPtr<Snapshot> &aSnapshot,
9853 bool aSetError /* = false */)
9854{
9855 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9856
9857 if (!mData->mFirstSnapshot)
9858 {
9859 if (aSetError)
9860 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9861 return E_FAIL;
9862 }
9863
9864 if (aId.isZero())
9865 aSnapshot = mData->mFirstSnapshot;
9866 else
9867 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9868
9869 if (!aSnapshot)
9870 {
9871 if (aSetError)
9872 return setError(E_FAIL,
9873 tr("Could not find a snapshot with UUID {%s}"),
9874 aId.toString().c_str());
9875 return E_FAIL;
9876 }
9877
9878 return S_OK;
9879}
9880
9881/**
9882 * Returns the snapshot with the given name or fails of no such snapshot.
9883 *
9884 * @param aName snapshot name to find
9885 * @param aSnapshot where to return the found snapshot
9886 * @param aSetError true to set extended error info on failure
9887 */
9888HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9889 ComObjPtr<Snapshot> &aSnapshot,
9890 bool aSetError /* = false */)
9891{
9892 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9893
9894 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9895
9896 if (!mData->mFirstSnapshot)
9897 {
9898 if (aSetError)
9899 return setError(VBOX_E_OBJECT_NOT_FOUND,
9900 tr("This machine does not have any snapshots"));
9901 return VBOX_E_OBJECT_NOT_FOUND;
9902 }
9903
9904 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9905
9906 if (!aSnapshot)
9907 {
9908 if (aSetError)
9909 return setError(VBOX_E_OBJECT_NOT_FOUND,
9910 tr("Could not find a snapshot named '%s'"), strName.c_str());
9911 return VBOX_E_OBJECT_NOT_FOUND;
9912 }
9913
9914 return S_OK;
9915}
9916
9917/**
9918 * Returns a storage controller object with the given name.
9919 *
9920 * @param aName storage controller name to find
9921 * @param aStorageController where to return the found storage controller
9922 * @param aSetError true to set extended error info on failure
9923 */
9924HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9925 ComObjPtr<StorageController> &aStorageController,
9926 bool aSetError /* = false */)
9927{
9928 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9929
9930 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9931 it != mStorageControllers->end();
9932 ++it)
9933 {
9934 if ((*it)->i_getName() == aName)
9935 {
9936 aStorageController = (*it);
9937 return S_OK;
9938 }
9939 }
9940
9941 if (aSetError)
9942 return setError(VBOX_E_OBJECT_NOT_FOUND,
9943 tr("Could not find a storage controller named '%s'"),
9944 aName.c_str());
9945 return VBOX_E_OBJECT_NOT_FOUND;
9946}
9947
9948/**
9949 * Returns a USB controller object with the given name.
9950 *
9951 * @param aName USB controller name to find
9952 * @param aUSBController where to return the found USB controller
9953 * @param aSetError true to set extended error info on failure
9954 */
9955HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9956 ComObjPtr<USBController> &aUSBController,
9957 bool aSetError /* = false */)
9958{
9959 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9960
9961 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9962 it != mUSBControllers->end();
9963 ++it)
9964 {
9965 if ((*it)->i_getName() == aName)
9966 {
9967 aUSBController = (*it);
9968 return S_OK;
9969 }
9970 }
9971
9972 if (aSetError)
9973 return setError(VBOX_E_OBJECT_NOT_FOUND,
9974 tr("Could not find a storage controller named '%s'"),
9975 aName.c_str());
9976 return VBOX_E_OBJECT_NOT_FOUND;
9977}
9978
9979/**
9980 * Returns the number of USB controller instance of the given type.
9981 *
9982 * @param enmType USB controller type.
9983 */
9984ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9985{
9986 ULONG cCtrls = 0;
9987
9988 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9989 it != mUSBControllers->end();
9990 ++it)
9991 {
9992 if ((*it)->i_getControllerType() == enmType)
9993 cCtrls++;
9994 }
9995
9996 return cCtrls;
9997}
9998
9999HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
10000 MediaData::AttachmentList &atts)
10001{
10002 AutoCaller autoCaller(this);
10003 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10004
10005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10006
10007 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
10008 it != mMediaData->mAttachments.end();
10009 ++it)
10010 {
10011 const ComObjPtr<MediumAttachment> &pAtt = *it;
10012
10013 // should never happen, but deal with NULL pointers in the list.
10014 AssertStmt(!pAtt.isNull(), continue);
10015
10016 // getControllerName() needs caller+read lock
10017 AutoCaller autoAttCaller(pAtt);
10018 if (FAILED(autoAttCaller.rc()))
10019 {
10020 atts.clear();
10021 return autoAttCaller.rc();
10022 }
10023 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10024
10025 if (pAtt->i_getControllerName() == aName)
10026 atts.push_back(pAtt);
10027 }
10028
10029 return S_OK;
10030}
10031
10032/**
10033 * Helper for #saveSettings. Cares about renaming the settings directory and
10034 * file if the machine name was changed and about creating a new settings file
10035 * if this is a new machine.
10036 *
10037 * @note Must be never called directly but only from #saveSettings().
10038 */
10039HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
10040{
10041 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10042
10043 HRESULT rc = S_OK;
10044
10045 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10046
10047 /// @todo need to handle primary group change, too
10048
10049 /* attempt to rename the settings file if machine name is changed */
10050 if ( mUserData->s.fNameSync
10051 && mUserData.isBackedUp()
10052 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10053 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10054 )
10055 {
10056 bool dirRenamed = false;
10057 bool fileRenamed = false;
10058
10059 Utf8Str configFile, newConfigFile;
10060 Utf8Str configFilePrev, newConfigFilePrev;
10061 Utf8Str configDir, newConfigDir;
10062
10063 do
10064 {
10065 int vrc = VINF_SUCCESS;
10066
10067 Utf8Str name = mUserData.backedUpData()->s.strName;
10068 Utf8Str newName = mUserData->s.strName;
10069 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10070 if (group == "/")
10071 group.setNull();
10072 Utf8Str newGroup = mUserData->s.llGroups.front();
10073 if (newGroup == "/")
10074 newGroup.setNull();
10075
10076 configFile = mData->m_strConfigFileFull;
10077
10078 /* first, rename the directory if it matches the group and machine name */
10079 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
10080 group.c_str(), RTPATH_DELIMITER, name.c_str());
10081 /** @todo hack, make somehow use of ComposeMachineFilename */
10082 if (mUserData->s.fDirectoryIncludesUUID)
10083 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10084 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
10085 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10086 /** @todo hack, make somehow use of ComposeMachineFilename */
10087 if (mUserData->s.fDirectoryIncludesUUID)
10088 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10089 configDir = configFile;
10090 configDir.stripFilename();
10091 newConfigDir = configDir;
10092 if ( configDir.length() >= groupPlusName.length()
10093 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
10094 {
10095 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10096 Utf8Str newConfigBaseDir(newConfigDir);
10097 newConfigDir.append(newGroupPlusName);
10098 /* consistency: use \ if appropriate on the platform */
10099 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10100 /* new dir and old dir cannot be equal here because of 'if'
10101 * above and because name != newName */
10102 Assert(configDir != newConfigDir);
10103 if (!fSettingsFileIsNew)
10104 {
10105 /* perform real rename only if the machine is not new */
10106 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10107 if ( vrc == VERR_FILE_NOT_FOUND
10108 || vrc == VERR_PATH_NOT_FOUND)
10109 {
10110 /* create the parent directory, then retry renaming */
10111 Utf8Str parent(newConfigDir);
10112 parent.stripFilename();
10113 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10114 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10115 }
10116 if (RT_FAILURE(vrc))
10117 {
10118 rc = setError(E_FAIL,
10119 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10120 configDir.c_str(),
10121 newConfigDir.c_str(),
10122 vrc);
10123 break;
10124 }
10125 /* delete subdirectories which are no longer needed */
10126 Utf8Str dir(configDir);
10127 dir.stripFilename();
10128 while (dir != newConfigBaseDir && dir != ".")
10129 {
10130 vrc = RTDirRemove(dir.c_str());
10131 if (RT_FAILURE(vrc))
10132 break;
10133 dir.stripFilename();
10134 }
10135 dirRenamed = true;
10136 }
10137 }
10138
10139 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10140 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10141
10142 /* then try to rename the settings file itself */
10143 if (newConfigFile != configFile)
10144 {
10145 /* get the path to old settings file in renamed directory */
10146 configFile = Utf8StrFmt("%s%c%s",
10147 newConfigDir.c_str(),
10148 RTPATH_DELIMITER,
10149 RTPathFilename(configFile.c_str()));
10150 if (!fSettingsFileIsNew)
10151 {
10152 /* perform real rename only if the machine is not new */
10153 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10154 if (RT_FAILURE(vrc))
10155 {
10156 rc = setError(E_FAIL,
10157 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10158 configFile.c_str(),
10159 newConfigFile.c_str(),
10160 vrc);
10161 break;
10162 }
10163 fileRenamed = true;
10164 configFilePrev = configFile;
10165 configFilePrev += "-prev";
10166 newConfigFilePrev = newConfigFile;
10167 newConfigFilePrev += "-prev";
10168 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10169 }
10170 }
10171
10172 // update m_strConfigFileFull amd mConfigFile
10173 mData->m_strConfigFileFull = newConfigFile;
10174 // compute the relative path too
10175 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10176
10177 // store the old and new so that VirtualBox::saveSettings() can update
10178 // the media registry
10179 if ( mData->mRegistered
10180 && configDir != newConfigDir)
10181 {
10182 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10183
10184 if (pfNeedsGlobalSaveSettings)
10185 *pfNeedsGlobalSaveSettings = true;
10186 }
10187
10188 // in the saved state file path, replace the old directory with the new directory
10189 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10190 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10191
10192 // and do the same thing for the saved state file paths of all the online snapshots
10193 if (mData->mFirstSnapshot)
10194 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
10195 newConfigDir.c_str());
10196 }
10197 while (0);
10198
10199 if (FAILED(rc))
10200 {
10201 /* silently try to rename everything back */
10202 if (fileRenamed)
10203 {
10204 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10205 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10206 }
10207 if (dirRenamed)
10208 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10209 }
10210
10211 if (FAILED(rc)) return rc;
10212 }
10213
10214 if (fSettingsFileIsNew)
10215 {
10216 /* create a virgin config file */
10217 int vrc = VINF_SUCCESS;
10218
10219 /* ensure the settings directory exists */
10220 Utf8Str path(mData->m_strConfigFileFull);
10221 path.stripFilename();
10222 if (!RTDirExists(path.c_str()))
10223 {
10224 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10225 if (RT_FAILURE(vrc))
10226 {
10227 return setError(E_FAIL,
10228 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10229 path.c_str(),
10230 vrc);
10231 }
10232 }
10233
10234 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10235 path = Utf8Str(mData->m_strConfigFileFull);
10236 RTFILE f = NIL_RTFILE;
10237 vrc = RTFileOpen(&f, path.c_str(),
10238 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10239 if (RT_FAILURE(vrc))
10240 return setError(E_FAIL,
10241 tr("Could not create the settings file '%s' (%Rrc)"),
10242 path.c_str(),
10243 vrc);
10244 RTFileClose(f);
10245 }
10246
10247 return rc;
10248}
10249
10250/**
10251 * Saves and commits machine data, user data and hardware data.
10252 *
10253 * Note that on failure, the data remains uncommitted.
10254 *
10255 * @a aFlags may combine the following flags:
10256 *
10257 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10258 * Used when saving settings after an operation that makes them 100%
10259 * correspond to the settings from the current snapshot.
10260 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10261 * #isReallyModified() returns false. This is necessary for cases when we
10262 * change machine data directly, not through the backup()/commit() mechanism.
10263 * - SaveS_Force: settings will be saved without doing a deep compare of the
10264 * settings structures. This is used when this is called because snapshots
10265 * have changed to avoid the overhead of the deep compare.
10266 *
10267 * @note Must be called from under this object's write lock. Locks children for
10268 * writing.
10269 *
10270 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10271 * initialized to false and that will be set to true by this function if
10272 * the caller must invoke VirtualBox::saveSettings() because the global
10273 * settings have changed. This will happen if a machine rename has been
10274 * saved and the global machine and media registries will therefore need
10275 * updating.
10276 */
10277HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10278 int aFlags /*= 0*/)
10279{
10280 LogFlowThisFuncEnter();
10281
10282 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10283
10284 /* make sure child objects are unable to modify the settings while we are
10285 * saving them */
10286 ensureNoStateDependencies();
10287
10288 AssertReturn(!isSnapshotMachine(),
10289 E_FAIL);
10290
10291 HRESULT rc = S_OK;
10292 bool fNeedsWrite = false;
10293
10294 /* First, prepare to save settings. It will care about renaming the
10295 * settings directory and file if the machine name was changed and about
10296 * creating a new settings file if this is a new machine. */
10297 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10298 if (FAILED(rc)) return rc;
10299
10300 // keep a pointer to the current settings structures
10301 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10302 settings::MachineConfigFile *pNewConfig = NULL;
10303
10304 try
10305 {
10306 // make a fresh one to have everyone write stuff into
10307 pNewConfig = new settings::MachineConfigFile(NULL);
10308 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10309
10310 // now go and copy all the settings data from COM to the settings structures
10311 // (this calles saveSettings() on all the COM objects in the machine)
10312 copyMachineDataToSettings(*pNewConfig);
10313
10314 if (aFlags & SaveS_ResetCurStateModified)
10315 {
10316 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10317 mData->mCurrentStateModified = FALSE;
10318 fNeedsWrite = true; // always, no need to compare
10319 }
10320 else if (aFlags & SaveS_Force)
10321 {
10322 fNeedsWrite = true; // always, no need to compare
10323 }
10324 else
10325 {
10326 if (!mData->mCurrentStateModified)
10327 {
10328 // do a deep compare of the settings that we just saved with the settings
10329 // previously stored in the config file; this invokes MachineConfigFile::operator==
10330 // which does a deep compare of all the settings, which is expensive but less expensive
10331 // than writing out XML in vain
10332 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10333
10334 // could still be modified if any settings changed
10335 mData->mCurrentStateModified = fAnySettingsChanged;
10336
10337 fNeedsWrite = fAnySettingsChanged;
10338 }
10339 else
10340 fNeedsWrite = true;
10341 }
10342
10343 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10344
10345 if (fNeedsWrite)
10346 // now spit it all out!
10347 pNewConfig->write(mData->m_strConfigFileFull);
10348
10349 mData->pMachineConfigFile = pNewConfig;
10350 delete pOldConfig;
10351 commit();
10352
10353 // after saving settings, we are no longer different from the XML on disk
10354 mData->flModifications = 0;
10355 }
10356 catch (HRESULT err)
10357 {
10358 // we assume that error info is set by the thrower
10359 rc = err;
10360
10361 // restore old config
10362 delete pNewConfig;
10363 mData->pMachineConfigFile = pOldConfig;
10364 }
10365 catch (...)
10366 {
10367 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10368 }
10369
10370 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10371 {
10372 /* Fire the data change event, even on failure (since we've already
10373 * committed all data). This is done only for SessionMachines because
10374 * mutable Machine instances are always not registered (i.e. private
10375 * to the client process that creates them) and thus don't need to
10376 * inform callbacks. */
10377 if (isSessionMachine())
10378 mParent->onMachineDataChange(mData->mUuid);
10379 }
10380
10381 LogFlowThisFunc(("rc=%08X\n", rc));
10382 LogFlowThisFuncLeave();
10383 return rc;
10384}
10385
10386/**
10387 * Implementation for saving the machine settings into the given
10388 * settings::MachineConfigFile instance. This copies machine extradata
10389 * from the previous machine config file in the instance data, if any.
10390 *
10391 * This gets called from two locations:
10392 *
10393 * -- Machine::saveSettings(), during the regular XML writing;
10394 *
10395 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10396 * exported to OVF and we write the VirtualBox proprietary XML
10397 * into a <vbox:Machine> tag.
10398 *
10399 * This routine fills all the fields in there, including snapshots, *except*
10400 * for the following:
10401 *
10402 * -- fCurrentStateModified. There is some special logic associated with that.
10403 *
10404 * The caller can then call MachineConfigFile::write() or do something else
10405 * with it.
10406 *
10407 * Caller must hold the machine lock!
10408 *
10409 * This throws XML errors and HRESULT, so the caller must have a catch block!
10410 */
10411void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10412{
10413 // deep copy extradata
10414 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10415
10416 config.uuid = mData->mUuid;
10417
10418 // copy name, description, OS type, teleport, UTC etc.
10419 config.machineUserData = mUserData->s;
10420
10421 // Encode the Icon Override data from Machine and store on config userdata.
10422 com::SafeArray<BYTE> iconByte;
10423 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10424 ssize_t cbData = iconByte.size();
10425 if (cbData > 0)
10426 {
10427 ssize_t cchOut = RTBase64EncodedLength(cbData);
10428 Utf8Str strIconData;
10429 strIconData.reserve(cchOut+1);
10430 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10431 strIconData.mutableRaw(), strIconData.capacity(),
10432 NULL);
10433 if (RT_FAILURE(vrc))
10434 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10435 strIconData.jolt();
10436 config.machineUserData.ovIcon = strIconData;
10437 }
10438 else
10439 config.machineUserData.ovIcon.setNull();
10440
10441 if ( mData->mMachineState == MachineState_Saved
10442 || mData->mMachineState == MachineState_Restoring
10443 // when deleting a snapshot we may or may not have a saved state in the current state,
10444 // so let's not assert here please
10445 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10446 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10447 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10448 && (!mSSData->strStateFilePath.isEmpty())
10449 )
10450 )
10451 {
10452 Assert(!mSSData->strStateFilePath.isEmpty());
10453 /* try to make the file name relative to the settings file dir */
10454 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10455 }
10456 else
10457 {
10458 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10459 config.strStateFile.setNull();
10460 }
10461
10462 if (mData->mCurrentSnapshot)
10463 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10464 else
10465 config.uuidCurrentSnapshot.clear();
10466
10467 config.timeLastStateChange = mData->mLastStateChange;
10468 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10469 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10470
10471 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10472 if (FAILED(rc)) throw rc;
10473
10474 rc = saveStorageControllers(config.storageMachine);
10475 if (FAILED(rc)) throw rc;
10476
10477 // save machine's media registry if this is VirtualBox 4.0 or later
10478 if (config.canHaveOwnMediaRegistry())
10479 {
10480 // determine machine folder
10481 Utf8Str strMachineFolder = getSettingsFileFull();
10482 strMachineFolder.stripFilename();
10483 mParent->saveMediaRegistry(config.mediaRegistry,
10484 getId(), // only media with registry ID == machine UUID
10485 strMachineFolder);
10486 // this throws HRESULT
10487 }
10488
10489 // save snapshots
10490 rc = saveAllSnapshots(config);
10491 if (FAILED(rc)) throw rc;
10492}
10493
10494/**
10495 * Saves all snapshots of the machine into the given machine config file. Called
10496 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10497 * @param config
10498 * @return
10499 */
10500HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10501{
10502 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10503
10504 HRESULT rc = S_OK;
10505
10506 try
10507 {
10508 config.llFirstSnapshot.clear();
10509
10510 if (mData->mFirstSnapshot)
10511 {
10512 settings::Snapshot snapNew;
10513 config.llFirstSnapshot.push_back(snapNew);
10514
10515 // get reference to the fresh copy of the snapshot on the list and
10516 // work on that copy directly to avoid excessive copying later
10517 settings::Snapshot &snap = config.llFirstSnapshot.front();
10518
10519 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10520 if (FAILED(rc)) throw rc;
10521 }
10522
10523// if (mType == IsSessionMachine)
10524// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10525
10526 }
10527 catch (HRESULT err)
10528 {
10529 /* we assume that error info is set by the thrower */
10530 rc = err;
10531 }
10532 catch (...)
10533 {
10534 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10535 }
10536
10537 return rc;
10538}
10539
10540/**
10541 * Saves the VM hardware configuration. It is assumed that the
10542 * given node is empty.
10543 *
10544 * @param data Reference to the settings object for the hardware config.
10545 * @param pDbg Pointer to the settings object for the debugging config
10546 * which happens to live in mHWData.
10547 * @param pAutostart Pointer to the settings object for the autostart config
10548 * which happens to live in mHWData.
10549 */
10550HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10551 settings::Autostart *pAutostart)
10552{
10553 HRESULT rc = S_OK;
10554
10555 try
10556 {
10557 /* The hardware version attribute (optional).
10558 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10559 if ( mHWData->mHWVersion == "1"
10560 && mSSData->strStateFilePath.isEmpty()
10561 )
10562 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. */
10563
10564 data.strVersion = mHWData->mHWVersion;
10565 data.uuid = mHWData->mHardwareUUID;
10566
10567 // CPU
10568 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10569 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10570 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10571 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10572 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10573 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10574 data.fPAE = !!mHWData->mPAEEnabled;
10575 data.enmLongMode = mHWData->mLongMode;
10576 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10577 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10578
10579 /* Standard and Extended CPUID leafs. */
10580 data.llCpuIdLeafs.clear();
10581 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10582 {
10583 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10584 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10585 }
10586 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10587 {
10588 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10589 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10590 }
10591
10592 data.cCPUs = mHWData->mCPUCount;
10593 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10594 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10595
10596 data.llCpus.clear();
10597 if (data.fCpuHotPlug)
10598 {
10599 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10600 {
10601 if (mHWData->mCPUAttached[idx])
10602 {
10603 settings::Cpu cpu;
10604 cpu.ulId = idx;
10605 data.llCpus.push_back(cpu);
10606 }
10607 }
10608 }
10609
10610 // memory
10611 data.ulMemorySizeMB = mHWData->mMemorySize;
10612 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10613
10614 // firmware
10615 data.firmwareType = mHWData->mFirmwareType;
10616
10617 // HID
10618 data.pointingHIDType = mHWData->mPointingHIDType;
10619 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10620
10621 // chipset
10622 data.chipsetType = mHWData->mChipsetType;
10623
10624 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10625
10626 // HPET
10627 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10628
10629 // boot order
10630 data.mapBootOrder.clear();
10631 for (size_t i = 0;
10632 i < RT_ELEMENTS(mHWData->mBootOrder);
10633 ++i)
10634 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10635
10636 // display
10637 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10638 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10639 data.cMonitors = mHWData->mMonitorCount;
10640 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10641 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10642 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10643 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10644 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10645 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10646 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10647 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10648 {
10649 if (mHWData->maVideoCaptureScreens[i])
10650 ASMBitSet(&data.u64VideoCaptureScreens, i);
10651 else
10652 ASMBitClear(&data.u64VideoCaptureScreens, i);
10653 }
10654 /* store relative video capture file if possible */
10655 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10656
10657 /* VRDEServer settings (optional) */
10658 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10659 if (FAILED(rc)) throw rc;
10660
10661 /* BIOS (required) */
10662 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10663 if (FAILED(rc)) throw rc;
10664
10665 /* USB Controller (required) */
10666 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10667 it != mUSBControllers->end();
10668 ++it)
10669 {
10670 ComObjPtr<USBController> ctrl = *it;
10671 settings::USBController settingsCtrl;
10672
10673 settingsCtrl.strName = ctrl->i_getName();
10674 settingsCtrl.enmType = ctrl->i_getControllerType();
10675
10676 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10677 }
10678
10679 /* USB device filters (required) */
10680 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10681 if (FAILED(rc)) throw rc;
10682
10683 /* Network adapters (required) */
10684 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10685 data.llNetworkAdapters.clear();
10686 /* Write out only the nominal number of network adapters for this
10687 * chipset type. Since Machine::commit() hasn't been called there
10688 * may be extra NIC settings in the vector. */
10689 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10690 {
10691 settings::NetworkAdapter nic;
10692 nic.ulSlot = slot;
10693 /* paranoia check... must not be NULL, but must not crash either. */
10694 if (mNetworkAdapters[slot])
10695 {
10696 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10697 if (FAILED(rc)) throw rc;
10698
10699 data.llNetworkAdapters.push_back(nic);
10700 }
10701 }
10702
10703 /* Serial ports */
10704 data.llSerialPorts.clear();
10705 for (ULONG slot = 0;
10706 slot < RT_ELEMENTS(mSerialPorts);
10707 ++slot)
10708 {
10709 settings::SerialPort s;
10710 s.ulSlot = slot;
10711 rc = mSerialPorts[slot]->i_saveSettings(s);
10712 if (FAILED(rc)) return rc;
10713
10714 data.llSerialPorts.push_back(s);
10715 }
10716
10717 /* Parallel ports */
10718 data.llParallelPorts.clear();
10719 for (ULONG slot = 0;
10720 slot < RT_ELEMENTS(mParallelPorts);
10721 ++slot)
10722 {
10723 settings::ParallelPort p;
10724 p.ulSlot = slot;
10725 rc = mParallelPorts[slot]->i_saveSettings(p);
10726 if (FAILED(rc)) return rc;
10727
10728 data.llParallelPorts.push_back(p);
10729 }
10730
10731 /* Audio adapter */
10732 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10733 if (FAILED(rc)) return rc;
10734
10735 /* Shared folders */
10736 data.llSharedFolders.clear();
10737 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10738 it != mHWData->mSharedFolders.end();
10739 ++it)
10740 {
10741 SharedFolder *pSF = *it;
10742 AutoCaller sfCaller(pSF);
10743 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10744 settings::SharedFolder sf;
10745 sf.strName = pSF->getName();
10746 sf.strHostPath = pSF->getHostPath();
10747 sf.fWritable = !!pSF->isWritable();
10748 sf.fAutoMount = !!pSF->isAutoMounted();
10749
10750 data.llSharedFolders.push_back(sf);
10751 }
10752
10753 // clipboard
10754 data.clipboardMode = mHWData->mClipboardMode;
10755
10756 // drag'n'drop
10757 data.dragAndDropMode = mHWData->mDragAndDropMode;
10758
10759 /* Guest */
10760 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10761
10762 // IO settings
10763 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10764 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10765
10766 /* BandwidthControl (required) */
10767 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10768 if (FAILED(rc)) throw rc;
10769
10770 /* Host PCI devices */
10771 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10772 it != mHWData->mPCIDeviceAssignments.end();
10773 ++it)
10774 {
10775 ComObjPtr<PCIDeviceAttachment> pda = *it;
10776 settings::HostPCIDeviceAttachment hpda;
10777
10778 rc = pda->saveSettings(hpda);
10779 if (FAILED(rc)) throw rc;
10780
10781 data.pciAttachments.push_back(hpda);
10782 }
10783
10784
10785 // guest properties
10786 data.llGuestProperties.clear();
10787#ifdef VBOX_WITH_GUEST_PROPS
10788 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10789 it != mHWData->mGuestProperties.end();
10790 ++it)
10791 {
10792 HWData::GuestProperty property = it->second;
10793
10794 /* Remove transient guest properties at shutdown unless we
10795 * are saving state */
10796 if ( ( mData->mMachineState == MachineState_PoweredOff
10797 || mData->mMachineState == MachineState_Aborted
10798 || mData->mMachineState == MachineState_Teleported)
10799 && ( property.mFlags & guestProp::TRANSIENT
10800 || property.mFlags & guestProp::TRANSRESET))
10801 continue;
10802 settings::GuestProperty prop;
10803 prop.strName = it->first;
10804 prop.strValue = property.strValue;
10805 prop.timestamp = property.mTimestamp;
10806 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10807 guestProp::writeFlags(property.mFlags, szFlags);
10808 prop.strFlags = szFlags;
10809
10810 data.llGuestProperties.push_back(prop);
10811 }
10812
10813 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10814 /* I presume this doesn't require a backup(). */
10815 mData->mGuestPropertiesModified = FALSE;
10816#endif /* VBOX_WITH_GUEST_PROPS defined */
10817
10818 *pDbg = mHWData->mDebugging;
10819 *pAutostart = mHWData->mAutostart;
10820
10821 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10822 }
10823 catch(std::bad_alloc &)
10824 {
10825 return E_OUTOFMEMORY;
10826 }
10827
10828 AssertComRC(rc);
10829 return rc;
10830}
10831
10832/**
10833 * Saves the storage controller configuration.
10834 *
10835 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10836 */
10837HRESULT Machine::saveStorageControllers(settings::Storage &data)
10838{
10839 data.llStorageControllers.clear();
10840
10841 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10842 it != mStorageControllers->end();
10843 ++it)
10844 {
10845 HRESULT rc;
10846 ComObjPtr<StorageController> pCtl = *it;
10847
10848 settings::StorageController ctl;
10849 ctl.strName = pCtl->i_getName();
10850 ctl.controllerType = pCtl->i_getControllerType();
10851 ctl.storageBus = pCtl->i_getStorageBus();
10852 ctl.ulInstance = pCtl->i_getInstance();
10853 ctl.fBootable = pCtl->i_getBootable();
10854
10855 /* Save the port count. */
10856 ULONG portCount;
10857 rc = pCtl->COMGETTER(PortCount)(&portCount);
10858 ComAssertComRCRet(rc, rc);
10859 ctl.ulPortCount = portCount;
10860
10861 /* Save fUseHostIOCache */
10862 BOOL fUseHostIOCache;
10863 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10864 ComAssertComRCRet(rc, rc);
10865 ctl.fUseHostIOCache = !!fUseHostIOCache;
10866
10867 /* Save IDE emulation settings. */
10868 if (ctl.controllerType == StorageControllerType_IntelAhci)
10869 {
10870 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10871 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10872 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10873 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10874 )
10875 ComAssertComRCRet(rc, rc);
10876 }
10877
10878 /* save the devices now. */
10879 rc = saveStorageDevices(pCtl, ctl);
10880 ComAssertComRCRet(rc, rc);
10881
10882 data.llStorageControllers.push_back(ctl);
10883 }
10884
10885 return S_OK;
10886}
10887
10888/**
10889 * Saves the hard disk configuration.
10890 */
10891HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10892 settings::StorageController &data)
10893{
10894 MediaData::AttachmentList atts;
10895
10896 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->i_getName()).raw(), atts);
10897 if (FAILED(rc)) return rc;
10898
10899 data.llAttachedDevices.clear();
10900 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10901 it != atts.end();
10902 ++it)
10903 {
10904 settings::AttachedDevice dev;
10905
10906 MediumAttachment *pAttach = *it;
10907 Medium *pMedium = pAttach->i_getMedium();
10908
10909 dev.deviceType = pAttach->i_getType();
10910 dev.lPort = pAttach->i_getPort();
10911 dev.lDevice = pAttach->i_getDevice();
10912 dev.fPassThrough = pAttach->i_getPassthrough();
10913 dev.fHotPluggable = pAttach->i_getHotPluggable();
10914 if (pMedium)
10915 {
10916 if (pMedium->i_isHostDrive())
10917 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10918 else
10919 dev.uuid = pMedium->i_getId();
10920 dev.fTempEject = pAttach->i_getTempEject();
10921 dev.fNonRotational = pAttach->i_getNonRotational();
10922 dev.fDiscard = pAttach->i_getDiscard();
10923 }
10924
10925 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10926
10927 data.llAttachedDevices.push_back(dev);
10928 }
10929
10930 return S_OK;
10931}
10932
10933/**
10934 * Saves machine state settings as defined by aFlags
10935 * (SaveSTS_* values).
10936 *
10937 * @param aFlags Combination of SaveSTS_* flags.
10938 *
10939 * @note Locks objects for writing.
10940 */
10941HRESULT Machine::saveStateSettings(int aFlags)
10942{
10943 if (aFlags == 0)
10944 return S_OK;
10945
10946 AutoCaller autoCaller(this);
10947 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10948
10949 /* This object's write lock is also necessary to serialize file access
10950 * (prevent concurrent reads and writes) */
10951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10952
10953 HRESULT rc = S_OK;
10954
10955 Assert(mData->pMachineConfigFile);
10956
10957 try
10958 {
10959 if (aFlags & SaveSTS_CurStateModified)
10960 mData->pMachineConfigFile->fCurrentStateModified = true;
10961
10962 if (aFlags & SaveSTS_StateFilePath)
10963 {
10964 if (!mSSData->strStateFilePath.isEmpty())
10965 /* try to make the file name relative to the settings file dir */
10966 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10967 else
10968 mData->pMachineConfigFile->strStateFile.setNull();
10969 }
10970
10971 if (aFlags & SaveSTS_StateTimeStamp)
10972 {
10973 Assert( mData->mMachineState != MachineState_Aborted
10974 || mSSData->strStateFilePath.isEmpty());
10975
10976 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10977
10978 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10979//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10980 }
10981
10982 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10983 }
10984 catch (...)
10985 {
10986 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10987 }
10988
10989 return rc;
10990}
10991
10992/**
10993 * Ensures that the given medium is added to a media registry. If this machine
10994 * was created with 4.0 or later, then the machine registry is used. Otherwise
10995 * the global VirtualBox media registry is used.
10996 *
10997 * Caller must NOT hold machine lock, media tree or any medium locks!
10998 *
10999 * @param pMedium
11000 */
11001void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11002{
11003 /* Paranoia checks: do not hold machine or media tree locks. */
11004 AssertReturnVoid(!isWriteLockOnCurrentThread());
11005 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11006
11007 ComObjPtr<Medium> pBase;
11008 {
11009 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11010 pBase = pMedium->i_getBase();
11011 }
11012
11013 /* Paranoia checks: do not hold medium locks. */
11014 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11015 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11016
11017 // decide which medium registry to use now that the medium is attached:
11018 Guid uuid;
11019 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
11020 // machine XML is VirtualBox 4.0 or higher:
11021 uuid = getId(); // machine UUID
11022 else
11023 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
11024
11025 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
11026 mParent->markRegistryModified(uuid);
11027
11028 /* For more complex hard disk structures it can happen that the base
11029 * medium isn't yet associated with any medium registry. Do that now. */
11030 if (pMedium != pBase)
11031 {
11032 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
11033 mParent->markRegistryModified(uuid);
11034 }
11035}
11036
11037/**
11038 * Creates differencing hard disks for all normal hard disks attached to this
11039 * machine and a new set of attachments to refer to created disks.
11040 *
11041 * Used when taking a snapshot or when deleting the current state. Gets called
11042 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11043 *
11044 * This method assumes that mMediaData contains the original hard disk attachments
11045 * it needs to create diffs for. On success, these attachments will be replaced
11046 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
11047 * called to delete created diffs which will also rollback mMediaData and restore
11048 * whatever was backed up before calling this method.
11049 *
11050 * Attachments with non-normal hard disks are left as is.
11051 *
11052 * If @a aOnline is @c false then the original hard disks that require implicit
11053 * diffs will be locked for reading. Otherwise it is assumed that they are
11054 * already locked for writing (when the VM was started). Note that in the latter
11055 * case it is responsibility of the caller to lock the newly created diffs for
11056 * writing if this method succeeds.
11057 *
11058 * @param aProgress Progress object to run (must contain at least as
11059 * many operations left as the number of hard disks
11060 * attached).
11061 * @param aOnline Whether the VM was online prior to this operation.
11062 *
11063 * @note The progress object is not marked as completed, neither on success nor
11064 * on failure. This is a responsibility of the caller.
11065 *
11066 * @note Locks this object and the media tree for writing.
11067 */
11068HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
11069 ULONG aWeight,
11070 bool aOnline)
11071{
11072 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11073
11074 AutoCaller autoCaller(this);
11075 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11076
11077 AutoMultiWriteLock2 alock(this->lockHandle(),
11078 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11079
11080 /* must be in a protective state because we release the lock below */
11081 AssertReturn( mData->mMachineState == MachineState_Saving
11082 || mData->mMachineState == MachineState_LiveSnapshotting
11083 || mData->mMachineState == MachineState_RestoringSnapshot
11084 || mData->mMachineState == MachineState_DeletingSnapshot
11085 , E_FAIL);
11086
11087 HRESULT rc = S_OK;
11088
11089 // use appropriate locked media map (online or offline)
11090 MediumLockListMap lockedMediaOffline;
11091 MediumLockListMap *lockedMediaMap;
11092 if (aOnline)
11093 lockedMediaMap = &mData->mSession.mLockedMedia;
11094 else
11095 lockedMediaMap = &lockedMediaOffline;
11096
11097 try
11098 {
11099 if (!aOnline)
11100 {
11101 /* lock all attached hard disks early to detect "in use"
11102 * situations before creating actual diffs */
11103 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11104 it != mMediaData->mAttachments.end();
11105 ++it)
11106 {
11107 MediumAttachment* pAtt = *it;
11108 if (pAtt->i_getType() == DeviceType_HardDisk)
11109 {
11110 Medium* pMedium = pAtt->i_getMedium();
11111 Assert(pMedium);
11112
11113 MediumLockList *pMediumLockList(new MediumLockList());
11114 alock.release();
11115 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11116 false /* fMediumLockWrite */,
11117 NULL,
11118 *pMediumLockList);
11119 alock.acquire();
11120 if (FAILED(rc))
11121 {
11122 delete pMediumLockList;
11123 throw rc;
11124 }
11125 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11126 if (FAILED(rc))
11127 {
11128 throw setError(rc,
11129 tr("Collecting locking information for all attached media failed"));
11130 }
11131 }
11132 }
11133
11134 /* Now lock all media. If this fails, nothing is locked. */
11135 alock.release();
11136 rc = lockedMediaMap->Lock();
11137 alock.acquire();
11138 if (FAILED(rc))
11139 {
11140 throw setError(rc,
11141 tr("Locking of attached media failed"));
11142 }
11143 }
11144
11145 /* remember the current list (note that we don't use backup() since
11146 * mMediaData may be already backed up) */
11147 MediaData::AttachmentList atts = mMediaData->mAttachments;
11148
11149 /* start from scratch */
11150 mMediaData->mAttachments.clear();
11151
11152 /* go through remembered attachments and create diffs for normal hard
11153 * disks and attach them */
11154 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11155 it != atts.end();
11156 ++it)
11157 {
11158 MediumAttachment* pAtt = *it;
11159
11160 DeviceType_T devType = pAtt->i_getType();
11161 Medium* pMedium = pAtt->i_getMedium();
11162
11163 if ( devType != DeviceType_HardDisk
11164 || pMedium == NULL
11165 || pMedium->i_getType() != MediumType_Normal)
11166 {
11167 /* copy the attachment as is */
11168
11169 /** @todo the progress object created in Console::TakeSnaphot
11170 * only expects operations for hard disks. Later other
11171 * device types need to show up in the progress as well. */
11172 if (devType == DeviceType_HardDisk)
11173 {
11174 if (pMedium == NULL)
11175 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11176 aWeight); // weight
11177 else
11178 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11179 pMedium->i_getBase()->i_getName().c_str()).raw(),
11180 aWeight); // weight
11181 }
11182
11183 mMediaData->mAttachments.push_back(pAtt);
11184 continue;
11185 }
11186
11187 /* need a diff */
11188 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11189 pMedium->i_getBase()->i_getName().c_str()).raw(),
11190 aWeight); // weight
11191
11192 Utf8Str strFullSnapshotFolder;
11193 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11194
11195 ComObjPtr<Medium> diff;
11196 diff.createObject();
11197 // store the diff in the same registry as the parent
11198 // (this cannot fail here because we can't create implicit diffs for
11199 // unregistered images)
11200 Guid uuidRegistryParent;
11201 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11202 Assert(fInRegistry); NOREF(fInRegistry);
11203 rc = diff->init(mParent,
11204 pMedium->i_getPreferredDiffFormat(),
11205 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11206 uuidRegistryParent);
11207 if (FAILED(rc)) throw rc;
11208
11209 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11210 * the push_back? Looks like we're going to release medium with the
11211 * wrong kind of lock (general issue with if we fail anywhere at all)
11212 * and an orphaned VDI in the snapshots folder. */
11213
11214 /* update the appropriate lock list */
11215 MediumLockList *pMediumLockList;
11216 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11217 AssertComRCThrowRC(rc);
11218 if (aOnline)
11219 {
11220 alock.release();
11221 /* The currently attached medium will be read-only, change
11222 * the lock type to read. */
11223 rc = pMediumLockList->Update(pMedium, false);
11224 alock.acquire();
11225 AssertComRCThrowRC(rc);
11226 }
11227
11228 /* release the locks before the potentially lengthy operation */
11229 alock.release();
11230 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
11231 pMediumLockList,
11232 NULL /* aProgress */,
11233 true /* aWait */);
11234 alock.acquire();
11235 if (FAILED(rc)) throw rc;
11236
11237 /* actual lock list update is done in Medium::commitMedia */
11238
11239 rc = diff->i_addBackReference(mData->mUuid);
11240 AssertComRCThrowRC(rc);
11241
11242 /* add a new attachment */
11243 ComObjPtr<MediumAttachment> attachment;
11244 attachment.createObject();
11245 rc = attachment->init(this,
11246 diff,
11247 pAtt->i_getControllerName(),
11248 pAtt->i_getPort(),
11249 pAtt->i_getDevice(),
11250 DeviceType_HardDisk,
11251 true /* aImplicit */,
11252 false /* aPassthrough */,
11253 false /* aTempEject */,
11254 pAtt->i_getNonRotational(),
11255 pAtt->i_getDiscard(),
11256 pAtt->i_getHotPluggable(),
11257 pAtt->i_getBandwidthGroup());
11258 if (FAILED(rc)) throw rc;
11259
11260 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11261 AssertComRCThrowRC(rc);
11262 mMediaData->mAttachments.push_back(attachment);
11263 }
11264 }
11265 catch (HRESULT aRC) { rc = aRC; }
11266
11267 /* unlock all hard disks we locked when there is no VM */
11268 if (!aOnline)
11269 {
11270 ErrorInfoKeeper eik;
11271
11272 HRESULT rc1 = lockedMediaMap->Clear();
11273 AssertComRC(rc1);
11274 }
11275
11276 return rc;
11277}
11278
11279/**
11280 * Deletes implicit differencing hard disks created either by
11281 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11282 *
11283 * Note that to delete hard disks created by #AttachDevice() this method is
11284 * called from #fixupMedia() when the changes are rolled back.
11285 *
11286 * @note Locks this object and the media tree for writing.
11287 */
11288HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11289{
11290 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11291
11292 AutoCaller autoCaller(this);
11293 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11294
11295 AutoMultiWriteLock2 alock(this->lockHandle(),
11296 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11297
11298 /* We absolutely must have backed up state. */
11299 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11300
11301 /* Check if there are any implicitly created diff images. */
11302 bool fImplicitDiffs = false;
11303 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11304 it != mMediaData->mAttachments.end();
11305 ++it)
11306 {
11307 const ComObjPtr<MediumAttachment> &pAtt = *it;
11308 if (pAtt->i_isImplicit())
11309 {
11310 fImplicitDiffs = true;
11311 break;
11312 }
11313 }
11314 /* If there is nothing to do, leave early. This saves lots of image locking
11315 * effort. It also avoids a MachineStateChanged event without real reason.
11316 * This is important e.g. when loading a VM config, because there should be
11317 * no events. Otherwise API clients can become thoroughly confused for
11318 * inaccessible VMs (the code for loading VM configs uses this method for
11319 * cleanup if the config makes no sense), as they take such events as an
11320 * indication that the VM is alive, and they would force the VM config to
11321 * be reread, leading to an endless loop. */
11322 if (!fImplicitDiffs)
11323 return S_OK;
11324
11325 HRESULT rc = S_OK;
11326 MachineState_T oldState = mData->mMachineState;
11327
11328 /* will release the lock before the potentially lengthy operation,
11329 * so protect with the special state (unless already protected) */
11330 if ( oldState != MachineState_Saving
11331 && oldState != MachineState_LiveSnapshotting
11332 && oldState != MachineState_RestoringSnapshot
11333 && oldState != MachineState_DeletingSnapshot
11334 && oldState != MachineState_DeletingSnapshotOnline
11335 && oldState != MachineState_DeletingSnapshotPaused
11336 )
11337 setMachineState(MachineState_SettingUp);
11338
11339 // use appropriate locked media map (online or offline)
11340 MediumLockListMap lockedMediaOffline;
11341 MediumLockListMap *lockedMediaMap;
11342 if (aOnline)
11343 lockedMediaMap = &mData->mSession.mLockedMedia;
11344 else
11345 lockedMediaMap = &lockedMediaOffline;
11346
11347 try
11348 {
11349 if (!aOnline)
11350 {
11351 /* lock all attached hard disks early to detect "in use"
11352 * situations before deleting actual diffs */
11353 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11354 it != mMediaData->mAttachments.end();
11355 ++it)
11356 {
11357 MediumAttachment* pAtt = *it;
11358 if (pAtt->i_getType() == DeviceType_HardDisk)
11359 {
11360 Medium* pMedium = pAtt->i_getMedium();
11361 Assert(pMedium);
11362
11363 MediumLockList *pMediumLockList(new MediumLockList());
11364 alock.release();
11365 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11366 false /* fMediumLockWrite */,
11367 NULL,
11368 *pMediumLockList);
11369 alock.acquire();
11370
11371 if (FAILED(rc))
11372 {
11373 delete pMediumLockList;
11374 throw rc;
11375 }
11376
11377 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11378 if (FAILED(rc))
11379 throw rc;
11380 }
11381 }
11382
11383 if (FAILED(rc))
11384 throw rc;
11385 } // end of offline
11386
11387 /* Lock lists are now up to date and include implicitly created media */
11388
11389 /* Go through remembered attachments and delete all implicitly created
11390 * diffs and fix up the attachment information */
11391 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11392 MediaData::AttachmentList implicitAtts;
11393 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11394 it != mMediaData->mAttachments.end();
11395 ++it)
11396 {
11397 ComObjPtr<MediumAttachment> pAtt = *it;
11398 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11399 if (pMedium.isNull())
11400 continue;
11401
11402 // Implicit attachments go on the list for deletion and back references are removed.
11403 if (pAtt->i_isImplicit())
11404 {
11405 /* Deassociate and mark for deletion */
11406 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11407 rc = pMedium->i_removeBackReference(mData->mUuid);
11408 if (FAILED(rc))
11409 throw rc;
11410 implicitAtts.push_back(pAtt);
11411 continue;
11412 }
11413
11414 /* Was this medium attached before? */
11415 if (!findAttachment(oldAtts, pMedium))
11416 {
11417 /* no: de-associate */
11418 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11419 rc = pMedium->i_removeBackReference(mData->mUuid);
11420 if (FAILED(rc))
11421 throw rc;
11422 continue;
11423 }
11424 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11425 }
11426
11427 /* If there are implicit attachments to delete, throw away the lock
11428 * map contents (which will unlock all media) since the medium
11429 * attachments will be rolled back. Below we need to completely
11430 * recreate the lock map anyway since it is infinitely complex to
11431 * do this incrementally (would need reconstructing each attachment
11432 * change, which would be extremely hairy). */
11433 if (implicitAtts.size() != 0)
11434 {
11435 ErrorInfoKeeper eik;
11436
11437 HRESULT rc1 = lockedMediaMap->Clear();
11438 AssertComRC(rc1);
11439 }
11440
11441 /* rollback hard disk changes */
11442 mMediaData.rollback();
11443
11444 MultiResult mrc(S_OK);
11445
11446 // Delete unused implicit diffs.
11447 if (implicitAtts.size() != 0)
11448 {
11449 alock.release();
11450
11451 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11452 it != implicitAtts.end();
11453 ++it)
11454 {
11455 // Remove medium associated with this attachment.
11456 ComObjPtr<MediumAttachment> pAtt = *it;
11457 Assert(pAtt);
11458 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11459 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11460 Assert(pMedium);
11461
11462 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11463 // continue on delete failure, just collect error messages
11464 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(), pMedium->i_getLocationFull().c_str() ));
11465 mrc = rc;
11466 }
11467
11468 alock.acquire();
11469
11470 /* if there is a VM recreate media lock map as mentioned above,
11471 * otherwise it is a waste of time and we leave things unlocked */
11472 if (aOnline)
11473 {
11474 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11475 /* must never be NULL, but better safe than sorry */
11476 if (!pMachine.isNull())
11477 {
11478 alock.release();
11479 rc = mData->mSession.mMachine->lockMedia();
11480 alock.acquire();
11481 if (FAILED(rc))
11482 throw rc;
11483 }
11484 }
11485 }
11486 }
11487 catch (HRESULT aRC) {rc = aRC;}
11488
11489 if (mData->mMachineState == MachineState_SettingUp)
11490 setMachineState(oldState);
11491
11492 /* unlock all hard disks we locked when there is no VM */
11493 if (!aOnline)
11494 {
11495 ErrorInfoKeeper eik;
11496
11497 HRESULT rc1 = lockedMediaMap->Clear();
11498 AssertComRC(rc1);
11499 }
11500
11501 return rc;
11502}
11503
11504
11505/**
11506 * Looks through the given list of media attachments for one with the given parameters
11507 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11508 * can be searched as well if needed.
11509 *
11510 * @param list
11511 * @param aControllerName
11512 * @param aControllerPort
11513 * @param aDevice
11514 * @return
11515 */
11516MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11517 IN_BSTR aControllerName,
11518 LONG aControllerPort,
11519 LONG aDevice)
11520{
11521 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11522 it != ll.end();
11523 ++it)
11524 {
11525 MediumAttachment *pAttach = *it;
11526 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11527 return pAttach;
11528 }
11529
11530 return NULL;
11531}
11532
11533/**
11534 * Looks through the given list of media attachments for one with the given parameters
11535 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11536 * can be searched as well if needed.
11537 *
11538 * @param list
11539 * @param aControllerName
11540 * @param aControllerPort
11541 * @param aDevice
11542 * @return
11543 */
11544MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11545 ComObjPtr<Medium> pMedium)
11546{
11547 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11548 it != ll.end();
11549 ++it)
11550 {
11551 MediumAttachment *pAttach = *it;
11552 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11553 if (pMediumThis == pMedium)
11554 return pAttach;
11555 }
11556
11557 return NULL;
11558}
11559
11560/**
11561 * Looks through the given list of media attachments for one with the given parameters
11562 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11563 * can be searched as well if needed.
11564 *
11565 * @param list
11566 * @param aControllerName
11567 * @param aControllerPort
11568 * @param aDevice
11569 * @return
11570 */
11571MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11572 Guid &id)
11573{
11574 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11575 it != ll.end();
11576 ++it)
11577 {
11578 MediumAttachment *pAttach = *it;
11579 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11580 if (pMediumThis->i_getId() == id)
11581 return pAttach;
11582 }
11583
11584 return NULL;
11585}
11586
11587/**
11588 * Main implementation for Machine::DetachDevice. This also gets called
11589 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11590 *
11591 * @param pAttach Medium attachment to detach.
11592 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11593 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11594 * @return
11595 */
11596HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11597 AutoWriteLock &writeLock,
11598 Snapshot *pSnapshot)
11599{
11600 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11601 DeviceType_T mediumType = pAttach->i_getType();
11602
11603 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11604
11605 if (pAttach->i_isImplicit())
11606 {
11607 /* attempt to implicitly delete the implicitly created diff */
11608
11609 /// @todo move the implicit flag from MediumAttachment to Medium
11610 /// and forbid any hard disk operation when it is implicit. Or maybe
11611 /// a special media state for it to make it even more simple.
11612
11613 Assert(mMediaData.isBackedUp());
11614
11615 /* will release the lock before the potentially lengthy operation, so
11616 * protect with the special state */
11617 MachineState_T oldState = mData->mMachineState;
11618 setMachineState(MachineState_SettingUp);
11619
11620 writeLock.release();
11621
11622 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11623 true /*aWait*/);
11624
11625 writeLock.acquire();
11626
11627 setMachineState(oldState);
11628
11629 if (FAILED(rc)) return rc;
11630 }
11631
11632 setModified(IsModified_Storage);
11633 mMediaData.backup();
11634 mMediaData->mAttachments.remove(pAttach);
11635
11636 if (!oldmedium.isNull())
11637 {
11638 // if this is from a snapshot, do not defer detachment to commitMedia()
11639 if (pSnapshot)
11640 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->getId());
11641 // else if non-hard disk media, do not defer detachment to commitMedia() either
11642 else if (mediumType != DeviceType_HardDisk)
11643 oldmedium->i_removeBackReference(mData->mUuid);
11644 }
11645
11646 return S_OK;
11647}
11648
11649/**
11650 * Goes thru all media of the given list and
11651 *
11652 * 1) calls detachDevice() on each of them for this machine and
11653 * 2) adds all Medium objects found in the process to the given list,
11654 * depending on cleanupMode.
11655 *
11656 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11657 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11658 * media to the list.
11659 *
11660 * This gets called from Machine::Unregister, both for the actual Machine and
11661 * the SnapshotMachine objects that might be found in the snapshots.
11662 *
11663 * Requires caller and locking. The machine lock must be passed in because it
11664 * will be passed on to detachDevice which needs it for temporary unlocking.
11665 *
11666 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11667 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11668 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11669 * otherwise no media get added.
11670 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11671 * @return
11672 */
11673HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11674 Snapshot *pSnapshot,
11675 CleanupMode_T cleanupMode,
11676 MediaList &llMedia)
11677{
11678 Assert(isWriteLockOnCurrentThread());
11679
11680 HRESULT rc;
11681
11682 // make a temporary list because detachDevice invalidates iterators into
11683 // mMediaData->mAttachments
11684 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11685
11686 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11687 it != llAttachments2.end();
11688 ++it)
11689 {
11690 ComObjPtr<MediumAttachment> &pAttach = *it;
11691 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11692
11693 if (!pMedium.isNull())
11694 {
11695 AutoCaller mac(pMedium);
11696 if (FAILED(mac.rc())) return mac.rc();
11697 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11698 DeviceType_T devType = pMedium->i_getDeviceType();
11699 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11700 && devType == DeviceType_HardDisk)
11701 || (cleanupMode == CleanupMode_Full)
11702 )
11703 {
11704 llMedia.push_back(pMedium);
11705 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11706 /*
11707 * Search for medias which are not attached to any machine, but
11708 * in the chain to an attached disk. Mediums are only consided
11709 * if they are:
11710 * - have only one child
11711 * - no references to any machines
11712 * - are of normal medium type
11713 */
11714 while (!pParent.isNull())
11715 {
11716 AutoCaller mac1(pParent);
11717 if (FAILED(mac1.rc())) return mac1.rc();
11718 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11719 if (pParent->i_getChildren().size() == 1)
11720 {
11721 if ( pParent->i_getMachineBackRefCount() == 0
11722 && pParent->i_getType() == MediumType_Normal
11723 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11724 llMedia.push_back(pParent);
11725 }
11726 else
11727 break;
11728 pParent = pParent->i_getParent();
11729 }
11730 }
11731 }
11732
11733 // real machine: then we need to use the proper method
11734 rc = detachDevice(pAttach, writeLock, pSnapshot);
11735
11736 if (FAILED(rc))
11737 return rc;
11738 }
11739
11740 return S_OK;
11741}
11742
11743/**
11744 * Perform deferred hard disk detachments.
11745 *
11746 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11747 * backed up).
11748 *
11749 * If @a aOnline is @c true then this method will also unlock the old hard disks
11750 * for which the new implicit diffs were created and will lock these new diffs for
11751 * writing.
11752 *
11753 * @param aOnline Whether the VM was online prior to this operation.
11754 *
11755 * @note Locks this object for writing!
11756 */
11757void Machine::commitMedia(bool aOnline /*= false*/)
11758{
11759 AutoCaller autoCaller(this);
11760 AssertComRCReturnVoid(autoCaller.rc());
11761
11762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11763
11764 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11765
11766 HRESULT rc = S_OK;
11767
11768 /* no attach/detach operations -- nothing to do */
11769 if (!mMediaData.isBackedUp())
11770 return;
11771
11772 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11773 bool fMediaNeedsLocking = false;
11774
11775 /* enumerate new attachments */
11776 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11777 it != mMediaData->mAttachments.end();
11778 ++it)
11779 {
11780 MediumAttachment *pAttach = *it;
11781
11782 pAttach->i_commit();
11783
11784 Medium* pMedium = pAttach->i_getMedium();
11785 bool fImplicit = pAttach->i_isImplicit();
11786
11787 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11788 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11789 fImplicit));
11790
11791 /** @todo convert all this Machine-based voodoo to MediumAttachment
11792 * based commit logic. */
11793 if (fImplicit)
11794 {
11795 /* convert implicit attachment to normal */
11796 pAttach->i_setImplicit(false);
11797
11798 if ( aOnline
11799 && pMedium
11800 && pAttach->i_getType() == DeviceType_HardDisk
11801 )
11802 {
11803 ComObjPtr<Medium> parent = pMedium->i_getParent();
11804 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11805
11806 /* update the appropriate lock list */
11807 MediumLockList *pMediumLockList;
11808 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11809 AssertComRC(rc);
11810 if (pMediumLockList)
11811 {
11812 /* unlock if there's a need to change the locking */
11813 if (!fMediaNeedsLocking)
11814 {
11815 rc = mData->mSession.mLockedMedia.Unlock();
11816 AssertComRC(rc);
11817 fMediaNeedsLocking = true;
11818 }
11819 rc = pMediumLockList->Update(parent, false);
11820 AssertComRC(rc);
11821 rc = pMediumLockList->Append(pMedium, true);
11822 AssertComRC(rc);
11823 }
11824 }
11825
11826 continue;
11827 }
11828
11829 if (pMedium)
11830 {
11831 /* was this medium attached before? */
11832 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11833 oldIt != oldAtts.end();
11834 ++oldIt)
11835 {
11836 MediumAttachment *pOldAttach = *oldIt;
11837 if (pOldAttach->i_getMedium() == pMedium)
11838 {
11839 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11840
11841 /* yes: remove from old to avoid de-association */
11842 oldAtts.erase(oldIt);
11843 break;
11844 }
11845 }
11846 }
11847 }
11848
11849 /* enumerate remaining old attachments and de-associate from the
11850 * current machine state */
11851 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11852 it != oldAtts.end();
11853 ++it)
11854 {
11855 MediumAttachment *pAttach = *it;
11856 Medium* pMedium = pAttach->i_getMedium();
11857
11858 /* Detach only hard disks, since DVD/floppy media is detached
11859 * instantly in MountMedium. */
11860 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11861 {
11862 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11863
11864 /* now de-associate from the current machine state */
11865 rc = pMedium->i_removeBackReference(mData->mUuid);
11866 AssertComRC(rc);
11867
11868 if (aOnline)
11869 {
11870 /* unlock since medium is not used anymore */
11871 MediumLockList *pMediumLockList;
11872 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11873 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11874 {
11875 /* this happens for online snapshots, there the attachment
11876 * is changing, but only to a diff image created under
11877 * the old one, so there is no separate lock list */
11878 Assert(!pMediumLockList);
11879 }
11880 else
11881 {
11882 AssertComRC(rc);
11883 if (pMediumLockList)
11884 {
11885 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11886 AssertComRC(rc);
11887 }
11888 }
11889 }
11890 }
11891 }
11892
11893 /* take media locks again so that the locking state is consistent */
11894 if (fMediaNeedsLocking)
11895 {
11896 Assert(aOnline);
11897 rc = mData->mSession.mLockedMedia.Lock();
11898 AssertComRC(rc);
11899 }
11900
11901 /* commit the hard disk changes */
11902 mMediaData.commit();
11903
11904 if (isSessionMachine())
11905 {
11906 /*
11907 * Update the parent machine to point to the new owner.
11908 * This is necessary because the stored parent will point to the
11909 * session machine otherwise and cause crashes or errors later
11910 * when the session machine gets invalid.
11911 */
11912 /** @todo Change the MediumAttachment class to behave like any other
11913 * class in this regard by creating peer MediumAttachment
11914 * objects for session machines and share the data with the peer
11915 * machine.
11916 */
11917 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11918 it != mMediaData->mAttachments.end();
11919 ++it)
11920 {
11921 (*it)->i_updateParentMachine(mPeer);
11922 }
11923
11924 /* attach new data to the primary machine and reshare it */
11925 mPeer->mMediaData.attach(mMediaData);
11926 }
11927
11928 return;
11929}
11930
11931/**
11932 * Perform deferred deletion of implicitly created diffs.
11933 *
11934 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11935 * backed up).
11936 *
11937 * @note Locks this object for writing!
11938 */
11939void Machine::rollbackMedia()
11940{
11941 AutoCaller autoCaller(this);
11942 AssertComRCReturnVoid(autoCaller.rc());
11943
11944 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11945 LogFlowThisFunc(("Entering rollbackMedia\n"));
11946
11947 HRESULT rc = S_OK;
11948
11949 /* no attach/detach operations -- nothing to do */
11950 if (!mMediaData.isBackedUp())
11951 return;
11952
11953 /* enumerate new attachments */
11954 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11955 it != mMediaData->mAttachments.end();
11956 ++it)
11957 {
11958 MediumAttachment *pAttach = *it;
11959 /* Fix up the backrefs for DVD/floppy media. */
11960 if (pAttach->i_getType() != DeviceType_HardDisk)
11961 {
11962 Medium* pMedium = pAttach->i_getMedium();
11963 if (pMedium)
11964 {
11965 rc = pMedium->i_removeBackReference(mData->mUuid);
11966 AssertComRC(rc);
11967 }
11968 }
11969
11970 (*it)->i_rollback();
11971
11972 pAttach = *it;
11973 /* Fix up the backrefs for DVD/floppy media. */
11974 if (pAttach->i_getType() != DeviceType_HardDisk)
11975 {
11976 Medium* pMedium = pAttach->i_getMedium();
11977 if (pMedium)
11978 {
11979 rc = pMedium->i_addBackReference(mData->mUuid);
11980 AssertComRC(rc);
11981 }
11982 }
11983 }
11984
11985 /** @todo convert all this Machine-based voodoo to MediumAttachment
11986 * based rollback logic. */
11987 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11988
11989 return;
11990}
11991
11992/**
11993 * Returns true if the settings file is located in the directory named exactly
11994 * as the machine; this means, among other things, that the machine directory
11995 * should be auto-renamed.
11996 *
11997 * @param aSettingsDir if not NULL, the full machine settings file directory
11998 * name will be assigned there.
11999 *
12000 * @note Doesn't lock anything.
12001 * @note Not thread safe (must be called from this object's lock).
12002 */
12003bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12004{
12005 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12006 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12007 if (aSettingsDir)
12008 *aSettingsDir = strMachineDirName;
12009 strMachineDirName.stripPath(); // vmname
12010 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12011 strConfigFileOnly.stripPath() // vmname.vbox
12012 .stripSuffix(); // vmname
12013 /** @todo hack, make somehow use of ComposeMachineFilename */
12014 if (mUserData->s.fDirectoryIncludesUUID)
12015 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
12016
12017 AssertReturn(!strMachineDirName.isEmpty(), false);
12018 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12019
12020 return strMachineDirName == strConfigFileOnly;
12021}
12022
12023/**
12024 * Discards all changes to machine settings.
12025 *
12026 * @param aNotify Whether to notify the direct session about changes or not.
12027 *
12028 * @note Locks objects for writing!
12029 */
12030void Machine::rollback(bool aNotify)
12031{
12032 AutoCaller autoCaller(this);
12033 AssertComRCReturn(autoCaller.rc(), (void)0);
12034
12035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12036
12037 if (!mStorageControllers.isNull())
12038 {
12039 if (mStorageControllers.isBackedUp())
12040 {
12041 /* unitialize all new devices (absent in the backed up list). */
12042 StorageControllerList::const_iterator it = mStorageControllers->begin();
12043 StorageControllerList *backedList = mStorageControllers.backedUpData();
12044 while (it != mStorageControllers->end())
12045 {
12046 if ( std::find(backedList->begin(), backedList->end(), *it)
12047 == backedList->end()
12048 )
12049 {
12050 (*it)->uninit();
12051 }
12052 ++it;
12053 }
12054
12055 /* restore the list */
12056 mStorageControllers.rollback();
12057 }
12058
12059 /* rollback any changes to devices after restoring the list */
12060 if (mData->flModifications & IsModified_Storage)
12061 {
12062 StorageControllerList::const_iterator it = mStorageControllers->begin();
12063 while (it != mStorageControllers->end())
12064 {
12065 (*it)->i_rollback();
12066 ++it;
12067 }
12068 }
12069 }
12070
12071 if (!mUSBControllers.isNull())
12072 {
12073 if (mUSBControllers.isBackedUp())
12074 {
12075 /* unitialize all new devices (absent in the backed up list). */
12076 USBControllerList::const_iterator it = mUSBControllers->begin();
12077 USBControllerList *backedList = mUSBControllers.backedUpData();
12078 while (it != mUSBControllers->end())
12079 {
12080 if ( std::find(backedList->begin(), backedList->end(), *it)
12081 == backedList->end()
12082 )
12083 {
12084 (*it)->uninit();
12085 }
12086 ++it;
12087 }
12088
12089 /* restore the list */
12090 mUSBControllers.rollback();
12091 }
12092
12093 /* rollback any changes to devices after restoring the list */
12094 if (mData->flModifications & IsModified_USB)
12095 {
12096 USBControllerList::const_iterator it = mUSBControllers->begin();
12097 while (it != mUSBControllers->end())
12098 {
12099 (*it)->i_rollback();
12100 ++it;
12101 }
12102 }
12103 }
12104
12105 mUserData.rollback();
12106
12107 mHWData.rollback();
12108
12109 if (mData->flModifications & IsModified_Storage)
12110 rollbackMedia();
12111
12112 if (mBIOSSettings)
12113 mBIOSSettings->i_rollback();
12114
12115 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12116 mVRDEServer->i_rollback();
12117
12118 if (mAudioAdapter)
12119 mAudioAdapter->i_rollback();
12120
12121 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12122 mUSBDeviceFilters->i_rollback();
12123
12124 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12125 mBandwidthControl->i_rollback();
12126
12127 if (!mHWData.isNull())
12128 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12129 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12130 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12131 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12132
12133 if (mData->flModifications & IsModified_NetworkAdapters)
12134 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12135 if ( mNetworkAdapters[slot]
12136 && mNetworkAdapters[slot]->i_isModified())
12137 {
12138 mNetworkAdapters[slot]->i_rollback();
12139 networkAdapters[slot] = mNetworkAdapters[slot];
12140 }
12141
12142 if (mData->flModifications & IsModified_SerialPorts)
12143 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12144 if ( mSerialPorts[slot]
12145 && mSerialPorts[slot]->i_isModified())
12146 {
12147 mSerialPorts[slot]->i_rollback();
12148 serialPorts[slot] = mSerialPorts[slot];
12149 }
12150
12151 if (mData->flModifications & IsModified_ParallelPorts)
12152 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12153 if ( mParallelPorts[slot]
12154 && mParallelPorts[slot]->i_isModified())
12155 {
12156 mParallelPorts[slot]->i_rollback();
12157 parallelPorts[slot] = mParallelPorts[slot];
12158 }
12159
12160 if (aNotify)
12161 {
12162 /* inform the direct session about changes */
12163
12164 ComObjPtr<Machine> that = this;
12165 uint32_t flModifications = mData->flModifications;
12166 alock.release();
12167
12168 if (flModifications & IsModified_SharedFolders)
12169 that->onSharedFolderChange();
12170
12171 if (flModifications & IsModified_VRDEServer)
12172 that->onVRDEServerChange(/* aRestart */ TRUE);
12173 if (flModifications & IsModified_USB)
12174 that->onUSBControllerChange();
12175
12176 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12177 if (networkAdapters[slot])
12178 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12179 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12180 if (serialPorts[slot])
12181 that->onSerialPortChange(serialPorts[slot]);
12182 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12183 if (parallelPorts[slot])
12184 that->onParallelPortChange(parallelPorts[slot]);
12185
12186 if (flModifications & IsModified_Storage)
12187 that->onStorageControllerChange();
12188
12189#if 0
12190 if (flModifications & IsModified_BandwidthControl)
12191 that->onBandwidthControlChange();
12192#endif
12193 }
12194}
12195
12196/**
12197 * Commits all the changes to machine settings.
12198 *
12199 * Note that this operation is supposed to never fail.
12200 *
12201 * @note Locks this object and children for writing.
12202 */
12203void Machine::commit()
12204{
12205 AutoCaller autoCaller(this);
12206 AssertComRCReturnVoid(autoCaller.rc());
12207
12208 AutoCaller peerCaller(mPeer);
12209 AssertComRCReturnVoid(peerCaller.rc());
12210
12211 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12212
12213 /*
12214 * use safe commit to ensure Snapshot machines (that share mUserData)
12215 * will still refer to a valid memory location
12216 */
12217 mUserData.commitCopy();
12218
12219 mHWData.commit();
12220
12221 if (mMediaData.isBackedUp())
12222 commitMedia(Global::IsOnline(mData->mMachineState));
12223
12224 mBIOSSettings->i_commit();
12225 mVRDEServer->i_commit();
12226 mAudioAdapter->i_commit();
12227 mUSBDeviceFilters->i_commit();
12228 mBandwidthControl->i_commit();
12229
12230 /* Since mNetworkAdapters is a list which might have been changed (resized)
12231 * without using the Backupable<> template we need to handle the copying
12232 * of the list entries manually, including the creation of peers for the
12233 * new objects. */
12234 bool commitNetworkAdapters = false;
12235 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12236 if (mPeer)
12237 {
12238 /* commit everything, even the ones which will go away */
12239 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12240 mNetworkAdapters[slot]->i_commit();
12241 /* copy over the new entries, creating a peer and uninit the original */
12242 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12243 for (size_t slot = 0; slot < newSize; slot++)
12244 {
12245 /* look if this adapter has a peer device */
12246 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12247 if (!peer)
12248 {
12249 /* no peer means the adapter is a newly created one;
12250 * create a peer owning data this data share it with */
12251 peer.createObject();
12252 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12253 }
12254 mPeer->mNetworkAdapters[slot] = peer;
12255 }
12256 /* uninit any no longer needed network adapters */
12257 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12258 mNetworkAdapters[slot]->uninit();
12259 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12260 {
12261 if (mPeer->mNetworkAdapters[slot])
12262 mPeer->mNetworkAdapters[slot]->uninit();
12263 }
12264 /* Keep the original network adapter count until this point, so that
12265 * discarding a chipset type change will not lose settings. */
12266 mNetworkAdapters.resize(newSize);
12267 mPeer->mNetworkAdapters.resize(newSize);
12268 }
12269 else
12270 {
12271 /* we have no peer (our parent is the newly created machine);
12272 * just commit changes to the network adapters */
12273 commitNetworkAdapters = true;
12274 }
12275 if (commitNetworkAdapters)
12276 {
12277 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12278 mNetworkAdapters[slot]->i_commit();
12279 }
12280
12281 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12282 mSerialPorts[slot]->i_commit();
12283 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12284 mParallelPorts[slot]->i_commit();
12285
12286 bool commitStorageControllers = false;
12287
12288 if (mStorageControllers.isBackedUp())
12289 {
12290 mStorageControllers.commit();
12291
12292 if (mPeer)
12293 {
12294 /* Commit all changes to new controllers (this will reshare data with
12295 * peers for those who have peers) */
12296 StorageControllerList *newList = new StorageControllerList();
12297 StorageControllerList::const_iterator it = mStorageControllers->begin();
12298 while (it != mStorageControllers->end())
12299 {
12300 (*it)->i_commit();
12301
12302 /* look if this controller has a peer device */
12303 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12304 if (!peer)
12305 {
12306 /* no peer means the device is a newly created one;
12307 * create a peer owning data this device share it with */
12308 peer.createObject();
12309 peer->init(mPeer, *it, true /* aReshare */);
12310 }
12311 else
12312 {
12313 /* remove peer from the old list */
12314 mPeer->mStorageControllers->remove(peer);
12315 }
12316 /* and add it to the new list */
12317 newList->push_back(peer);
12318
12319 ++it;
12320 }
12321
12322 /* uninit old peer's controllers that are left */
12323 it = mPeer->mStorageControllers->begin();
12324 while (it != mPeer->mStorageControllers->end())
12325 {
12326 (*it)->uninit();
12327 ++it;
12328 }
12329
12330 /* attach new list of controllers to our peer */
12331 mPeer->mStorageControllers.attach(newList);
12332 }
12333 else
12334 {
12335 /* we have no peer (our parent is the newly created machine);
12336 * just commit changes to devices */
12337 commitStorageControllers = true;
12338 }
12339 }
12340 else
12341 {
12342 /* the list of controllers itself is not changed,
12343 * just commit changes to controllers themselves */
12344 commitStorageControllers = true;
12345 }
12346
12347 if (commitStorageControllers)
12348 {
12349 StorageControllerList::const_iterator it = mStorageControllers->begin();
12350 while (it != mStorageControllers->end())
12351 {
12352 (*it)->i_commit();
12353 ++it;
12354 }
12355 }
12356
12357 bool commitUSBControllers = false;
12358
12359 if (mUSBControllers.isBackedUp())
12360 {
12361 mUSBControllers.commit();
12362
12363 if (mPeer)
12364 {
12365 /* Commit all changes to new controllers (this will reshare data with
12366 * peers for those who have peers) */
12367 USBControllerList *newList = new USBControllerList();
12368 USBControllerList::const_iterator it = mUSBControllers->begin();
12369 while (it != mUSBControllers->end())
12370 {
12371 (*it)->i_commit();
12372
12373 /* look if this controller has a peer device */
12374 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12375 if (!peer)
12376 {
12377 /* no peer means the device is a newly created one;
12378 * create a peer owning data this device share it with */
12379 peer.createObject();
12380 peer->init(mPeer, *it, true /* aReshare */);
12381 }
12382 else
12383 {
12384 /* remove peer from the old list */
12385 mPeer->mUSBControllers->remove(peer);
12386 }
12387 /* and add it to the new list */
12388 newList->push_back(peer);
12389
12390 ++it;
12391 }
12392
12393 /* uninit old peer's controllers that are left */
12394 it = mPeer->mUSBControllers->begin();
12395 while (it != mPeer->mUSBControllers->end())
12396 {
12397 (*it)->uninit();
12398 ++it;
12399 }
12400
12401 /* attach new list of controllers to our peer */
12402 mPeer->mUSBControllers.attach(newList);
12403 }
12404 else
12405 {
12406 /* we have no peer (our parent is the newly created machine);
12407 * just commit changes to devices */
12408 commitUSBControllers = true;
12409 }
12410 }
12411 else
12412 {
12413 /* the list of controllers itself is not changed,
12414 * just commit changes to controllers themselves */
12415 commitUSBControllers = true;
12416 }
12417
12418 if (commitUSBControllers)
12419 {
12420 USBControllerList::const_iterator it = mUSBControllers->begin();
12421 while (it != mUSBControllers->end())
12422 {
12423 (*it)->i_commit();
12424 ++it;
12425 }
12426 }
12427
12428 if (isSessionMachine())
12429 {
12430 /* attach new data to the primary machine and reshare it */
12431 mPeer->mUserData.attach(mUserData);
12432 mPeer->mHWData.attach(mHWData);
12433 /* mMediaData is reshared by fixupMedia */
12434 // mPeer->mMediaData.attach(mMediaData);
12435 Assert(mPeer->mMediaData.data() == mMediaData.data());
12436 }
12437}
12438
12439/**
12440 * Copies all the hardware data from the given machine.
12441 *
12442 * Currently, only called when the VM is being restored from a snapshot. In
12443 * particular, this implies that the VM is not running during this method's
12444 * call.
12445 *
12446 * @note This method must be called from under this object's lock.
12447 *
12448 * @note This method doesn't call #commit(), so all data remains backed up and
12449 * unsaved.
12450 */
12451void Machine::copyFrom(Machine *aThat)
12452{
12453 AssertReturnVoid(!isSnapshotMachine());
12454 AssertReturnVoid(aThat->isSnapshotMachine());
12455
12456 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12457
12458 mHWData.assignCopy(aThat->mHWData);
12459
12460 // create copies of all shared folders (mHWData after attaching a copy
12461 // contains just references to original objects)
12462 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12463 it != mHWData->mSharedFolders.end();
12464 ++it)
12465 {
12466 ComObjPtr<SharedFolder> folder;
12467 folder.createObject();
12468 HRESULT rc = folder->initCopy(getMachine(), *it);
12469 AssertComRC(rc);
12470 *it = folder;
12471 }
12472
12473 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12474 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12475 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12476 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12477 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12478
12479 /* create private copies of all controllers */
12480 mStorageControllers.backup();
12481 mStorageControllers->clear();
12482 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12483 it != aThat->mStorageControllers->end();
12484 ++it)
12485 {
12486 ComObjPtr<StorageController> ctrl;
12487 ctrl.createObject();
12488 ctrl->initCopy(this, *it);
12489 mStorageControllers->push_back(ctrl);
12490 }
12491
12492 /* create private copies of all USB controllers */
12493 mUSBControllers.backup();
12494 mUSBControllers->clear();
12495 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12496 it != aThat->mUSBControllers->end();
12497 ++it)
12498 {
12499 ComObjPtr<USBController> ctrl;
12500 ctrl.createObject();
12501 ctrl->initCopy(this, *it);
12502 mUSBControllers->push_back(ctrl);
12503 }
12504
12505 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12506 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12507 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12508 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12509 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12510 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12511 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12512}
12513
12514/**
12515 * Returns whether the given storage controller is hotplug capable.
12516 *
12517 * @returns true if the controller supports hotplugging
12518 * false otherwise.
12519 * @param enmCtrlType The controller type to check for.
12520 */
12521bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12522{
12523 switch (enmCtrlType)
12524 {
12525 case StorageControllerType_IntelAhci:
12526 case StorageControllerType_USB:
12527 return true;
12528 case StorageControllerType_LsiLogic:
12529 case StorageControllerType_LsiLogicSas:
12530 case StorageControllerType_BusLogic:
12531 case StorageControllerType_PIIX3:
12532 case StorageControllerType_PIIX4:
12533 case StorageControllerType_ICH6:
12534 case StorageControllerType_I82078:
12535 default:
12536 return false;
12537 }
12538}
12539
12540#ifdef VBOX_WITH_RESOURCE_USAGE_API
12541
12542void Machine::getDiskList(MediaList &list)
12543{
12544 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12545 it != mMediaData->mAttachments.end();
12546 ++it)
12547 {
12548 MediumAttachment* pAttach = *it;
12549 /* just in case */
12550 AssertStmt(pAttach, continue);
12551
12552 AutoCaller localAutoCallerA(pAttach);
12553 if (FAILED(localAutoCallerA.rc())) continue;
12554
12555 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12556
12557 if (pAttach->i_getType() == DeviceType_HardDisk)
12558 list.push_back(pAttach->i_getMedium());
12559 }
12560}
12561
12562void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12563{
12564 AssertReturnVoid(isWriteLockOnCurrentThread());
12565 AssertPtrReturnVoid(aCollector);
12566
12567 pm::CollectorHAL *hal = aCollector->getHAL();
12568 /* Create sub metrics */
12569 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12570 "Percentage of processor time spent in user mode by the VM process.");
12571 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12572 "Percentage of processor time spent in kernel mode by the VM process.");
12573 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12574 "Size of resident portion of VM process in memory.");
12575 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12576 "Actual size of all VM disks combined.");
12577 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12578 "Network receive rate.");
12579 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12580 "Network transmit rate.");
12581 /* Create and register base metrics */
12582 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12583 cpuLoadUser, cpuLoadKernel);
12584 aCollector->registerBaseMetric(cpuLoad);
12585 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12586 ramUsageUsed);
12587 aCollector->registerBaseMetric(ramUsage);
12588 MediaList disks;
12589 getDiskList(disks);
12590 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12591 diskUsageUsed);
12592 aCollector->registerBaseMetric(diskUsage);
12593
12594 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12595 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12596 new pm::AggregateAvg()));
12597 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12598 new pm::AggregateMin()));
12599 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12600 new pm::AggregateMax()));
12601 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12602 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12603 new pm::AggregateAvg()));
12604 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12605 new pm::AggregateMin()));
12606 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12607 new pm::AggregateMax()));
12608
12609 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12610 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12611 new pm::AggregateAvg()));
12612 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12613 new pm::AggregateMin()));
12614 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12615 new pm::AggregateMax()));
12616
12617 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12618 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12619 new pm::AggregateAvg()));
12620 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12621 new pm::AggregateMin()));
12622 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12623 new pm::AggregateMax()));
12624
12625
12626 /* Guest metrics collector */
12627 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12628 aCollector->registerGuest(mCollectorGuest);
12629 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12630 this, __PRETTY_FUNCTION__, mCollectorGuest));
12631
12632 /* Create sub metrics */
12633 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12634 "Percentage of processor time spent in user mode as seen by the guest.");
12635 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12636 "Percentage of processor time spent in kernel mode as seen by the guest.");
12637 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12638 "Percentage of processor time spent idling as seen by the guest.");
12639
12640 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12641 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12642 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12643 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12644 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12645 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12646
12647 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12648
12649 /* Create and register base metrics */
12650 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12651 machineNetRx, machineNetTx);
12652 aCollector->registerBaseMetric(machineNetRate);
12653
12654 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12655 guestLoadUser, guestLoadKernel, guestLoadIdle);
12656 aCollector->registerBaseMetric(guestCpuLoad);
12657
12658 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12659 guestMemTotal, guestMemFree,
12660 guestMemBalloon, guestMemShared,
12661 guestMemCache, guestPagedTotal);
12662 aCollector->registerBaseMetric(guestCpuMem);
12663
12664 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12665 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12666 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12667 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12668
12669 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12670 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12671 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12672 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12673
12674 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12675 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12676 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12677 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12678
12679 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12680 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12681 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12682 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12683
12684 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12685 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12686 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12687 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12688
12689 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12690 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12691 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12692 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12693
12694 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12695 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12696 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12697 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12698
12699 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12700 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12701 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12702 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12703
12704 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12705 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12706 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12707 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12708
12709 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12710 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12711 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12712 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12713
12714 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12715 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12716 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12717 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12718}
12719
12720void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12721{
12722 AssertReturnVoid(isWriteLockOnCurrentThread());
12723
12724 if (aCollector)
12725 {
12726 aCollector->unregisterMetricsFor(aMachine);
12727 aCollector->unregisterBaseMetricsFor(aMachine);
12728 }
12729}
12730
12731#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12732
12733
12734////////////////////////////////////////////////////////////////////////////////
12735
12736DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12737
12738HRESULT SessionMachine::FinalConstruct()
12739{
12740 LogFlowThisFunc(("\n"));
12741
12742 mClientToken = NULL;
12743
12744 return BaseFinalConstruct();
12745}
12746
12747void SessionMachine::FinalRelease()
12748{
12749 LogFlowThisFunc(("\n"));
12750
12751 Assert(!mClientToken);
12752 /* paranoia, should not hang around any more */
12753 if (mClientToken)
12754 {
12755 delete mClientToken;
12756 mClientToken = NULL;
12757 }
12758
12759 uninit(Uninit::Unexpected);
12760
12761 BaseFinalRelease();
12762}
12763
12764/**
12765 * @note Must be called only by Machine::LockMachine() from its own write lock.
12766 */
12767HRESULT SessionMachine::init(Machine *aMachine)
12768{
12769 LogFlowThisFuncEnter();
12770 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12771
12772 AssertReturn(aMachine, E_INVALIDARG);
12773
12774 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12775
12776 /* Enclose the state transition NotReady->InInit->Ready */
12777 AutoInitSpan autoInitSpan(this);
12778 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12779
12780 HRESULT rc = S_OK;
12781
12782 /* create the machine client token */
12783 try
12784 {
12785 mClientToken = new ClientToken(aMachine, this);
12786 if (!mClientToken->isReady())
12787 {
12788 delete mClientToken;
12789 mClientToken = NULL;
12790 rc = E_FAIL;
12791 }
12792 }
12793 catch (std::bad_alloc &)
12794 {
12795 rc = E_OUTOFMEMORY;
12796 }
12797 if (FAILED(rc))
12798 return rc;
12799
12800 /* memorize the peer Machine */
12801 unconst(mPeer) = aMachine;
12802 /* share the parent pointer */
12803 unconst(mParent) = aMachine->mParent;
12804
12805 /* take the pointers to data to share */
12806 mData.share(aMachine->mData);
12807 mSSData.share(aMachine->mSSData);
12808
12809 mUserData.share(aMachine->mUserData);
12810 mHWData.share(aMachine->mHWData);
12811 mMediaData.share(aMachine->mMediaData);
12812
12813 mStorageControllers.allocate();
12814 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12815 it != aMachine->mStorageControllers->end();
12816 ++it)
12817 {
12818 ComObjPtr<StorageController> ctl;
12819 ctl.createObject();
12820 ctl->init(this, *it);
12821 mStorageControllers->push_back(ctl);
12822 }
12823
12824 mUSBControllers.allocate();
12825 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12826 it != aMachine->mUSBControllers->end();
12827 ++it)
12828 {
12829 ComObjPtr<USBController> ctl;
12830 ctl.createObject();
12831 ctl->init(this, *it);
12832 mUSBControllers->push_back(ctl);
12833 }
12834
12835 unconst(mBIOSSettings).createObject();
12836 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12837 /* create another VRDEServer object that will be mutable */
12838 unconst(mVRDEServer).createObject();
12839 mVRDEServer->init(this, aMachine->mVRDEServer);
12840 /* create another audio adapter object that will be mutable */
12841 unconst(mAudioAdapter).createObject();
12842 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12843 /* create a list of serial ports that will be mutable */
12844 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12845 {
12846 unconst(mSerialPorts[slot]).createObject();
12847 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12848 }
12849 /* create a list of parallel ports that will be mutable */
12850 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12851 {
12852 unconst(mParallelPorts[slot]).createObject();
12853 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12854 }
12855
12856 /* create another USB device filters object that will be mutable */
12857 unconst(mUSBDeviceFilters).createObject();
12858 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12859
12860 /* create a list of network adapters that will be mutable */
12861 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12862 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12863 {
12864 unconst(mNetworkAdapters[slot]).createObject();
12865 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12866
12867 NetworkAttachmentType_T type;
12868 HRESULT hrc;
12869 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12870 if ( SUCCEEDED(hrc)
12871 && type == NetworkAttachmentType_NATNetwork)
12872 {
12873 Bstr name;
12874 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12875 if (SUCCEEDED(hrc))
12876 {
12877 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12878 mUserData->s.strName.c_str(), name.raw()));
12879 aMachine->lockHandle()->unlockWrite();
12880 mParent->natNetworkRefInc(name.raw());
12881#ifdef RT_LOCK_STRICT
12882 aMachine->lockHandle()->lockWrite(RT_SRC_POS);
12883#else
12884 aMachine->lockHandle()->lockWrite();
12885#endif
12886 }
12887 }
12888 }
12889
12890 /* create another bandwidth control object that will be mutable */
12891 unconst(mBandwidthControl).createObject();
12892 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12893
12894 /* default is to delete saved state on Saved -> PoweredOff transition */
12895 mRemoveSavedState = true;
12896
12897 /* Confirm a successful initialization when it's the case */
12898 autoInitSpan.setSucceeded();
12899
12900 LogFlowThisFuncLeave();
12901 return rc;
12902}
12903
12904/**
12905 * Uninitializes this session object. If the reason is other than
12906 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12907 * or the client watcher code.
12908 *
12909 * @param aReason uninitialization reason
12910 *
12911 * @note Locks mParent + this object for writing.
12912 */
12913void SessionMachine::uninit(Uninit::Reason aReason)
12914{
12915 LogFlowThisFuncEnter();
12916 LogFlowThisFunc(("reason=%d\n", aReason));
12917
12918 /*
12919 * Strongly reference ourselves to prevent this object deletion after
12920 * mData->mSession.mMachine.setNull() below (which can release the last
12921 * reference and call the destructor). Important: this must be done before
12922 * accessing any members (and before AutoUninitSpan that does it as well).
12923 * This self reference will be released as the very last step on return.
12924 */
12925 ComObjPtr<SessionMachine> selfRef = this;
12926
12927 /* Enclose the state transition Ready->InUninit->NotReady */
12928 AutoUninitSpan autoUninitSpan(this);
12929 if (autoUninitSpan.uninitDone())
12930 {
12931 LogFlowThisFunc(("Already uninitialized\n"));
12932 LogFlowThisFuncLeave();
12933 return;
12934 }
12935
12936 if (autoUninitSpan.initFailed())
12937 {
12938 /* We've been called by init() because it's failed. It's not really
12939 * necessary (nor it's safe) to perform the regular uninit sequence
12940 * below, the following is enough.
12941 */
12942 LogFlowThisFunc(("Initialization failed.\n"));
12943 /* destroy the machine client token */
12944 if (mClientToken)
12945 {
12946 delete mClientToken;
12947 mClientToken = NULL;
12948 }
12949 uninitDataAndChildObjects();
12950 mData.free();
12951 unconst(mParent) = NULL;
12952 unconst(mPeer) = NULL;
12953 LogFlowThisFuncLeave();
12954 return;
12955 }
12956
12957 MachineState_T lastState;
12958 {
12959 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12960 lastState = mData->mMachineState;
12961 }
12962 NOREF(lastState);
12963
12964#ifdef VBOX_WITH_USB
12965 // release all captured USB devices, but do this before requesting the locks below
12966 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12967 {
12968 /* Console::captureUSBDevices() is called in the VM process only after
12969 * setting the machine state to Starting or Restoring.
12970 * Console::detachAllUSBDevices() will be called upon successful
12971 * termination. So, we need to release USB devices only if there was
12972 * an abnormal termination of a running VM.
12973 *
12974 * This is identical to SessionMachine::DetachAllUSBDevices except
12975 * for the aAbnormal argument. */
12976 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12977 AssertComRC(rc);
12978 NOREF(rc);
12979
12980 USBProxyService *service = mParent->host()->i_usbProxyService();
12981 if (service)
12982 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12983 }
12984#endif /* VBOX_WITH_USB */
12985
12986 // we need to lock this object in uninit() because the lock is shared
12987 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12988 // and others need mParent lock, and USB needs host lock.
12989 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12990
12991#ifdef VBOX_WITH_RESOURCE_USAGE_API
12992 /*
12993 * It is safe to call Machine::unregisterMetrics() here because
12994 * PerformanceCollector::samplerCallback no longer accesses guest methods
12995 * holding the lock.
12996 */
12997 unregisterMetrics(mParent->performanceCollector(), mPeer);
12998 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12999 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
13000 this, __PRETTY_FUNCTION__, mCollectorGuest));
13001 if (mCollectorGuest)
13002 {
13003 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
13004 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13005 mCollectorGuest = NULL;
13006 }
13007#endif
13008
13009 if (aReason == Uninit::Abnormal)
13010 {
13011 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
13012 Global::IsOnlineOrTransient(lastState)));
13013
13014 /* reset the state to Aborted */
13015 if (mData->mMachineState != MachineState_Aborted)
13016 setMachineState(MachineState_Aborted);
13017 }
13018
13019 // any machine settings modified?
13020 if (mData->flModifications)
13021 {
13022 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
13023 rollback(false /* aNotify */);
13024 }
13025
13026 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
13027 || !mConsoleTaskData.mSnapshot);
13028 if (!mConsoleTaskData.strStateFilePath.isEmpty())
13029 {
13030 LogWarningThisFunc(("canceling failed save state request!\n"));
13031 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
13032 }
13033 else if (!mConsoleTaskData.mSnapshot.isNull())
13034 {
13035 LogWarningThisFunc(("canceling untaken snapshot!\n"));
13036
13037 /* delete all differencing hard disks created (this will also attach
13038 * their parents back by rolling back mMediaData) */
13039 rollbackMedia();
13040
13041 // delete the saved state file (it might have been already created)
13042 // AFTER killing the snapshot so that releaseSavedStateFile() won't
13043 // think it's still in use
13044 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
13045 mConsoleTaskData.mSnapshot->uninit();
13046 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
13047 }
13048
13049 if (!mData->mSession.mType.isEmpty())
13050 {
13051 /* mType is not null when this machine's process has been started by
13052 * Machine::LaunchVMProcess(), therefore it is our child. We
13053 * need to queue the PID to reap the process (and avoid zombies on
13054 * Linux). */
13055 Assert(mData->mSession.mPID != NIL_RTPROCESS);
13056 mParent->addProcessToReap(mData->mSession.mPID);
13057 }
13058
13059 mData->mSession.mPID = NIL_RTPROCESS;
13060
13061 if (aReason == Uninit::Unexpected)
13062 {
13063 /* Uninitialization didn't come from #checkForDeath(), so tell the
13064 * client watcher thread to update the set of machines that have open
13065 * sessions. */
13066 mParent->updateClientWatcher();
13067 }
13068
13069 /* uninitialize all remote controls */
13070 if (mData->mSession.mRemoteControls.size())
13071 {
13072 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13073 mData->mSession.mRemoteControls.size()));
13074
13075 Data::Session::RemoteControlList::iterator it =
13076 mData->mSession.mRemoteControls.begin();
13077 while (it != mData->mSession.mRemoteControls.end())
13078 {
13079 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13080 HRESULT rc = (*it)->Uninitialize();
13081 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13082 if (FAILED(rc))
13083 LogWarningThisFunc(("Forgot to close the remote session?\n"));
13084 ++it;
13085 }
13086 mData->mSession.mRemoteControls.clear();
13087 }
13088
13089 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
13090 {
13091 NetworkAttachmentType_T type;
13092 HRESULT hrc;
13093
13094 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13095 if ( SUCCEEDED(hrc)
13096 && type == NetworkAttachmentType_NATNetwork)
13097 {
13098 Bstr name;
13099 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13100 if (SUCCEEDED(hrc))
13101 {
13102 multilock.release();
13103 LogRel(("VM '%s' stops using NAT network '%ls'\n",
13104 mUserData->s.strName.c_str(), name.raw()));
13105 mParent->natNetworkRefDec(name.raw());
13106 multilock.acquire();
13107 }
13108 }
13109 }
13110
13111 /*
13112 * An expected uninitialization can come only from #checkForDeath().
13113 * Otherwise it means that something's gone really wrong (for example,
13114 * the Session implementation has released the VirtualBox reference
13115 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13116 * etc). However, it's also possible, that the client releases the IPC
13117 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13118 * but the VirtualBox release event comes first to the server process.
13119 * This case is practically possible, so we should not assert on an
13120 * unexpected uninit, just log a warning.
13121 */
13122
13123 if ((aReason == Uninit::Unexpected))
13124 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13125
13126 if (aReason != Uninit::Normal)
13127 {
13128 mData->mSession.mDirectControl.setNull();
13129 }
13130 else
13131 {
13132 /* this must be null here (see #OnSessionEnd()) */
13133 Assert(mData->mSession.mDirectControl.isNull());
13134 Assert(mData->mSession.mState == SessionState_Unlocking);
13135 Assert(!mData->mSession.mProgress.isNull());
13136 }
13137 if (mData->mSession.mProgress)
13138 {
13139 if (aReason == Uninit::Normal)
13140 mData->mSession.mProgress->notifyComplete(S_OK);
13141 else
13142 mData->mSession.mProgress->notifyComplete(E_FAIL,
13143 COM_IIDOF(ISession),
13144 getComponentName(),
13145 tr("The VM session was aborted"));
13146 mData->mSession.mProgress.setNull();
13147 }
13148
13149 /* remove the association between the peer machine and this session machine */
13150 Assert( (SessionMachine*)mData->mSession.mMachine == this
13151 || aReason == Uninit::Unexpected);
13152
13153 /* reset the rest of session data */
13154 mData->mSession.mMachine.setNull();
13155 mData->mSession.mState = SessionState_Unlocked;
13156 mData->mSession.mType.setNull();
13157
13158 /* destroy the machine client token before leaving the exclusive lock */
13159 if (mClientToken)
13160 {
13161 delete mClientToken;
13162 mClientToken = NULL;
13163 }
13164
13165 /* fire an event */
13166 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13167
13168 uninitDataAndChildObjects();
13169
13170 /* free the essential data structure last */
13171 mData.free();
13172
13173 /* release the exclusive lock before setting the below two to NULL */
13174 multilock.release();
13175
13176 unconst(mParent) = NULL;
13177 unconst(mPeer) = NULL;
13178
13179 LogFlowThisFuncLeave();
13180}
13181
13182// util::Lockable interface
13183////////////////////////////////////////////////////////////////////////////////
13184
13185/**
13186 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13187 * with the primary Machine instance (mPeer).
13188 */
13189RWLockHandle *SessionMachine::lockHandle() const
13190{
13191 AssertReturn(mPeer != NULL, NULL);
13192 return mPeer->lockHandle();
13193}
13194
13195// IInternalMachineControl methods
13196////////////////////////////////////////////////////////////////////////////////
13197
13198/**
13199 * Passes collected guest statistics to performance collector object
13200 */
13201STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13202 ULONG aCpuKernel, ULONG aCpuIdle,
13203 ULONG aMemTotal, ULONG aMemFree,
13204 ULONG aMemBalloon, ULONG aMemShared,
13205 ULONG aMemCache, ULONG aPageTotal,
13206 ULONG aAllocVMM, ULONG aFreeVMM,
13207 ULONG aBalloonedVMM, ULONG aSharedVMM,
13208 ULONG aVmNetRx, ULONG aVmNetTx)
13209{
13210#ifdef VBOX_WITH_RESOURCE_USAGE_API
13211 if (mCollectorGuest)
13212 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13213 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13214 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13215 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13216
13217 return S_OK;
13218#else
13219 NOREF(aValidStats);
13220 NOREF(aCpuUser);
13221 NOREF(aCpuKernel);
13222 NOREF(aCpuIdle);
13223 NOREF(aMemTotal);
13224 NOREF(aMemFree);
13225 NOREF(aMemBalloon);
13226 NOREF(aMemShared);
13227 NOREF(aMemCache);
13228 NOREF(aPageTotal);
13229 NOREF(aAllocVMM);
13230 NOREF(aFreeVMM);
13231 NOREF(aBalloonedVMM);
13232 NOREF(aSharedVMM);
13233 NOREF(aVmNetRx);
13234 NOREF(aVmNetTx);
13235 return E_NOTIMPL;
13236#endif
13237}
13238
13239/**
13240 * @note Locks this object for writing.
13241 */
13242STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13243{
13244 AutoCaller autoCaller(this);
13245 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13246
13247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13248
13249 mRemoveSavedState = aRemove;
13250
13251 return S_OK;
13252}
13253
13254/**
13255 * @note Locks the same as #setMachineState() does.
13256 */
13257STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13258{
13259 return setMachineState(aMachineState);
13260}
13261
13262/**
13263 * @note Locks this object for writing.
13264 */
13265STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13266{
13267 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13268 AutoCaller autoCaller(this);
13269 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13270
13271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13272
13273 if (mData->mSession.mState != SessionState_Locked)
13274 return VBOX_E_INVALID_OBJECT_STATE;
13275
13276 if (!mData->mSession.mProgress.isNull())
13277 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13278
13279 LogFlowThisFunc(("returns S_OK.\n"));
13280 return S_OK;
13281}
13282
13283/**
13284 * @note Locks this object for writing.
13285 */
13286STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13287{
13288 AutoCaller autoCaller(this);
13289 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13290
13291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13292
13293 if (mData->mSession.mState != SessionState_Locked)
13294 return VBOX_E_INVALID_OBJECT_STATE;
13295
13296 /* Finalize the LaunchVMProcess progress object. */
13297 if (mData->mSession.mProgress)
13298 {
13299 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13300 mData->mSession.mProgress.setNull();
13301 }
13302
13303 if (SUCCEEDED((HRESULT)iResult))
13304 {
13305#ifdef VBOX_WITH_RESOURCE_USAGE_API
13306 /* The VM has been powered up successfully, so it makes sense
13307 * now to offer the performance metrics for a running machine
13308 * object. Doing it earlier wouldn't be safe. */
13309 registerMetrics(mParent->performanceCollector(), mPeer,
13310 mData->mSession.mPID);
13311#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13312 }
13313
13314 return S_OK;
13315}
13316
13317/**
13318 * @note Locks this object for writing.
13319 */
13320STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13321{
13322 LogFlowThisFuncEnter();
13323
13324 CheckComArgOutPointerValid(aProgress);
13325
13326 AutoCaller autoCaller(this);
13327 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13328
13329 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13330
13331 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13332 E_FAIL);
13333
13334 /* create a progress object to track operation completion */
13335 ComObjPtr<Progress> pProgress;
13336 pProgress.createObject();
13337 pProgress->init(getVirtualBox(),
13338 static_cast<IMachine *>(this) /* aInitiator */,
13339 Bstr(tr("Stopping the virtual machine")).raw(),
13340 FALSE /* aCancelable */);
13341
13342 /* fill in the console task data */
13343 mConsoleTaskData.mLastState = mData->mMachineState;
13344 mConsoleTaskData.mProgress = pProgress;
13345
13346 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13347 setMachineState(MachineState_Stopping);
13348
13349 pProgress.queryInterfaceTo(aProgress);
13350
13351 return S_OK;
13352}
13353
13354/**
13355 * @note Locks this object for writing.
13356 */
13357STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13358{
13359 LogFlowThisFuncEnter();
13360
13361 AutoCaller autoCaller(this);
13362 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13363
13364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13365
13366 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13367 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13368 && mConsoleTaskData.mLastState != MachineState_Null,
13369 E_FAIL);
13370
13371 /*
13372 * On failure, set the state to the state we had when BeginPoweringDown()
13373 * was called (this is expected by Console::PowerDown() and the associated
13374 * task). On success the VM process already changed the state to
13375 * MachineState_PoweredOff, so no need to do anything.
13376 */
13377 if (FAILED(iResult))
13378 setMachineState(mConsoleTaskData.mLastState);
13379
13380 /* notify the progress object about operation completion */
13381 Assert(mConsoleTaskData.mProgress);
13382 if (SUCCEEDED(iResult))
13383 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13384 else
13385 {
13386 Utf8Str strErrMsg(aErrMsg);
13387 if (strErrMsg.length())
13388 mConsoleTaskData.mProgress->notifyComplete(iResult,
13389 COM_IIDOF(ISession),
13390 getComponentName(),
13391 strErrMsg.c_str());
13392 else
13393 mConsoleTaskData.mProgress->notifyComplete(iResult);
13394 }
13395
13396 /* clear out the temporary saved state data */
13397 mConsoleTaskData.mLastState = MachineState_Null;
13398 mConsoleTaskData.mProgress.setNull();
13399
13400 LogFlowThisFuncLeave();
13401 return S_OK;
13402}
13403
13404
13405/**
13406 * Goes through the USB filters of the given machine to see if the given
13407 * device matches any filter or not.
13408 *
13409 * @note Locks the same as USBController::hasMatchingFilter() does.
13410 */
13411STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13412 BOOL *aMatched,
13413 ULONG *aMaskedIfs)
13414{
13415 LogFlowThisFunc(("\n"));
13416
13417 CheckComArgNotNull(aUSBDevice);
13418 CheckComArgOutPointerValid(aMatched);
13419
13420 AutoCaller autoCaller(this);
13421 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13422
13423#ifdef VBOX_WITH_USB
13424 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aUSBDevice, aMaskedIfs);
13425#else
13426 NOREF(aUSBDevice);
13427 NOREF(aMaskedIfs);
13428 *aMatched = FALSE;
13429#endif
13430
13431 return S_OK;
13432}
13433
13434/**
13435 * @note Locks the same as Host::captureUSBDevice() does.
13436 */
13437STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13438{
13439 LogFlowThisFunc(("\n"));
13440
13441 AutoCaller autoCaller(this);
13442 AssertComRCReturnRC(autoCaller.rc());
13443
13444#ifdef VBOX_WITH_USB
13445 /* if captureDeviceForVM() fails, it must have set extended error info */
13446 clearError();
13447 MultiResult rc = mParent->host()->i_checkUSBProxyService();
13448 if (FAILED(rc)) return rc;
13449
13450 USBProxyService *service = mParent->host()->i_usbProxyService();
13451 AssertReturn(service, E_FAIL);
13452 return service->captureDeviceForVM(this, Guid(aId).ref());
13453#else
13454 NOREF(aId);
13455 return E_NOTIMPL;
13456#endif
13457}
13458
13459/**
13460 * @note Locks the same as Host::detachUSBDevice() does.
13461 */
13462STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13463{
13464 LogFlowThisFunc(("\n"));
13465
13466 AutoCaller autoCaller(this);
13467 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13468
13469#ifdef VBOX_WITH_USB
13470 USBProxyService *service = mParent->host()->i_usbProxyService();
13471 AssertReturn(service, E_FAIL);
13472 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13473#else
13474 NOREF(aId);
13475 NOREF(aDone);
13476 return E_NOTIMPL;
13477#endif
13478}
13479
13480/**
13481 * Inserts all machine filters to the USB proxy service and then calls
13482 * Host::autoCaptureUSBDevices().
13483 *
13484 * Called by Console from the VM process upon VM startup.
13485 *
13486 * @note Locks what called methods lock.
13487 */
13488STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13489{
13490 LogFlowThisFunc(("\n"));
13491
13492 AutoCaller autoCaller(this);
13493 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13494
13495#ifdef VBOX_WITH_USB
13496 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13497 AssertComRC(rc);
13498 NOREF(rc);
13499
13500 USBProxyService *service = mParent->host()->i_usbProxyService();
13501 AssertReturn(service, E_FAIL);
13502 return service->autoCaptureDevicesForVM(this);
13503#else
13504 return S_OK;
13505#endif
13506}
13507
13508/**
13509 * Removes all machine filters from the USB proxy service and then calls
13510 * Host::detachAllUSBDevices().
13511 *
13512 * Called by Console from the VM process upon normal VM termination or by
13513 * SessionMachine::uninit() upon abnormal VM termination (from under the
13514 * Machine/SessionMachine lock).
13515 *
13516 * @note Locks what called methods lock.
13517 */
13518STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13519{
13520 LogFlowThisFunc(("\n"));
13521
13522 AutoCaller autoCaller(this);
13523 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13524
13525#ifdef VBOX_WITH_USB
13526 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13527 AssertComRC(rc);
13528 NOREF(rc);
13529
13530 USBProxyService *service = mParent->host()->i_usbProxyService();
13531 AssertReturn(service, E_FAIL);
13532 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13533#else
13534 NOREF(aDone);
13535 return S_OK;
13536#endif
13537}
13538
13539/**
13540 * @note Locks this object for writing.
13541 */
13542STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13543 IProgress **aProgress)
13544{
13545 LogFlowThisFuncEnter();
13546
13547 AssertReturn(aSession, E_INVALIDARG);
13548 AssertReturn(aProgress, E_INVALIDARG);
13549
13550 AutoCaller autoCaller(this);
13551
13552 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13553 /*
13554 * We don't assert below because it might happen that a non-direct session
13555 * informs us it is closed right after we've been uninitialized -- it's ok.
13556 */
13557 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13558
13559 /* get IInternalSessionControl interface */
13560 ComPtr<IInternalSessionControl> control(aSession);
13561
13562 ComAssertRet(!control.isNull(), E_INVALIDARG);
13563
13564 /* Creating a Progress object requires the VirtualBox lock, and
13565 * thus locking it here is required by the lock order rules. */
13566 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13567
13568 if (control == mData->mSession.mDirectControl)
13569 {
13570 ComAssertRet(aProgress, E_POINTER);
13571
13572 /* The direct session is being normally closed by the client process
13573 * ----------------------------------------------------------------- */
13574
13575 /* go to the closing state (essential for all open*Session() calls and
13576 * for #checkForDeath()) */
13577 Assert(mData->mSession.mState == SessionState_Locked);
13578 mData->mSession.mState = SessionState_Unlocking;
13579
13580 /* set direct control to NULL to release the remote instance */
13581 mData->mSession.mDirectControl.setNull();
13582 LogFlowThisFunc(("Direct control is set to NULL\n"));
13583
13584 if (mData->mSession.mProgress)
13585 {
13586 /* finalize the progress, someone might wait if a frontend
13587 * closes the session before powering on the VM. */
13588 mData->mSession.mProgress->notifyComplete(E_FAIL,
13589 COM_IIDOF(ISession),
13590 getComponentName(),
13591 tr("The VM session was closed before any attempt to power it on"));
13592 mData->mSession.mProgress.setNull();
13593 }
13594
13595 /* Create the progress object the client will use to wait until
13596 * #checkForDeath() is called to uninitialize this session object after
13597 * it releases the IPC semaphore.
13598 * Note! Because we're "reusing" mProgress here, this must be a proxy
13599 * object just like for LaunchVMProcess. */
13600 Assert(mData->mSession.mProgress.isNull());
13601 ComObjPtr<ProgressProxy> progress;
13602 progress.createObject();
13603 ComPtr<IUnknown> pPeer(mPeer);
13604 progress->init(mParent, pPeer,
13605 Bstr(tr("Closing session")).raw(),
13606 FALSE /* aCancelable */);
13607 progress.queryInterfaceTo(aProgress);
13608 mData->mSession.mProgress = progress;
13609 }
13610 else
13611 {
13612 /* the remote session is being normally closed */
13613 Data::Session::RemoteControlList::iterator it =
13614 mData->mSession.mRemoteControls.begin();
13615 while (it != mData->mSession.mRemoteControls.end())
13616 {
13617 if (control == *it)
13618 break;
13619 ++it;
13620 }
13621 BOOL found = it != mData->mSession.mRemoteControls.end();
13622 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13623 E_INVALIDARG);
13624 // This MUST be erase(it), not remove(*it) as the latter triggers a
13625 // very nasty use after free due to the place where the value "lives".
13626 mData->mSession.mRemoteControls.erase(it);
13627 }
13628
13629 /* signal the client watcher thread, because the client is going away */
13630 mParent->updateClientWatcher();
13631
13632 LogFlowThisFuncLeave();
13633 return S_OK;
13634}
13635
13636/**
13637 * @note Locks this object for writing.
13638 */
13639STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13640{
13641 LogFlowThisFuncEnter();
13642
13643 CheckComArgOutPointerValid(aProgress);
13644 CheckComArgOutPointerValid(aStateFilePath);
13645
13646 AutoCaller autoCaller(this);
13647 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13648
13649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13650
13651 AssertReturn( mData->mMachineState == MachineState_Paused
13652 && mConsoleTaskData.mLastState == MachineState_Null
13653 && mConsoleTaskData.strStateFilePath.isEmpty(),
13654 E_FAIL);
13655
13656 /* create a progress object to track operation completion */
13657 ComObjPtr<Progress> pProgress;
13658 pProgress.createObject();
13659 pProgress->init(getVirtualBox(),
13660 static_cast<IMachine *>(this) /* aInitiator */,
13661 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13662 FALSE /* aCancelable */);
13663
13664 Utf8Str strStateFilePath;
13665 /* stateFilePath is null when the machine is not running */
13666 if (mData->mMachineState == MachineState_Paused)
13667 composeSavedStateFilename(strStateFilePath);
13668
13669 /* fill in the console task data */
13670 mConsoleTaskData.mLastState = mData->mMachineState;
13671 mConsoleTaskData.strStateFilePath = strStateFilePath;
13672 mConsoleTaskData.mProgress = pProgress;
13673
13674 /* set the state to Saving (this is expected by Console::SaveState()) */
13675 setMachineState(MachineState_Saving);
13676
13677 strStateFilePath.cloneTo(aStateFilePath);
13678 pProgress.queryInterfaceTo(aProgress);
13679
13680 return S_OK;
13681}
13682
13683/**
13684 * @note Locks mParent + this object for writing.
13685 */
13686STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13687{
13688 LogFlowThisFunc(("\n"));
13689
13690 AutoCaller autoCaller(this);
13691 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13692
13693 /* endSavingState() need mParent lock */
13694 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13695
13696 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13697 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13698 && mConsoleTaskData.mLastState != MachineState_Null
13699 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13700 E_FAIL);
13701
13702 /*
13703 * On failure, set the state to the state we had when BeginSavingState()
13704 * was called (this is expected by Console::SaveState() and the associated
13705 * task). On success the VM process already changed the state to
13706 * MachineState_Saved, so no need to do anything.
13707 */
13708 if (FAILED(iResult))
13709 setMachineState(mConsoleTaskData.mLastState);
13710
13711 return endSavingState(iResult, aErrMsg);
13712}
13713
13714/**
13715 * @note Locks this object for writing.
13716 */
13717STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13718{
13719 LogFlowThisFunc(("\n"));
13720
13721 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13722
13723 AutoCaller autoCaller(this);
13724 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13725
13726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13727
13728 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13729 || mData->mMachineState == MachineState_Teleported
13730 || mData->mMachineState == MachineState_Aborted
13731 , E_FAIL); /** @todo setError. */
13732
13733 Utf8Str stateFilePathFull = aSavedStateFile;
13734 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13735 if (RT_FAILURE(vrc))
13736 return setError(VBOX_E_FILE_ERROR,
13737 tr("Invalid saved state file path '%ls' (%Rrc)"),
13738 aSavedStateFile,
13739 vrc);
13740
13741 mSSData->strStateFilePath = stateFilePathFull;
13742
13743 /* The below setMachineState() will detect the state transition and will
13744 * update the settings file */
13745
13746 return setMachineState(MachineState_Saved);
13747}
13748
13749STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13750 ComSafeArrayOut(BSTR, aValues),
13751 ComSafeArrayOut(LONG64, aTimestamps),
13752 ComSafeArrayOut(BSTR, aFlags))
13753{
13754 LogFlowThisFunc(("\n"));
13755
13756#ifdef VBOX_WITH_GUEST_PROPS
13757 using namespace guestProp;
13758
13759 AutoCaller autoCaller(this);
13760 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13761
13762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13763
13764 CheckComArgOutSafeArrayPointerValid(aNames);
13765 CheckComArgOutSafeArrayPointerValid(aValues);
13766 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13767 CheckComArgOutSafeArrayPointerValid(aFlags);
13768
13769 size_t cEntries = mHWData->mGuestProperties.size();
13770 com::SafeArray<BSTR> names(cEntries);
13771 com::SafeArray<BSTR> values(cEntries);
13772 com::SafeArray<LONG64> timestamps(cEntries);
13773 com::SafeArray<BSTR> flags(cEntries);
13774 unsigned i = 0;
13775 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13776 it != mHWData->mGuestProperties.end();
13777 ++it)
13778 {
13779 char szFlags[MAX_FLAGS_LEN + 1];
13780 it->first.cloneTo(&names[i]);
13781 it->second.strValue.cloneTo(&values[i]);
13782 timestamps[i] = it->second.mTimestamp;
13783 /* If it is NULL, keep it NULL. */
13784 if (it->second.mFlags)
13785 {
13786 writeFlags(it->second.mFlags, szFlags);
13787 Bstr(szFlags).cloneTo(&flags[i]);
13788 }
13789 else
13790 flags[i] = NULL;
13791 ++i;
13792 }
13793 names.detachTo(ComSafeArrayOutArg(aNames));
13794 values.detachTo(ComSafeArrayOutArg(aValues));
13795 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13796 flags.detachTo(ComSafeArrayOutArg(aFlags));
13797 return S_OK;
13798#else
13799 ReturnComNotImplemented();
13800#endif
13801}
13802
13803STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13804 IN_BSTR aValue,
13805 LONG64 aTimestamp,
13806 IN_BSTR aFlags)
13807{
13808 LogFlowThisFunc(("\n"));
13809
13810#ifdef VBOX_WITH_GUEST_PROPS
13811 using namespace guestProp;
13812
13813 CheckComArgStrNotEmptyOrNull(aName);
13814 CheckComArgNotNull(aValue);
13815 CheckComArgNotNull(aFlags);
13816
13817 try
13818 {
13819 /*
13820 * Convert input up front.
13821 */
13822 Utf8Str utf8Name(aName);
13823 uint32_t fFlags = NILFLAG;
13824 if (aFlags)
13825 {
13826 Utf8Str utf8Flags(aFlags);
13827 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13828 AssertRCReturn(vrc, E_INVALIDARG);
13829 }
13830
13831 /*
13832 * Now grab the object lock, validate the state and do the update.
13833 */
13834 AutoCaller autoCaller(this);
13835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13836
13837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13838
13839 switch (mData->mMachineState)
13840 {
13841 case MachineState_Paused:
13842 case MachineState_Running:
13843 case MachineState_Teleporting:
13844 case MachineState_TeleportingPausedVM:
13845 case MachineState_LiveSnapshotting:
13846 case MachineState_DeletingSnapshotOnline:
13847 case MachineState_DeletingSnapshotPaused:
13848 case MachineState_Saving:
13849 case MachineState_Stopping:
13850 break;
13851
13852 default:
13853 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13854 VBOX_E_INVALID_VM_STATE);
13855 }
13856
13857 setModified(IsModified_MachineData);
13858 mHWData.backup();
13859
13860 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13861 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13862 if (it != mHWData->mGuestProperties.end())
13863 {
13864 if (!fDelete)
13865 {
13866 it->second.strValue = aValue;
13867 it->second.mTimestamp = aTimestamp;
13868 it->second.mFlags = fFlags;
13869 }
13870 else
13871 mHWData->mGuestProperties.erase(it);
13872
13873 mData->mGuestPropertiesModified = TRUE;
13874 }
13875 else if (!fDelete)
13876 {
13877 HWData::GuestProperty prop;
13878 prop.strValue = aValue;
13879 prop.mTimestamp = aTimestamp;
13880 prop.mFlags = fFlags;
13881
13882 mHWData->mGuestProperties[utf8Name] = prop;
13883 mData->mGuestPropertiesModified = TRUE;
13884 }
13885
13886 /*
13887 * Send a callback notification if appropriate
13888 */
13889 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13890 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13891 RTSTR_MAX,
13892 utf8Name.c_str(),
13893 RTSTR_MAX, NULL)
13894 )
13895 {
13896 alock.release();
13897
13898 mParent->onGuestPropertyChange(mData->mUuid,
13899 aName,
13900 aValue,
13901 aFlags);
13902 }
13903 }
13904 catch (...)
13905 {
13906 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13907 }
13908 return S_OK;
13909#else
13910 ReturnComNotImplemented();
13911#endif
13912}
13913
13914STDMETHODIMP SessionMachine::LockMedia()
13915{
13916 AutoCaller autoCaller(this);
13917 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13918
13919 AutoMultiWriteLock2 alock(this->lockHandle(),
13920 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13921
13922 AssertReturn( mData->mMachineState == MachineState_Starting
13923 || mData->mMachineState == MachineState_Restoring
13924 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13925
13926 clearError();
13927 alock.release();
13928 return lockMedia();
13929}
13930
13931STDMETHODIMP SessionMachine::UnlockMedia()
13932{
13933 unlockMedia();
13934 return S_OK;
13935}
13936
13937STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13938 IMediumAttachment **aNewAttachment)
13939{
13940 CheckComArgNotNull(aAttachment);
13941 CheckComArgOutPointerValid(aNewAttachment);
13942
13943 AutoCaller autoCaller(this);
13944 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13945
13946 // request the host lock first, since might be calling Host methods for getting host drives;
13947 // next, protect the media tree all the while we're in here, as well as our member variables
13948 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13949 this->lockHandle(),
13950 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13951
13952 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13953
13954 Bstr ctrlName;
13955 LONG lPort;
13956 LONG lDevice;
13957 bool fTempEject;
13958 {
13959 AutoCaller autoAttachCaller(this);
13960 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13961
13962 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13963
13964 /* Need to query the details first, as the IMediumAttachment reference
13965 * might be to the original settings, which we are going to change. */
13966 ctrlName = pAttach->i_getControllerName();
13967 lPort = pAttach->i_getPort();
13968 lDevice = pAttach->i_getDevice();
13969 fTempEject = pAttach->i_getTempEject();
13970 }
13971
13972 if (!fTempEject)
13973 {
13974 /* Remember previously mounted medium. The medium before taking the
13975 * backup is not necessarily the same thing. */
13976 ComObjPtr<Medium> oldmedium;
13977 oldmedium = pAttach->i_getMedium();
13978
13979 setModified(IsModified_Storage);
13980 mMediaData.backup();
13981
13982 // The backup operation makes the pAttach reference point to the
13983 // old settings. Re-get the correct reference.
13984 pAttach = findAttachment(mMediaData->mAttachments,
13985 ctrlName.raw(),
13986 lPort,
13987 lDevice);
13988
13989 {
13990 AutoCaller autoAttachCaller(this);
13991 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13992
13993 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13994 if (!oldmedium.isNull())
13995 oldmedium->i_removeBackReference(mData->mUuid);
13996
13997 pAttach->i_updateMedium(NULL);
13998 pAttach->i_updateEjected();
13999 }
14000
14001 setModified(IsModified_Storage);
14002 }
14003 else
14004 {
14005 {
14006 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14007 pAttach->i_updateEjected();
14008 }
14009 }
14010
14011 pAttach.queryInterfaceTo(aNewAttachment);
14012
14013 return S_OK;
14014}
14015
14016// public methods only for internal purposes
14017/////////////////////////////////////////////////////////////////////////////
14018
14019#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14020/**
14021 * Called from the client watcher thread to check for expected or unexpected
14022 * death of the client process that has a direct session to this machine.
14023 *
14024 * On Win32 and on OS/2, this method is called only when we've got the
14025 * mutex (i.e. the client has either died or terminated normally) so it always
14026 * returns @c true (the client is terminated, the session machine is
14027 * uninitialized).
14028 *
14029 * On other platforms, the method returns @c true if the client process has
14030 * terminated normally or abnormally and the session machine was uninitialized,
14031 * and @c false if the client process is still alive.
14032 *
14033 * @note Locks this object for writing.
14034 */
14035bool SessionMachine::checkForDeath()
14036{
14037 Uninit::Reason reason;
14038 bool terminated = false;
14039
14040 /* Enclose autoCaller with a block because calling uninit() from under it
14041 * will deadlock. */
14042 {
14043 AutoCaller autoCaller(this);
14044 if (!autoCaller.isOk())
14045 {
14046 /* return true if not ready, to cause the client watcher to exclude
14047 * the corresponding session from watching */
14048 LogFlowThisFunc(("Already uninitialized!\n"));
14049 return true;
14050 }
14051
14052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14053
14054 /* Determine the reason of death: if the session state is Closing here,
14055 * everything is fine. Otherwise it means that the client did not call
14056 * OnSessionEnd() before it released the IPC semaphore. This may happen
14057 * either because the client process has abnormally terminated, or
14058 * because it simply forgot to call ISession::Close() before exiting. We
14059 * threat the latter also as an abnormal termination (see
14060 * Session::uninit() for details). */
14061 reason = mData->mSession.mState == SessionState_Unlocking ?
14062 Uninit::Normal :
14063 Uninit::Abnormal;
14064
14065 if (mClientToken)
14066 terminated = mClientToken->release();
14067 } /* AutoCaller block */
14068
14069 if (terminated)
14070 uninit(reason);
14071
14072 return terminated;
14073}
14074
14075void SessionMachine::getTokenId(Utf8Str &strTokenId)
14076{
14077 LogFlowThisFunc(("\n"));
14078
14079 strTokenId.setNull();
14080
14081 AutoCaller autoCaller(this);
14082 AssertComRCReturnVoid(autoCaller.rc());
14083
14084 Assert(mClientToken);
14085 if (mClientToken)
14086 mClientToken->getId(strTokenId);
14087}
14088#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14089IToken *SessionMachine::getToken()
14090{
14091 LogFlowThisFunc(("\n"));
14092
14093 AutoCaller autoCaller(this);
14094 AssertComRCReturn(autoCaller.rc(), NULL);
14095
14096 Assert(mClientToken);
14097 if (mClientToken)
14098 return mClientToken->getToken();
14099 else
14100 return NULL;
14101}
14102#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14103
14104Machine::ClientToken *SessionMachine::getClientToken()
14105{
14106 LogFlowThisFunc(("\n"));
14107
14108 AutoCaller autoCaller(this);
14109 AssertComRCReturn(autoCaller.rc(), NULL);
14110
14111 return mClientToken;
14112}
14113
14114
14115/**
14116 * @note Locks this object for reading.
14117 */
14118HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14119{
14120 LogFlowThisFunc(("\n"));
14121
14122 AutoCaller autoCaller(this);
14123 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14124
14125 ComPtr<IInternalSessionControl> directControl;
14126 {
14127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14128 directControl = mData->mSession.mDirectControl;
14129 }
14130
14131 /* ignore notifications sent after #OnSessionEnd() is called */
14132 if (!directControl)
14133 return S_OK;
14134
14135 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14136}
14137
14138/**
14139 * @note Locks this object for reading.
14140 */
14141HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14142 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
14143{
14144 LogFlowThisFunc(("\n"));
14145
14146 AutoCaller autoCaller(this);
14147 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14148
14149 ComPtr<IInternalSessionControl> directControl;
14150 {
14151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14152 directControl = mData->mSession.mDirectControl;
14153 }
14154
14155 /* ignore notifications sent after #OnSessionEnd() is called */
14156 if (!directControl)
14157 return S_OK;
14158 /*
14159 * instead acting like callback we ask IVirtualBox deliver corresponding event
14160 */
14161
14162 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14163 return S_OK;
14164}
14165
14166/**
14167 * @note Locks this object for reading.
14168 */
14169HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14170{
14171 LogFlowThisFunc(("\n"));
14172
14173 AutoCaller autoCaller(this);
14174 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14175
14176 ComPtr<IInternalSessionControl> directControl;
14177 {
14178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14179 directControl = mData->mSession.mDirectControl;
14180 }
14181
14182 /* ignore notifications sent after #OnSessionEnd() is called */
14183 if (!directControl)
14184 return S_OK;
14185
14186 return directControl->OnSerialPortChange(serialPort);
14187}
14188
14189/**
14190 * @note Locks this object for reading.
14191 */
14192HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14193{
14194 LogFlowThisFunc(("\n"));
14195
14196 AutoCaller autoCaller(this);
14197 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14198
14199 ComPtr<IInternalSessionControl> directControl;
14200 {
14201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14202 directControl = mData->mSession.mDirectControl;
14203 }
14204
14205 /* ignore notifications sent after #OnSessionEnd() is called */
14206 if (!directControl)
14207 return S_OK;
14208
14209 return directControl->OnParallelPortChange(parallelPort);
14210}
14211
14212/**
14213 * @note Locks this object for reading.
14214 */
14215HRESULT SessionMachine::onStorageControllerChange()
14216{
14217 LogFlowThisFunc(("\n"));
14218
14219 AutoCaller autoCaller(this);
14220 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14221
14222 ComPtr<IInternalSessionControl> directControl;
14223 {
14224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14225 directControl = mData->mSession.mDirectControl;
14226 }
14227
14228 /* ignore notifications sent after #OnSessionEnd() is called */
14229 if (!directControl)
14230 return S_OK;
14231
14232 return directControl->OnStorageControllerChange();
14233}
14234
14235/**
14236 * @note Locks this object for reading.
14237 */
14238HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14239{
14240 LogFlowThisFunc(("\n"));
14241
14242 AutoCaller autoCaller(this);
14243 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14244
14245 ComPtr<IInternalSessionControl> directControl;
14246 {
14247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14248 directControl = mData->mSession.mDirectControl;
14249 }
14250
14251 /* ignore notifications sent after #OnSessionEnd() is called */
14252 if (!directControl)
14253 return S_OK;
14254
14255 return directControl->OnMediumChange(aAttachment, aForce);
14256}
14257
14258/**
14259 * @note Locks this object for reading.
14260 */
14261HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14262{
14263 LogFlowThisFunc(("\n"));
14264
14265 AutoCaller autoCaller(this);
14266 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14267
14268 ComPtr<IInternalSessionControl> directControl;
14269 {
14270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14271 directControl = mData->mSession.mDirectControl;
14272 }
14273
14274 /* ignore notifications sent after #OnSessionEnd() is called */
14275 if (!directControl)
14276 return S_OK;
14277
14278 return directControl->OnCPUChange(aCPU, aRemove);
14279}
14280
14281HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14282{
14283 LogFlowThisFunc(("\n"));
14284
14285 AutoCaller autoCaller(this);
14286 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14287
14288 ComPtr<IInternalSessionControl> directControl;
14289 {
14290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14291 directControl = mData->mSession.mDirectControl;
14292 }
14293
14294 /* ignore notifications sent after #OnSessionEnd() is called */
14295 if (!directControl)
14296 return S_OK;
14297
14298 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14299}
14300
14301/**
14302 * @note Locks this object for reading.
14303 */
14304HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14305{
14306 LogFlowThisFunc(("\n"));
14307
14308 AutoCaller autoCaller(this);
14309 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14310
14311 ComPtr<IInternalSessionControl> directControl;
14312 {
14313 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14314 directControl = mData->mSession.mDirectControl;
14315 }
14316
14317 /* ignore notifications sent after #OnSessionEnd() is called */
14318 if (!directControl)
14319 return S_OK;
14320
14321 return directControl->OnVRDEServerChange(aRestart);
14322}
14323
14324/**
14325 * @note Locks this object for reading.
14326 */
14327HRESULT SessionMachine::onVideoCaptureChange()
14328{
14329 LogFlowThisFunc(("\n"));
14330
14331 AutoCaller autoCaller(this);
14332 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14333
14334 ComPtr<IInternalSessionControl> directControl;
14335 {
14336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14337 directControl = mData->mSession.mDirectControl;
14338 }
14339
14340 /* ignore notifications sent after #OnSessionEnd() is called */
14341 if (!directControl)
14342 return S_OK;
14343
14344 return directControl->OnVideoCaptureChange();
14345}
14346
14347/**
14348 * @note Locks this object for reading.
14349 */
14350HRESULT SessionMachine::onUSBControllerChange()
14351{
14352 LogFlowThisFunc(("\n"));
14353
14354 AutoCaller autoCaller(this);
14355 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14356
14357 ComPtr<IInternalSessionControl> directControl;
14358 {
14359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14360 directControl = mData->mSession.mDirectControl;
14361 }
14362
14363 /* ignore notifications sent after #OnSessionEnd() is called */
14364 if (!directControl)
14365 return S_OK;
14366
14367 return directControl->OnUSBControllerChange();
14368}
14369
14370/**
14371 * @note Locks this object for reading.
14372 */
14373HRESULT SessionMachine::onSharedFolderChange()
14374{
14375 LogFlowThisFunc(("\n"));
14376
14377 AutoCaller autoCaller(this);
14378 AssertComRCReturnRC(autoCaller.rc());
14379
14380 ComPtr<IInternalSessionControl> directControl;
14381 {
14382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14383 directControl = mData->mSession.mDirectControl;
14384 }
14385
14386 /* ignore notifications sent after #OnSessionEnd() is called */
14387 if (!directControl)
14388 return S_OK;
14389
14390 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14391}
14392
14393/**
14394 * @note Locks this object for reading.
14395 */
14396HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14397{
14398 LogFlowThisFunc(("\n"));
14399
14400 AutoCaller autoCaller(this);
14401 AssertComRCReturnRC(autoCaller.rc());
14402
14403 ComPtr<IInternalSessionControl> directControl;
14404 {
14405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14406 directControl = mData->mSession.mDirectControl;
14407 }
14408
14409 /* ignore notifications sent after #OnSessionEnd() is called */
14410 if (!directControl)
14411 return S_OK;
14412
14413 return directControl->OnClipboardModeChange(aClipboardMode);
14414}
14415
14416/**
14417 * @note Locks this object for reading.
14418 */
14419HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14420{
14421 LogFlowThisFunc(("\n"));
14422
14423 AutoCaller autoCaller(this);
14424 AssertComRCReturnRC(autoCaller.rc());
14425
14426 ComPtr<IInternalSessionControl> directControl;
14427 {
14428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14429 directControl = mData->mSession.mDirectControl;
14430 }
14431
14432 /* ignore notifications sent after #OnSessionEnd() is called */
14433 if (!directControl)
14434 return S_OK;
14435
14436 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14437}
14438
14439/**
14440 * @note Locks this object for reading.
14441 */
14442HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14443{
14444 LogFlowThisFunc(("\n"));
14445
14446 AutoCaller autoCaller(this);
14447 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14448
14449 ComPtr<IInternalSessionControl> directControl;
14450 {
14451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14452 directControl = mData->mSession.mDirectControl;
14453 }
14454
14455 /* ignore notifications sent after #OnSessionEnd() is called */
14456 if (!directControl)
14457 return S_OK;
14458
14459 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14460}
14461
14462/**
14463 * @note Locks this object for reading.
14464 */
14465HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14466{
14467 LogFlowThisFunc(("\n"));
14468
14469 AutoCaller autoCaller(this);
14470 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14471
14472 ComPtr<IInternalSessionControl> directControl;
14473 {
14474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14475 directControl = mData->mSession.mDirectControl;
14476 }
14477
14478 /* ignore notifications sent after #OnSessionEnd() is called */
14479 if (!directControl)
14480 return S_OK;
14481
14482 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14483}
14484
14485/**
14486 * Returns @c true if this machine's USB controller reports it has a matching
14487 * filter for the given USB device and @c false otherwise.
14488 *
14489 * @note locks this object for reading.
14490 */
14491bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14492{
14493 AutoCaller autoCaller(this);
14494 /* silently return if not ready -- this method may be called after the
14495 * direct machine session has been called */
14496 if (!autoCaller.isOk())
14497 return false;
14498
14499#ifdef VBOX_WITH_USB
14500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14501
14502 switch (mData->mMachineState)
14503 {
14504 case MachineState_Starting:
14505 case MachineState_Restoring:
14506 case MachineState_TeleportingIn:
14507 case MachineState_Paused:
14508 case MachineState_Running:
14509 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14510 * elsewhere... */
14511 alock.release();
14512 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14513 default: break;
14514 }
14515#else
14516 NOREF(aDevice);
14517 NOREF(aMaskedIfs);
14518#endif
14519 return false;
14520}
14521
14522/**
14523 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14524 */
14525HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14526 IVirtualBoxErrorInfo *aError,
14527 ULONG aMaskedIfs)
14528{
14529 LogFlowThisFunc(("\n"));
14530
14531 AutoCaller autoCaller(this);
14532
14533 /* This notification may happen after the machine object has been
14534 * uninitialized (the session was closed), so don't assert. */
14535 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14536
14537 ComPtr<IInternalSessionControl> directControl;
14538 {
14539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14540 directControl = mData->mSession.mDirectControl;
14541 }
14542
14543 /* fail on notifications sent after #OnSessionEnd() is called, it is
14544 * expected by the caller */
14545 if (!directControl)
14546 return E_FAIL;
14547
14548 /* No locks should be held at this point. */
14549 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14550 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14551
14552 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14553}
14554
14555/**
14556 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14557 */
14558HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14559 IVirtualBoxErrorInfo *aError)
14560{
14561 LogFlowThisFunc(("\n"));
14562
14563 AutoCaller autoCaller(this);
14564
14565 /* This notification may happen after the machine object has been
14566 * uninitialized (the session was closed), so don't assert. */
14567 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14568
14569 ComPtr<IInternalSessionControl> directControl;
14570 {
14571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14572 directControl = mData->mSession.mDirectControl;
14573 }
14574
14575 /* fail on notifications sent after #OnSessionEnd() is called, it is
14576 * expected by the caller */
14577 if (!directControl)
14578 return E_FAIL;
14579
14580 /* No locks should be held at this point. */
14581 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14582 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14583
14584 return directControl->OnUSBDeviceDetach(aId, aError);
14585}
14586
14587// protected methods
14588/////////////////////////////////////////////////////////////////////////////
14589
14590/**
14591 * Helper method to finalize saving the state.
14592 *
14593 * @note Must be called from under this object's lock.
14594 *
14595 * @param aRc S_OK if the snapshot has been taken successfully
14596 * @param aErrMsg human readable error message for failure
14597 *
14598 * @note Locks mParent + this objects for writing.
14599 */
14600HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14601{
14602 LogFlowThisFuncEnter();
14603
14604 AutoCaller autoCaller(this);
14605 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14606
14607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14608
14609 HRESULT rc = S_OK;
14610
14611 if (SUCCEEDED(aRc))
14612 {
14613 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14614
14615 /* save all VM settings */
14616 rc = saveSettings(NULL);
14617 // no need to check whether VirtualBox.xml needs saving also since
14618 // we can't have a name change pending at this point
14619 }
14620 else
14621 {
14622 // delete the saved state file (it might have been already created);
14623 // we need not check whether this is shared with a snapshot here because
14624 // we certainly created this saved state file here anew
14625 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14626 }
14627
14628 /* notify the progress object about operation completion */
14629 Assert(mConsoleTaskData.mProgress);
14630 if (SUCCEEDED(aRc))
14631 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14632 else
14633 {
14634 if (aErrMsg.length())
14635 mConsoleTaskData.mProgress->notifyComplete(aRc,
14636 COM_IIDOF(ISession),
14637 getComponentName(),
14638 aErrMsg.c_str());
14639 else
14640 mConsoleTaskData.mProgress->notifyComplete(aRc);
14641 }
14642
14643 /* clear out the temporary saved state data */
14644 mConsoleTaskData.mLastState = MachineState_Null;
14645 mConsoleTaskData.strStateFilePath.setNull();
14646 mConsoleTaskData.mProgress.setNull();
14647
14648 LogFlowThisFuncLeave();
14649 return rc;
14650}
14651
14652/**
14653 * Deletes the given file if it is no longer in use by either the current machine state
14654 * (if the machine is "saved") or any of the machine's snapshots.
14655 *
14656 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14657 * but is different for each SnapshotMachine. When calling this, the order of calling this
14658 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14659 * is therefore critical. I know, it's all rather messy.
14660 *
14661 * @param strStateFile
14662 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14663 */
14664void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14665 Snapshot *pSnapshotToIgnore)
14666{
14667 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14668 if ( (strStateFile.isNotEmpty())
14669 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14670 )
14671 // ... and it must also not be shared with other snapshots
14672 if ( !mData->mFirstSnapshot
14673 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14674 // this checks the SnapshotMachine's state file paths
14675 )
14676 RTFileDelete(strStateFile.c_str());
14677}
14678
14679/**
14680 * Locks the attached media.
14681 *
14682 * All attached hard disks are locked for writing and DVD/floppy are locked for
14683 * reading. Parents of attached hard disks (if any) are locked for reading.
14684 *
14685 * This method also performs accessibility check of all media it locks: if some
14686 * media is inaccessible, the method will return a failure and a bunch of
14687 * extended error info objects per each inaccessible medium.
14688 *
14689 * Note that this method is atomic: if it returns a success, all media are
14690 * locked as described above; on failure no media is locked at all (all
14691 * succeeded individual locks will be undone).
14692 *
14693 * The caller is responsible for doing the necessary state sanity checks.
14694 *
14695 * The locks made by this method must be undone by calling #unlockMedia() when
14696 * no more needed.
14697 */
14698HRESULT SessionMachine::lockMedia()
14699{
14700 AutoCaller autoCaller(this);
14701 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14702
14703 AutoMultiWriteLock2 alock(this->lockHandle(),
14704 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14705
14706 /* bail out if trying to lock things with already set up locking */
14707 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14708
14709 MultiResult mrc(S_OK);
14710
14711 /* Collect locking information for all medium objects attached to the VM. */
14712 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14713 it != mMediaData->mAttachments.end();
14714 ++it)
14715 {
14716 MediumAttachment* pAtt = *it;
14717 DeviceType_T devType = pAtt->i_getType();
14718 Medium *pMedium = pAtt->i_getMedium();
14719
14720 MediumLockList *pMediumLockList(new MediumLockList());
14721 // There can be attachments without a medium (floppy/dvd), and thus
14722 // it's impossible to create a medium lock list. It still makes sense
14723 // to have the empty medium lock list in the map in case a medium is
14724 // attached later.
14725 if (pMedium != NULL)
14726 {
14727 MediumType_T mediumType = pMedium->i_getType();
14728 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14729 || mediumType == MediumType_Shareable;
14730 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14731
14732 alock.release();
14733 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14734 !fIsReadOnlyLock /* fMediumLockWrite */,
14735 NULL,
14736 *pMediumLockList);
14737 alock.acquire();
14738 if (FAILED(mrc))
14739 {
14740 delete pMediumLockList;
14741 mData->mSession.mLockedMedia.Clear();
14742 break;
14743 }
14744 }
14745
14746 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14747 if (FAILED(rc))
14748 {
14749 mData->mSession.mLockedMedia.Clear();
14750 mrc = setError(rc,
14751 tr("Collecting locking information for all attached media failed"));
14752 break;
14753 }
14754 }
14755
14756 if (SUCCEEDED(mrc))
14757 {
14758 /* Now lock all media. If this fails, nothing is locked. */
14759 alock.release();
14760 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14761 alock.acquire();
14762 if (FAILED(rc))
14763 {
14764 mrc = setError(rc,
14765 tr("Locking of attached media failed"));
14766 }
14767 }
14768
14769 return mrc;
14770}
14771
14772/**
14773 * Undoes the locks made by by #lockMedia().
14774 */
14775void SessionMachine::unlockMedia()
14776{
14777 AutoCaller autoCaller(this);
14778 AssertComRCReturnVoid(autoCaller.rc());
14779
14780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14781
14782 /* we may be holding important error info on the current thread;
14783 * preserve it */
14784 ErrorInfoKeeper eik;
14785
14786 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14787 AssertComRC(rc);
14788}
14789
14790/**
14791 * Helper to change the machine state (reimplementation).
14792 *
14793 * @note Locks this object for writing.
14794 * @note This method must not call saveSettings or SaveSettings, otherwise
14795 * it can cause crashes in random places due to unexpectedly committing
14796 * the current settings. The caller is responsible for that. The call
14797 * to saveStateSettings is fine, because this method does not commit.
14798 */
14799HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14800{
14801 LogFlowThisFuncEnter();
14802 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14803
14804 AutoCaller autoCaller(this);
14805 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14806
14807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14808
14809 MachineState_T oldMachineState = mData->mMachineState;
14810
14811 AssertMsgReturn(oldMachineState != aMachineState,
14812 ("oldMachineState=%s, aMachineState=%s\n",
14813 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14814 E_FAIL);
14815
14816 HRESULT rc = S_OK;
14817
14818 int stsFlags = 0;
14819 bool deleteSavedState = false;
14820
14821 /* detect some state transitions */
14822
14823 if ( ( oldMachineState == MachineState_Saved
14824 && aMachineState == MachineState_Restoring)
14825 || ( ( oldMachineState == MachineState_PoweredOff
14826 || oldMachineState == MachineState_Teleported
14827 || oldMachineState == MachineState_Aborted
14828 )
14829 && ( aMachineState == MachineState_TeleportingIn
14830 || aMachineState == MachineState_Starting
14831 )
14832 )
14833 )
14834 {
14835 /* The EMT thread is about to start */
14836
14837 /* Nothing to do here for now... */
14838
14839 /// @todo NEWMEDIA don't let mDVDDrive and other children
14840 /// change anything when in the Starting/Restoring state
14841 }
14842 else if ( ( oldMachineState == MachineState_Running
14843 || oldMachineState == MachineState_Paused
14844 || oldMachineState == MachineState_Teleporting
14845 || oldMachineState == MachineState_LiveSnapshotting
14846 || oldMachineState == MachineState_Stuck
14847 || oldMachineState == MachineState_Starting
14848 || oldMachineState == MachineState_Stopping
14849 || oldMachineState == MachineState_Saving
14850 || oldMachineState == MachineState_Restoring
14851 || oldMachineState == MachineState_TeleportingPausedVM
14852 || oldMachineState == MachineState_TeleportingIn
14853 )
14854 && ( aMachineState == MachineState_PoweredOff
14855 || aMachineState == MachineState_Saved
14856 || aMachineState == MachineState_Teleported
14857 || aMachineState == MachineState_Aborted
14858 )
14859 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14860 * snapshot */
14861 && ( mConsoleTaskData.mSnapshot.isNull()
14862 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14863 )
14864 )
14865 {
14866 /* The EMT thread has just stopped, unlock attached media. Note that as
14867 * opposed to locking that is done from Console, we do unlocking here
14868 * because the VM process may have aborted before having a chance to
14869 * properly unlock all media it locked. */
14870
14871 unlockMedia();
14872 }
14873
14874 if (oldMachineState == MachineState_Restoring)
14875 {
14876 if (aMachineState != MachineState_Saved)
14877 {
14878 /*
14879 * delete the saved state file once the machine has finished
14880 * restoring from it (note that Console sets the state from
14881 * Restoring to Saved if the VM couldn't restore successfully,
14882 * to give the user an ability to fix an error and retry --
14883 * we keep the saved state file in this case)
14884 */
14885 deleteSavedState = true;
14886 }
14887 }
14888 else if ( oldMachineState == MachineState_Saved
14889 && ( aMachineState == MachineState_PoweredOff
14890 || aMachineState == MachineState_Aborted
14891 || aMachineState == MachineState_Teleported
14892 )
14893 )
14894 {
14895 /*
14896 * delete the saved state after Console::ForgetSavedState() is called
14897 * or if the VM process (owning a direct VM session) crashed while the
14898 * VM was Saved
14899 */
14900
14901 /// @todo (dmik)
14902 // Not sure that deleting the saved state file just because of the
14903 // client death before it attempted to restore the VM is a good
14904 // thing. But when it crashes we need to go to the Aborted state
14905 // which cannot have the saved state file associated... The only
14906 // way to fix this is to make the Aborted condition not a VM state
14907 // but a bool flag: i.e., when a crash occurs, set it to true and
14908 // change the state to PoweredOff or Saved depending on the
14909 // saved state presence.
14910
14911 deleteSavedState = true;
14912 mData->mCurrentStateModified = TRUE;
14913 stsFlags |= SaveSTS_CurStateModified;
14914 }
14915
14916 if ( aMachineState == MachineState_Starting
14917 || aMachineState == MachineState_Restoring
14918 || aMachineState == MachineState_TeleportingIn
14919 )
14920 {
14921 /* set the current state modified flag to indicate that the current
14922 * state is no more identical to the state in the
14923 * current snapshot */
14924 if (!mData->mCurrentSnapshot.isNull())
14925 {
14926 mData->mCurrentStateModified = TRUE;
14927 stsFlags |= SaveSTS_CurStateModified;
14928 }
14929 }
14930
14931 if (deleteSavedState)
14932 {
14933 if (mRemoveSavedState)
14934 {
14935 Assert(!mSSData->strStateFilePath.isEmpty());
14936
14937 // it is safe to delete the saved state file if ...
14938 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14939 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14940 // ... none of the snapshots share the saved state file
14941 )
14942 RTFileDelete(mSSData->strStateFilePath.c_str());
14943 }
14944
14945 mSSData->strStateFilePath.setNull();
14946 stsFlags |= SaveSTS_StateFilePath;
14947 }
14948
14949 /* redirect to the underlying peer machine */
14950 mPeer->setMachineState(aMachineState);
14951
14952 if ( aMachineState == MachineState_PoweredOff
14953 || aMachineState == MachineState_Teleported
14954 || aMachineState == MachineState_Aborted
14955 || aMachineState == MachineState_Saved)
14956 {
14957 /* the machine has stopped execution
14958 * (or the saved state file was adopted) */
14959 stsFlags |= SaveSTS_StateTimeStamp;
14960 }
14961
14962 if ( ( oldMachineState == MachineState_PoweredOff
14963 || oldMachineState == MachineState_Aborted
14964 || oldMachineState == MachineState_Teleported
14965 )
14966 && aMachineState == MachineState_Saved)
14967 {
14968 /* the saved state file was adopted */
14969 Assert(!mSSData->strStateFilePath.isEmpty());
14970 stsFlags |= SaveSTS_StateFilePath;
14971 }
14972
14973#ifdef VBOX_WITH_GUEST_PROPS
14974 if ( aMachineState == MachineState_PoweredOff
14975 || aMachineState == MachineState_Aborted
14976 || aMachineState == MachineState_Teleported)
14977 {
14978 /* Make sure any transient guest properties get removed from the
14979 * property store on shutdown. */
14980
14981 HWData::GuestPropertyMap::const_iterator it;
14982 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14983 if (!fNeedsSaving)
14984 for (it = mHWData->mGuestProperties.begin();
14985 it != mHWData->mGuestProperties.end(); ++it)
14986 if ( (it->second.mFlags & guestProp::TRANSIENT)
14987 || (it->second.mFlags & guestProp::TRANSRESET))
14988 {
14989 fNeedsSaving = true;
14990 break;
14991 }
14992 if (fNeedsSaving)
14993 {
14994 mData->mCurrentStateModified = TRUE;
14995 stsFlags |= SaveSTS_CurStateModified;
14996 }
14997 }
14998#endif
14999
15000 rc = saveStateSettings(stsFlags);
15001
15002 if ( ( oldMachineState != MachineState_PoweredOff
15003 && oldMachineState != MachineState_Aborted
15004 && oldMachineState != MachineState_Teleported
15005 )
15006 && ( aMachineState == MachineState_PoweredOff
15007 || aMachineState == MachineState_Aborted
15008 || aMachineState == MachineState_Teleported
15009 )
15010 )
15011 {
15012 /* we've been shut down for any reason */
15013 /* no special action so far */
15014 }
15015
15016 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15017 LogFlowThisFuncLeave();
15018 return rc;
15019}
15020
15021/**
15022 * Sends the current machine state value to the VM process.
15023 *
15024 * @note Locks this object for reading, then calls a client process.
15025 */
15026HRESULT SessionMachine::updateMachineStateOnClient()
15027{
15028 AutoCaller autoCaller(this);
15029 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15030
15031 ComPtr<IInternalSessionControl> directControl;
15032 {
15033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15034 AssertReturn(!!mData, E_FAIL);
15035 directControl = mData->mSession.mDirectControl;
15036
15037 /* directControl may be already set to NULL here in #OnSessionEnd()
15038 * called too early by the direct session process while there is still
15039 * some operation (like deleting the snapshot) in progress. The client
15040 * process in this case is waiting inside Session::close() for the
15041 * "end session" process object to complete, while #uninit() called by
15042 * #checkForDeath() on the Watcher thread is waiting for the pending
15043 * operation to complete. For now, we accept this inconsistent behavior
15044 * and simply do nothing here. */
15045
15046 if (mData->mSession.mState == SessionState_Unlocking)
15047 return S_OK;
15048
15049 AssertReturn(!directControl.isNull(), E_FAIL);
15050 }
15051
15052 return directControl->UpdateMachineState(mData->mMachineState);
15053}
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