VirtualBox

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

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

build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 506.9 KB
Line 
1/* $Id: MachineImpl.cpp 50297 2014-01-31 10:20:54Z 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.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#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#ifdef VBOX_WITH_VMSVGA
1937 case GraphicsControllerType_VMSVGA:
1938#endif
1939 break;
1940 default:
1941 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1942 }
1943
1944 AutoCaller autoCaller(this);
1945 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1946
1947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1948
1949 HRESULT rc = checkStateDependency(MutableStateDep);
1950 if (FAILED(rc)) return rc;
1951
1952 setModified(IsModified_MachineData);
1953 mHWData.backup();
1954 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1955
1956 return S_OK;
1957}
1958
1959STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1960{
1961 CheckComArgOutPointerValid(memorySize);
1962
1963 AutoCaller autoCaller(this);
1964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1965
1966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1967
1968 *memorySize = mHWData->mVRAMSize;
1969
1970 return S_OK;
1971}
1972
1973STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1974{
1975 /* check VRAM limits */
1976 if (memorySize < SchemaDefs::MinGuestVRAM ||
1977 memorySize > SchemaDefs::MaxGuestVRAM)
1978 return setError(E_INVALIDARG,
1979 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1980 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1981
1982 AutoCaller autoCaller(this);
1983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1984
1985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1986
1987 HRESULT rc = checkStateDependency(MutableStateDep);
1988 if (FAILED(rc)) return rc;
1989
1990 setModified(IsModified_MachineData);
1991 mHWData.backup();
1992 mHWData->mVRAMSize = memorySize;
1993
1994 return S_OK;
1995}
1996
1997/** @todo this method should not be public */
1998STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1999{
2000 CheckComArgOutPointerValid(memoryBalloonSize);
2001
2002 AutoCaller autoCaller(this);
2003 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2004
2005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2006
2007 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2008
2009 return S_OK;
2010}
2011
2012/**
2013 * Set the memory balloon size.
2014 *
2015 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2016 * we have to make sure that we never call IGuest from here.
2017 */
2018STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2019{
2020 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2021#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2022 /* check limits */
2023 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2024 return setError(E_INVALIDARG,
2025 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2026 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2027
2028 AutoCaller autoCaller(this);
2029 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2030
2031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2032
2033 setModified(IsModified_MachineData);
2034 mHWData.backup();
2035 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2036
2037 return S_OK;
2038#else
2039 NOREF(memoryBalloonSize);
2040 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2041#endif
2042}
2043
2044STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2045{
2046 CheckComArgOutPointerValid(aEnabled);
2047
2048 AutoCaller autoCaller(this);
2049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2050
2051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2052
2053 *aEnabled = mHWData->mPageFusionEnabled;
2054 return S_OK;
2055}
2056
2057STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2058{
2059#ifdef VBOX_WITH_PAGE_SHARING
2060 AutoCaller autoCaller(this);
2061 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2062
2063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2064
2065 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2066 setModified(IsModified_MachineData);
2067 mHWData.backup();
2068 mHWData->mPageFusionEnabled = aEnabled;
2069 return S_OK;
2070#else
2071 NOREF(aEnabled);
2072 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2073#endif
2074}
2075
2076STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2077{
2078 CheckComArgOutPointerValid(aEnabled);
2079
2080 AutoCaller autoCaller(this);
2081 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2082
2083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2084
2085 *aEnabled = mHWData->mAccelerate3DEnabled;
2086
2087 return S_OK;
2088}
2089
2090STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2091{
2092 AutoCaller autoCaller(this);
2093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2094
2095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2096
2097 HRESULT rc = checkStateDependency(MutableStateDep);
2098 if (FAILED(rc)) return rc;
2099
2100 /** @todo check validity! */
2101
2102 setModified(IsModified_MachineData);
2103 mHWData.backup();
2104 mHWData->mAccelerate3DEnabled = enable;
2105
2106 return S_OK;
2107}
2108
2109
2110STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2111{
2112 CheckComArgOutPointerValid(aEnabled);
2113
2114 AutoCaller autoCaller(this);
2115 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2116
2117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2118
2119 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2120
2121 return S_OK;
2122}
2123
2124STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2125{
2126 AutoCaller autoCaller(this);
2127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2128
2129 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2130
2131 HRESULT rc = checkStateDependency(MutableStateDep);
2132 if (FAILED(rc)) return rc;
2133
2134 /** @todo check validity! */
2135
2136 setModified(IsModified_MachineData);
2137 mHWData.backup();
2138 mHWData->mAccelerate2DVideoEnabled = enable;
2139
2140 return S_OK;
2141}
2142
2143STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2144{
2145 CheckComArgOutPointerValid(monitorCount);
2146
2147 AutoCaller autoCaller(this);
2148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2149
2150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2151
2152 *monitorCount = mHWData->mMonitorCount;
2153
2154 return S_OK;
2155}
2156
2157STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2158{
2159 /* make sure monitor count is a sensible number */
2160 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2161 return setError(E_INVALIDARG,
2162 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2163 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2164
2165 AutoCaller autoCaller(this);
2166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2167
2168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2169
2170 HRESULT rc = checkStateDependency(MutableStateDep);
2171 if (FAILED(rc)) return rc;
2172
2173 setModified(IsModified_MachineData);
2174 mHWData.backup();
2175 mHWData->mMonitorCount = monitorCount;
2176
2177 return S_OK;
2178}
2179
2180STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2181{
2182 CheckComArgOutPointerValid(biosSettings);
2183
2184 AutoCaller autoCaller(this);
2185 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2186
2187 /* mBIOSSettings is constant during life time, no need to lock */
2188 mBIOSSettings.queryInterfaceTo(biosSettings);
2189
2190 return S_OK;
2191}
2192
2193STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2194{
2195 CheckComArgOutPointerValid(aVal);
2196
2197 AutoCaller autoCaller(this);
2198 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2199
2200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2201
2202 switch (property)
2203 {
2204 case CPUPropertyType_PAE:
2205 *aVal = mHWData->mPAEEnabled;
2206 break;
2207
2208 case CPUPropertyType_Synthetic:
2209 *aVal = mHWData->mSyntheticCpu;
2210 break;
2211
2212 case CPUPropertyType_LongMode:
2213 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2214 *aVal = TRUE;
2215 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2216 *aVal = FALSE;
2217#if HC_ARCH_BITS == 64
2218 else
2219 *aVal = TRUE;
2220#else
2221 else
2222 {
2223 *aVal = FALSE;
2224
2225 ComPtr<IGuestOSType> ptrGuestOSType;
2226 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2227 if (SUCCEEDED(hrc2))
2228 {
2229 BOOL fIs64Bit = FALSE;
2230 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2231 if (SUCCEEDED(hrc2) && fIs64Bit)
2232 {
2233 ComObjPtr<Host> ptrHost = mParent->host();
2234 alock.release();
2235
2236 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2237 if (FAILED(hrc2))
2238 *aVal = FALSE;
2239 }
2240 }
2241 }
2242#endif
2243 break;
2244
2245 case CPUPropertyType_TripleFaultReset:
2246 *aVal = mHWData->mTripleFaultReset;
2247 break;
2248
2249 default:
2250 return E_INVALIDARG;
2251 }
2252 return S_OK;
2253}
2254
2255STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2256{
2257 AutoCaller autoCaller(this);
2258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2259
2260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2261
2262 HRESULT rc = checkStateDependency(MutableStateDep);
2263 if (FAILED(rc)) return rc;
2264
2265 switch (property)
2266 {
2267 case CPUPropertyType_PAE:
2268 setModified(IsModified_MachineData);
2269 mHWData.backup();
2270 mHWData->mPAEEnabled = !!aVal;
2271 break;
2272
2273 case CPUPropertyType_Synthetic:
2274 setModified(IsModified_MachineData);
2275 mHWData.backup();
2276 mHWData->mSyntheticCpu = !!aVal;
2277 break;
2278
2279 case CPUPropertyType_LongMode:
2280 setModified(IsModified_MachineData);
2281 mHWData.backup();
2282 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2283 break;
2284
2285 case CPUPropertyType_TripleFaultReset:
2286 setModified(IsModified_MachineData);
2287 mHWData.backup();
2288 mHWData->mTripleFaultReset = !!aVal;
2289 break;
2290
2291 default:
2292 return E_INVALIDARG;
2293 }
2294 return S_OK;
2295}
2296
2297STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2298{
2299 CheckComArgOutPointerValid(aValEax);
2300 CheckComArgOutPointerValid(aValEbx);
2301 CheckComArgOutPointerValid(aValEcx);
2302 CheckComArgOutPointerValid(aValEdx);
2303
2304 AutoCaller autoCaller(this);
2305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2306
2307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2308
2309 switch(aId)
2310 {
2311 case 0x0:
2312 case 0x1:
2313 case 0x2:
2314 case 0x3:
2315 case 0x4:
2316 case 0x5:
2317 case 0x6:
2318 case 0x7:
2319 case 0x8:
2320 case 0x9:
2321 case 0xA:
2322 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2323 return E_INVALIDARG;
2324
2325 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2326 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2327 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2328 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2329 break;
2330
2331 case 0x80000000:
2332 case 0x80000001:
2333 case 0x80000002:
2334 case 0x80000003:
2335 case 0x80000004:
2336 case 0x80000005:
2337 case 0x80000006:
2338 case 0x80000007:
2339 case 0x80000008:
2340 case 0x80000009:
2341 case 0x8000000A:
2342 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2343 return E_INVALIDARG;
2344
2345 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2346 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2347 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2348 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2349 break;
2350
2351 default:
2352 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2353 }
2354 return S_OK;
2355}
2356
2357STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2358{
2359 AutoCaller autoCaller(this);
2360 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2361
2362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2363
2364 HRESULT rc = checkStateDependency(MutableStateDep);
2365 if (FAILED(rc)) return rc;
2366
2367 switch(aId)
2368 {
2369 case 0x0:
2370 case 0x1:
2371 case 0x2:
2372 case 0x3:
2373 case 0x4:
2374 case 0x5:
2375 case 0x6:
2376 case 0x7:
2377 case 0x8:
2378 case 0x9:
2379 case 0xA:
2380 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2381 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2382 setModified(IsModified_MachineData);
2383 mHWData.backup();
2384 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2385 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2386 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2387 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2388 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2389 break;
2390
2391 case 0x80000000:
2392 case 0x80000001:
2393 case 0x80000002:
2394 case 0x80000003:
2395 case 0x80000004:
2396 case 0x80000005:
2397 case 0x80000006:
2398 case 0x80000007:
2399 case 0x80000008:
2400 case 0x80000009:
2401 case 0x8000000A:
2402 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2403 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2404 setModified(IsModified_MachineData);
2405 mHWData.backup();
2406 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2407 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2408 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2409 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2410 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2411 break;
2412
2413 default:
2414 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2415 }
2416 return S_OK;
2417}
2418
2419STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2420{
2421 AutoCaller autoCaller(this);
2422 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2423
2424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2425
2426 HRESULT rc = checkStateDependency(MutableStateDep);
2427 if (FAILED(rc)) return rc;
2428
2429 switch(aId)
2430 {
2431 case 0x0:
2432 case 0x1:
2433 case 0x2:
2434 case 0x3:
2435 case 0x4:
2436 case 0x5:
2437 case 0x6:
2438 case 0x7:
2439 case 0x8:
2440 case 0x9:
2441 case 0xA:
2442 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2443 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2444 setModified(IsModified_MachineData);
2445 mHWData.backup();
2446 /* Invalidate leaf. */
2447 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2448 break;
2449
2450 case 0x80000000:
2451 case 0x80000001:
2452 case 0x80000002:
2453 case 0x80000003:
2454 case 0x80000004:
2455 case 0x80000005:
2456 case 0x80000006:
2457 case 0x80000007:
2458 case 0x80000008:
2459 case 0x80000009:
2460 case 0x8000000A:
2461 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2462 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2463 setModified(IsModified_MachineData);
2464 mHWData.backup();
2465 /* Invalidate leaf. */
2466 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2467 break;
2468
2469 default:
2470 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2471 }
2472 return S_OK;
2473}
2474
2475STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2476{
2477 AutoCaller autoCaller(this);
2478 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2479
2480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2481
2482 HRESULT rc = checkStateDependency(MutableStateDep);
2483 if (FAILED(rc)) return rc;
2484
2485 setModified(IsModified_MachineData);
2486 mHWData.backup();
2487
2488 /* Invalidate all standard leafs. */
2489 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2490 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2491
2492 /* Invalidate all extended leafs. */
2493 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2494 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2495
2496 return S_OK;
2497}
2498
2499STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2500{
2501 CheckComArgOutPointerValid(aVal);
2502
2503 AutoCaller autoCaller(this);
2504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2505
2506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2507
2508 switch(property)
2509 {
2510 case HWVirtExPropertyType_Enabled:
2511 *aVal = mHWData->mHWVirtExEnabled;
2512 break;
2513
2514 case HWVirtExPropertyType_VPID:
2515 *aVal = mHWData->mHWVirtExVPIDEnabled;
2516 break;
2517
2518 case HWVirtExPropertyType_NestedPaging:
2519 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2520 break;
2521
2522 case HWVirtExPropertyType_UnrestrictedExecution:
2523 *aVal = mHWData->mHWVirtExUXEnabled;
2524 break;
2525
2526 case HWVirtExPropertyType_LargePages:
2527 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2528#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2529 *aVal = FALSE;
2530#endif
2531 break;
2532
2533 case HWVirtExPropertyType_Force:
2534 *aVal = mHWData->mHWVirtExForceEnabled;
2535 break;
2536
2537 default:
2538 return E_INVALIDARG;
2539 }
2540 return S_OK;
2541}
2542
2543STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2544{
2545 AutoCaller autoCaller(this);
2546 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2547
2548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2549
2550 HRESULT rc = checkStateDependency(MutableStateDep);
2551 if (FAILED(rc)) return rc;
2552
2553 switch(property)
2554 {
2555 case HWVirtExPropertyType_Enabled:
2556 setModified(IsModified_MachineData);
2557 mHWData.backup();
2558 mHWData->mHWVirtExEnabled = !!aVal;
2559 break;
2560
2561 case HWVirtExPropertyType_VPID:
2562 setModified(IsModified_MachineData);
2563 mHWData.backup();
2564 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2565 break;
2566
2567 case HWVirtExPropertyType_NestedPaging:
2568 setModified(IsModified_MachineData);
2569 mHWData.backup();
2570 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2571 break;
2572
2573 case HWVirtExPropertyType_UnrestrictedExecution:
2574 setModified(IsModified_MachineData);
2575 mHWData.backup();
2576 mHWData->mHWVirtExUXEnabled = !!aVal;
2577 break;
2578
2579 case HWVirtExPropertyType_LargePages:
2580 setModified(IsModified_MachineData);
2581 mHWData.backup();
2582 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2583 break;
2584
2585 case HWVirtExPropertyType_Force:
2586 setModified(IsModified_MachineData);
2587 mHWData.backup();
2588 mHWData->mHWVirtExForceEnabled = !!aVal;
2589 break;
2590
2591 default:
2592 return E_INVALIDARG;
2593 }
2594
2595 return S_OK;
2596}
2597
2598STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2599{
2600 CheckComArgOutPointerValid(aSnapshotFolder);
2601
2602 AutoCaller autoCaller(this);
2603 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2604
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 Utf8Str strFullSnapshotFolder;
2608 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2609 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2610
2611 return S_OK;
2612}
2613
2614STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2615{
2616 /* @todo (r=dmik):
2617 * 1. Allow to change the name of the snapshot folder containing snapshots
2618 * 2. Rename the folder on disk instead of just changing the property
2619 * value (to be smart and not to leave garbage). Note that it cannot be
2620 * done here because the change may be rolled back. Thus, the right
2621 * place is #saveSettings().
2622 */
2623
2624 AutoCaller autoCaller(this);
2625 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2626
2627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2628
2629 HRESULT rc = checkStateDependency(MutableStateDep);
2630 if (FAILED(rc)) return rc;
2631
2632 if (!mData->mCurrentSnapshot.isNull())
2633 return setError(E_FAIL,
2634 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2635
2636 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2637
2638 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2639 if (strSnapshotFolder.isEmpty())
2640 strSnapshotFolder = "Snapshots";
2641 int vrc = calculateFullPath(strSnapshotFolder,
2642 strSnapshotFolder);
2643 if (RT_FAILURE(vrc))
2644 return setError(E_FAIL,
2645 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2646 aSnapshotFolder, vrc);
2647
2648 setModified(IsModified_MachineData);
2649 mUserData.backup();
2650
2651 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2652
2653 return S_OK;
2654}
2655
2656STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2657{
2658 CheckComArgOutSafeArrayPointerValid(aAttachments);
2659
2660 AutoCaller autoCaller(this);
2661 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2662
2663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2664
2665 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2666 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2667
2668 return S_OK;
2669}
2670
2671STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2672{
2673 CheckComArgOutPointerValid(vrdeServer);
2674
2675 AutoCaller autoCaller(this);
2676 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2677
2678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2679
2680 Assert(!!mVRDEServer);
2681 mVRDEServer.queryInterfaceTo(vrdeServer);
2682
2683 return S_OK;
2684}
2685
2686STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2687{
2688 CheckComArgOutPointerValid(audioAdapter);
2689
2690 AutoCaller autoCaller(this);
2691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2692
2693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 mAudioAdapter.queryInterfaceTo(audioAdapter);
2696 return S_OK;
2697}
2698
2699STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2700{
2701#ifdef VBOX_WITH_VUSB
2702 CheckComArgOutPointerValid(aUSBControllers);
2703
2704 AutoCaller autoCaller(this);
2705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2706
2707 clearError();
2708 MultiResult rc(S_OK);
2709
2710# ifdef VBOX_WITH_USB
2711 rc = mParent->host()->i_checkUSBProxyService();
2712 if (FAILED(rc)) return rc;
2713# endif
2714
2715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2718 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2719 return S_OK;
2720#else
2721 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2722 * extended error info to indicate that USB is simply not available
2723 * (w/o treating it as a failure), for example, as in OSE */
2724 NOREF(aUSBControllers);
2725 ReturnComNotImplemented();
2726#endif /* VBOX_WITH_VUSB */
2727}
2728
2729STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2730{
2731#ifdef VBOX_WITH_VUSB
2732 CheckComArgOutPointerValid(aUSBDeviceFilters);
2733
2734 AutoCaller autoCaller(this);
2735 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2736
2737 clearError();
2738 MultiResult rc(S_OK);
2739
2740# ifdef VBOX_WITH_USB
2741 rc = mParent->host()->i_checkUSBProxyService();
2742 if (FAILED(rc)) return rc;
2743# endif
2744
2745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2746
2747 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2748#else
2749 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2750 * extended error info to indicate that USB is simply not available
2751 * (w/o treating it as a failure), for example, as in OSE */
2752 NOREF(aUSBDeviceFilters);
2753 ReturnComNotImplemented();
2754#endif /* VBOX_WITH_VUSB */
2755}
2756
2757STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2758{
2759 CheckComArgOutPointerValid(aFilePath);
2760
2761 AutoLimitedCaller autoCaller(this);
2762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2763
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 mData->m_strConfigFileFull.cloneTo(aFilePath);
2767 return S_OK;
2768}
2769
2770STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2771{
2772 CheckComArgOutPointerValid(aModified);
2773
2774 AutoCaller autoCaller(this);
2775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2776
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 HRESULT rc = checkStateDependency(MutableStateDep);
2780 if (FAILED(rc)) return rc;
2781
2782 if (!mData->pMachineConfigFile->fileExists())
2783 // this is a new machine, and no config file exists yet:
2784 *aModified = TRUE;
2785 else
2786 *aModified = (mData->flModifications != 0);
2787
2788 return S_OK;
2789}
2790
2791STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2792{
2793 CheckComArgOutPointerValid(aSessionState);
2794
2795 AutoCaller autoCaller(this);
2796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2797
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 *aSessionState = mData->mSession.mState;
2801
2802 return S_OK;
2803}
2804
2805STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2806{
2807 CheckComArgOutPointerValid(aSessionType);
2808
2809 AutoCaller autoCaller(this);
2810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2811
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 mData->mSession.mType.cloneTo(aSessionType);
2815
2816 return S_OK;
2817}
2818
2819STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2820{
2821 CheckComArgOutPointerValid(aSessionPID);
2822
2823 AutoCaller autoCaller(this);
2824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2825
2826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 *aSessionPID = mData->mSession.mPID;
2829
2830 return S_OK;
2831}
2832
2833STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2834{
2835 CheckComArgOutPointerValid(machineState);
2836
2837 AutoCaller autoCaller(this);
2838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2839
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 *machineState = mData->mMachineState;
2843
2844 return S_OK;
2845}
2846
2847STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2848{
2849 CheckComArgOutPointerValid(aLastStateChange);
2850
2851 AutoCaller autoCaller(this);
2852 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2853
2854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2857
2858 return S_OK;
2859}
2860
2861STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2862{
2863 CheckComArgOutPointerValid(aStateFilePath);
2864
2865 AutoCaller autoCaller(this);
2866 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2867
2868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2871
2872 return S_OK;
2873}
2874
2875STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2876{
2877 CheckComArgOutPointerValid(aLogFolder);
2878
2879 AutoCaller autoCaller(this);
2880 AssertComRCReturnRC(autoCaller.rc());
2881
2882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2883
2884 Utf8Str logFolder;
2885 getLogFolder(logFolder);
2886 logFolder.cloneTo(aLogFolder);
2887
2888 return S_OK;
2889}
2890
2891STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2892{
2893 CheckComArgOutPointerValid(aCurrentSnapshot);
2894
2895 AutoCaller autoCaller(this);
2896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2897
2898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2901
2902 return S_OK;
2903}
2904
2905STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2906{
2907 CheckComArgOutPointerValid(aSnapshotCount);
2908
2909 AutoCaller autoCaller(this);
2910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2911
2912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2913
2914 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2915 ? 0
2916 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2917
2918 return S_OK;
2919}
2920
2921STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2922{
2923 CheckComArgOutPointerValid(aCurrentStateModified);
2924
2925 AutoCaller autoCaller(this);
2926 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2927
2928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 /* Note: for machines with no snapshots, we always return FALSE
2931 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2932 * reasons :) */
2933
2934 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2935 ? FALSE
2936 : mData->mCurrentStateModified;
2937
2938 return S_OK;
2939}
2940
2941STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2942{
2943 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2944
2945 AutoCaller autoCaller(this);
2946 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2947
2948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2949
2950 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2951 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2952
2953 return S_OK;
2954}
2955
2956STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2957{
2958 CheckComArgOutPointerValid(aClipboardMode);
2959
2960 AutoCaller autoCaller(this);
2961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2962
2963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2964
2965 *aClipboardMode = mHWData->mClipboardMode;
2966
2967 return S_OK;
2968}
2969
2970STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2971{
2972 HRESULT rc = S_OK;
2973
2974 AutoCaller autoCaller(this);
2975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2976
2977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2978
2979 alock.release();
2980 rc = onClipboardModeChange(aClipboardMode);
2981 alock.acquire();
2982 if (FAILED(rc)) return rc;
2983
2984 setModified(IsModified_MachineData);
2985 mHWData.backup();
2986 mHWData->mClipboardMode = aClipboardMode;
2987
2988 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2989 if (Global::IsOnline(mData->mMachineState))
2990 saveSettings(NULL);
2991
2992 return S_OK;
2993}
2994
2995STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2996{
2997 CheckComArgOutPointerValid(aDragAndDropMode);
2998
2999 AutoCaller autoCaller(this);
3000 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3001
3002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 *aDragAndDropMode = mHWData->mDragAndDropMode;
3005
3006 return S_OK;
3007}
3008
3009STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3010{
3011 HRESULT rc = S_OK;
3012
3013 AutoCaller autoCaller(this);
3014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3015
3016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3017
3018 alock.release();
3019 rc = onDragAndDropModeChange(aDragAndDropMode);
3020 alock.acquire();
3021 if (FAILED(rc)) return rc;
3022
3023 setModified(IsModified_MachineData);
3024 mHWData.backup();
3025 mHWData->mDragAndDropMode = aDragAndDropMode;
3026
3027 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3028 if (Global::IsOnline(mData->mMachineState))
3029 saveSettings(NULL);
3030
3031 return S_OK;
3032}
3033
3034STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3035{
3036 CheckComArgOutPointerValid(aPatterns);
3037
3038 AutoCaller autoCaller(this);
3039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3040
3041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3042
3043 try
3044 {
3045 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3046 }
3047 catch (...)
3048 {
3049 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3050 }
3051
3052 return S_OK;
3053}
3054
3055STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3056{
3057 AutoCaller autoCaller(this);
3058 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3059
3060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3061
3062 HRESULT rc = checkStateDependency(MutableStateDep);
3063 if (FAILED(rc)) return rc;
3064
3065 setModified(IsModified_MachineData);
3066 mHWData.backup();
3067 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3068 return rc;
3069}
3070
3071STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3072{
3073 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3074
3075 AutoCaller autoCaller(this);
3076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3077
3078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3081 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3082
3083 return S_OK;
3084}
3085
3086STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3087{
3088 CheckComArgOutPointerValid(aEnabled);
3089
3090 AutoCaller autoCaller(this);
3091 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3092
3093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3094
3095 *aEnabled = mUserData->s.fTeleporterEnabled;
3096
3097 return S_OK;
3098}
3099
3100STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3101{
3102 AutoCaller autoCaller(this);
3103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3104
3105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3106
3107 /* Only allow it to be set to true when PoweredOff or Aborted.
3108 (Clearing it is always permitted.) */
3109 if ( aEnabled
3110 && mData->mRegistered
3111 && ( !isSessionMachine()
3112 || ( mData->mMachineState != MachineState_PoweredOff
3113 && mData->mMachineState != MachineState_Teleported
3114 && mData->mMachineState != MachineState_Aborted
3115 )
3116 )
3117 )
3118 return setError(VBOX_E_INVALID_VM_STATE,
3119 tr("The machine is not powered off (state is %s)"),
3120 Global::stringifyMachineState(mData->mMachineState));
3121
3122 setModified(IsModified_MachineData);
3123 mUserData.backup();
3124 mUserData->s.fTeleporterEnabled = !!aEnabled;
3125
3126 return S_OK;
3127}
3128
3129STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3130{
3131 CheckComArgOutPointerValid(aPort);
3132
3133 AutoCaller autoCaller(this);
3134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3135
3136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3139
3140 return S_OK;
3141}
3142
3143STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3144{
3145 if (aPort >= _64K)
3146 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3147
3148 AutoCaller autoCaller(this);
3149 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3150
3151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3152
3153 HRESULT rc = checkStateDependency(MutableStateDep);
3154 if (FAILED(rc)) return rc;
3155
3156 setModified(IsModified_MachineData);
3157 mUserData.backup();
3158 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3159
3160 return S_OK;
3161}
3162
3163STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3164{
3165 CheckComArgOutPointerValid(aAddress);
3166
3167 AutoCaller autoCaller(this);
3168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3169
3170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3171
3172 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3173
3174 return S_OK;
3175}
3176
3177STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3178{
3179 AutoCaller autoCaller(this);
3180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3181
3182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3183
3184 HRESULT rc = checkStateDependency(MutableStateDep);
3185 if (FAILED(rc)) return rc;
3186
3187 setModified(IsModified_MachineData);
3188 mUserData.backup();
3189 mUserData->s.strTeleporterAddress = aAddress;
3190
3191 return S_OK;
3192}
3193
3194STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3195{
3196 CheckComArgOutPointerValid(aPassword);
3197
3198 AutoCaller autoCaller(this);
3199 HRESULT hrc = autoCaller.rc();
3200 if (SUCCEEDED(hrc))
3201 {
3202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3203 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3204 }
3205
3206 return hrc;
3207}
3208
3209STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3210{
3211 /*
3212 * Hash the password first.
3213 */
3214 Utf8Str strPassword(aPassword);
3215 if (!strPassword.isEmpty())
3216 {
3217 if (VBoxIsPasswordHashed(&strPassword))
3218 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3219 VBoxHashPassword(&strPassword);
3220 }
3221
3222 /*
3223 * Do the update.
3224 */
3225 AutoCaller autoCaller(this);
3226 HRESULT hrc = autoCaller.rc();
3227 if (SUCCEEDED(hrc))
3228 {
3229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3230 hrc = checkStateDependency(MutableStateDep);
3231 if (SUCCEEDED(hrc))
3232 {
3233 setModified(IsModified_MachineData);
3234 mUserData.backup();
3235 mUserData->s.strTeleporterPassword = strPassword;
3236 }
3237 }
3238
3239 return hrc;
3240}
3241
3242STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3243{
3244 CheckComArgOutPointerValid(aState);
3245
3246 AutoCaller autoCaller(this);
3247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3248
3249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3250
3251 *aState = mUserData->s.enmFaultToleranceState;
3252 return S_OK;
3253}
3254
3255STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3256{
3257 AutoCaller autoCaller(this);
3258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3259
3260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3261
3262 /* @todo deal with running state change. */
3263 HRESULT rc = checkStateDependency(MutableStateDep);
3264 if (FAILED(rc)) return rc;
3265
3266 setModified(IsModified_MachineData);
3267 mUserData.backup();
3268 mUserData->s.enmFaultToleranceState = aState;
3269 return S_OK;
3270}
3271
3272STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3273{
3274 CheckComArgOutPointerValid(aAddress);
3275
3276 AutoCaller autoCaller(this);
3277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3278
3279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3280
3281 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3282 return S_OK;
3283}
3284
3285STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3286{
3287 AutoCaller autoCaller(this);
3288 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3289
3290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3291
3292 /* @todo deal with running state change. */
3293 HRESULT rc = checkStateDependency(MutableStateDep);
3294 if (FAILED(rc)) return rc;
3295
3296 setModified(IsModified_MachineData);
3297 mUserData.backup();
3298 mUserData->s.strFaultToleranceAddress = aAddress;
3299 return S_OK;
3300}
3301
3302STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3303{
3304 CheckComArgOutPointerValid(aPort);
3305
3306 AutoCaller autoCaller(this);
3307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3308
3309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3310
3311 *aPort = mUserData->s.uFaultTolerancePort;
3312 return S_OK;
3313}
3314
3315STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3316{
3317 AutoCaller autoCaller(this);
3318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3319
3320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3321
3322 /* @todo deal with running state change. */
3323 HRESULT rc = checkStateDependency(MutableStateDep);
3324 if (FAILED(rc)) return rc;
3325
3326 setModified(IsModified_MachineData);
3327 mUserData.backup();
3328 mUserData->s.uFaultTolerancePort = aPort;
3329 return S_OK;
3330}
3331
3332STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3333{
3334 CheckComArgOutPointerValid(aPassword);
3335
3336 AutoCaller autoCaller(this);
3337 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3338
3339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3340
3341 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3342
3343 return S_OK;
3344}
3345
3346STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3347{
3348 AutoCaller autoCaller(this);
3349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3350
3351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3352
3353 /* @todo deal with running state change. */
3354 HRESULT rc = checkStateDependency(MutableStateDep);
3355 if (FAILED(rc)) return rc;
3356
3357 setModified(IsModified_MachineData);
3358 mUserData.backup();
3359 mUserData->s.strFaultTolerancePassword = aPassword;
3360
3361 return S_OK;
3362}
3363
3364STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3365{
3366 CheckComArgOutPointerValid(aInterval);
3367
3368 AutoCaller autoCaller(this);
3369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3370
3371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3372
3373 *aInterval = mUserData->s.uFaultToleranceInterval;
3374 return S_OK;
3375}
3376
3377STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3378{
3379 AutoCaller autoCaller(this);
3380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3381
3382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3383
3384 /* @todo deal with running state change. */
3385 HRESULT rc = checkStateDependency(MutableStateDep);
3386 if (FAILED(rc)) return rc;
3387
3388 setModified(IsModified_MachineData);
3389 mUserData.backup();
3390 mUserData->s.uFaultToleranceInterval = aInterval;
3391 return S_OK;
3392}
3393
3394STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3395{
3396 CheckComArgOutPointerValid(aEnabled);
3397
3398 AutoCaller autoCaller(this);
3399 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3400
3401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3402
3403 *aEnabled = mUserData->s.fRTCUseUTC;
3404
3405 return S_OK;
3406}
3407
3408STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3409{
3410 AutoCaller autoCaller(this);
3411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3412
3413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3414
3415 /* Only allow it to be set to true when PoweredOff or Aborted.
3416 (Clearing it is always permitted.) */
3417 if ( aEnabled
3418 && mData->mRegistered
3419 && ( !isSessionMachine()
3420 || ( mData->mMachineState != MachineState_PoweredOff
3421 && mData->mMachineState != MachineState_Teleported
3422 && mData->mMachineState != MachineState_Aborted
3423 )
3424 )
3425 )
3426 return setError(VBOX_E_INVALID_VM_STATE,
3427 tr("The machine is not powered off (state is %s)"),
3428 Global::stringifyMachineState(mData->mMachineState));
3429
3430 setModified(IsModified_MachineData);
3431 mUserData.backup();
3432 mUserData->s.fRTCUseUTC = !!aEnabled;
3433
3434 return S_OK;
3435}
3436
3437STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3438{
3439 CheckComArgOutPointerValid(aEnabled);
3440
3441 AutoCaller autoCaller(this);
3442 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3443
3444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3445
3446 *aEnabled = mHWData->mIOCacheEnabled;
3447
3448 return S_OK;
3449}
3450
3451STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3452{
3453 AutoCaller autoCaller(this);
3454 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3455
3456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3457
3458 HRESULT rc = checkStateDependency(MutableStateDep);
3459 if (FAILED(rc)) return rc;
3460
3461 setModified(IsModified_MachineData);
3462 mHWData.backup();
3463 mHWData->mIOCacheEnabled = aEnabled;
3464
3465 return S_OK;
3466}
3467
3468STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3469{
3470 CheckComArgOutPointerValid(aIOCacheSize);
3471
3472 AutoCaller autoCaller(this);
3473 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3474
3475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3476
3477 *aIOCacheSize = mHWData->mIOCacheSize;
3478
3479 return S_OK;
3480}
3481
3482STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3483{
3484 AutoCaller autoCaller(this);
3485 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3486
3487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3488
3489 HRESULT rc = checkStateDependency(MutableStateDep);
3490 if (FAILED(rc)) return rc;
3491
3492 setModified(IsModified_MachineData);
3493 mHWData.backup();
3494 mHWData->mIOCacheSize = aIOCacheSize;
3495
3496 return S_OK;
3497}
3498
3499
3500/**
3501 * @note Locks objects!
3502 */
3503STDMETHODIMP Machine::LockMachine(ISession *aSession,
3504 LockType_T lockType)
3505{
3506 CheckComArgNotNull(aSession);
3507
3508 AutoCaller autoCaller(this);
3509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3510
3511 /* check the session state */
3512 SessionState_T state;
3513 HRESULT rc = aSession->COMGETTER(State)(&state);
3514 if (FAILED(rc)) return rc;
3515
3516 if (state != SessionState_Unlocked)
3517 return setError(VBOX_E_INVALID_OBJECT_STATE,
3518 tr("The given session is busy"));
3519
3520 // get the client's IInternalSessionControl interface
3521 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3522 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3523 E_INVALIDARG);
3524
3525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3526
3527 if (!mData->mRegistered)
3528 return setError(E_UNEXPECTED,
3529 tr("The machine '%s' is not registered"),
3530 mUserData->s.strName.c_str());
3531
3532 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3533
3534 SessionState_T oldState = mData->mSession.mState;
3535 /* Hack: in case the session is closing and there is a progress object
3536 * which allows waiting for the session to be closed, take the opportunity
3537 * and do a limited wait (max. 1 second). This helps a lot when the system
3538 * is busy and thus session closing can take a little while. */
3539 if ( mData->mSession.mState == SessionState_Unlocking
3540 && mData->mSession.mProgress)
3541 {
3542 alock.release();
3543 mData->mSession.mProgress->WaitForCompletion(1000);
3544 alock.acquire();
3545 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3546 }
3547
3548 // try again now
3549 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3550 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3551 )
3552 {
3553 // OK, share the session... we are now dealing with three processes:
3554 // 1) VBoxSVC (where this code runs);
3555 // 2) process C: the caller's client process (who wants a shared session);
3556 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3557
3558 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3559 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3560 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3561 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3562 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3563
3564 /*
3565 * Release the lock before calling the client process. It's safe here
3566 * since the only thing to do after we get the lock again is to add
3567 * the remote control to the list (which doesn't directly influence
3568 * anything).
3569 */
3570 alock.release();
3571
3572 // get the console of the session holding the write lock (this is a remote call)
3573 ComPtr<IConsole> pConsoleW;
3574 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3575 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3576 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3577 if (FAILED(rc))
3578 // the failure may occur w/o any error info (from RPC), so provide one
3579 return setError(VBOX_E_VM_ERROR,
3580 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3581
3582 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3583
3584 // share the session machine and W's console with the caller's session
3585 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3586 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3587 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3588
3589 if (FAILED(rc))
3590 // the failure may occur w/o any error info (from RPC), so provide one
3591 return setError(VBOX_E_VM_ERROR,
3592 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3593 alock.acquire();
3594
3595 // need to revalidate the state after acquiring the lock again
3596 if (mData->mSession.mState != SessionState_Locked)
3597 {
3598 pSessionControl->Uninitialize();
3599 return setError(VBOX_E_INVALID_SESSION_STATE,
3600 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3601 mUserData->s.strName.c_str());
3602 }
3603
3604 // add the caller's session to the list
3605 mData->mSession.mRemoteControls.push_back(pSessionControl);
3606 }
3607 else if ( mData->mSession.mState == SessionState_Locked
3608 || mData->mSession.mState == SessionState_Unlocking
3609 )
3610 {
3611 // sharing not permitted, or machine still unlocking:
3612 return setError(VBOX_E_INVALID_OBJECT_STATE,
3613 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3614 mUserData->s.strName.c_str());
3615 }
3616 else
3617 {
3618 // machine is not locked: then write-lock the machine (create the session machine)
3619
3620 // must not be busy
3621 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3622
3623 // get the caller's session PID
3624 RTPROCESS pid = NIL_RTPROCESS;
3625 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3626 pSessionControl->GetPID((ULONG*)&pid);
3627 Assert(pid != NIL_RTPROCESS);
3628
3629 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3630
3631 if (fLaunchingVMProcess)
3632 {
3633 if (mData->mSession.mPID == NIL_RTPROCESS)
3634 {
3635 // two or more clients racing for a lock, the one which set the
3636 // session state to Spawning will win, the others will get an
3637 // error as we can't decide here if waiting a little would help
3638 // (only for shared locks this would avoid an error)
3639 return setError(VBOX_E_INVALID_OBJECT_STATE,
3640 tr("The machine '%s' already has a lock request pending"),
3641 mUserData->s.strName.c_str());
3642 }
3643
3644 // this machine is awaiting for a spawning session to be opened:
3645 // then the calling process must be the one that got started by
3646 // LaunchVMProcess()
3647
3648 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3649 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3650
3651 if (mData->mSession.mPID != pid)
3652 return setError(E_ACCESSDENIED,
3653 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3654 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3655 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3656 }
3657
3658 // create the mutable SessionMachine from the current machine
3659 ComObjPtr<SessionMachine> sessionMachine;
3660 sessionMachine.createObject();
3661 rc = sessionMachine->init(this);
3662 AssertComRC(rc);
3663
3664 /* NOTE: doing return from this function after this point but
3665 * before the end is forbidden since it may call SessionMachine::uninit()
3666 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3667 * lock while still holding the Machine lock in alock so that a deadlock
3668 * is possible due to the wrong lock order. */
3669
3670 if (SUCCEEDED(rc))
3671 {
3672 /*
3673 * Set the session state to Spawning to protect against subsequent
3674 * attempts to open a session and to unregister the machine after
3675 * we release the lock.
3676 */
3677 SessionState_T origState = mData->mSession.mState;
3678 mData->mSession.mState = SessionState_Spawning;
3679
3680#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3681 /* Get the client token ID to be passed to the client process */
3682 Utf8Str strTokenId;
3683 sessionMachine->getTokenId(strTokenId);
3684 Assert(!strTokenId.isEmpty());
3685#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3686 /* Get the client token to be passed to the client process */
3687 ComPtr<IToken> pToken(sessionMachine->getToken());
3688 /* The token is now "owned" by pToken, fix refcount */
3689 if (!pToken.isNull())
3690 pToken->Release();
3691#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3692
3693 /*
3694 * Release the lock before calling the client process -- it will call
3695 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3696 * because the state is Spawning, so that LaunchVMProcess() and
3697 * LockMachine() calls will fail. This method, called before we
3698 * acquire the lock again, will fail because of the wrong PID.
3699 *
3700 * Note that mData->mSession.mRemoteControls accessed outside
3701 * the lock may not be modified when state is Spawning, so it's safe.
3702 */
3703 alock.release();
3704
3705 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3706#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3707 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3708#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3709 rc = pSessionControl->AssignMachine(sessionMachine, lockType, pToken);
3710 /* Now the token is owned by the client process. */
3711 pToken.setNull();
3712#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3713 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3714
3715 /* The failure may occur w/o any error info (from RPC), so provide one */
3716 if (FAILED(rc))
3717 setError(VBOX_E_VM_ERROR,
3718 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3719
3720 if ( SUCCEEDED(rc)
3721 && fLaunchingVMProcess
3722 )
3723 {
3724 /* complete the remote session initialization */
3725
3726 /* get the console from the direct session */
3727 ComPtr<IConsole> console;
3728 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3729 ComAssertComRC(rc);
3730
3731 if (SUCCEEDED(rc) && !console)
3732 {
3733 ComAssert(!!console);
3734 rc = E_FAIL;
3735 }
3736
3737 /* assign machine & console to the remote session */
3738 if (SUCCEEDED(rc))
3739 {
3740 /*
3741 * after LaunchVMProcess(), the first and the only
3742 * entry in remoteControls is that remote session
3743 */
3744 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3745 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3746 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3747
3748 /* The failure may occur w/o any error info (from RPC), so provide one */
3749 if (FAILED(rc))
3750 setError(VBOX_E_VM_ERROR,
3751 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3752 }
3753
3754 if (FAILED(rc))
3755 pSessionControl->Uninitialize();
3756 }
3757
3758 /* acquire the lock again */
3759 alock.acquire();
3760
3761 /* Restore the session state */
3762 mData->mSession.mState = origState;
3763 }
3764
3765 // finalize spawning anyway (this is why we don't return on errors above)
3766 if (fLaunchingVMProcess)
3767 {
3768 /* Note that the progress object is finalized later */
3769 /** @todo Consider checking mData->mSession.mProgress for cancellation
3770 * around here. */
3771
3772 /* We don't reset mSession.mPID here because it is necessary for
3773 * SessionMachine::uninit() to reap the child process later. */
3774
3775 if (FAILED(rc))
3776 {
3777 /* Close the remote session, remove the remote control from the list
3778 * and reset session state to Closed (@note keep the code in sync
3779 * with the relevant part in checkForSpawnFailure()). */
3780
3781 Assert(mData->mSession.mRemoteControls.size() == 1);
3782 if (mData->mSession.mRemoteControls.size() == 1)
3783 {
3784 ErrorInfoKeeper eik;
3785 mData->mSession.mRemoteControls.front()->Uninitialize();
3786 }
3787
3788 mData->mSession.mRemoteControls.clear();
3789 mData->mSession.mState = SessionState_Unlocked;
3790 }
3791 }
3792 else
3793 {
3794 /* memorize PID of the directly opened session */
3795 if (SUCCEEDED(rc))
3796 mData->mSession.mPID = pid;
3797 }
3798
3799 if (SUCCEEDED(rc))
3800 {
3801 /* memorize the direct session control and cache IUnknown for it */
3802 mData->mSession.mDirectControl = pSessionControl;
3803 mData->mSession.mState = SessionState_Locked;
3804 /* associate the SessionMachine with this Machine */
3805 mData->mSession.mMachine = sessionMachine;
3806
3807 /* request an IUnknown pointer early from the remote party for later
3808 * identity checks (it will be internally cached within mDirectControl
3809 * at least on XPCOM) */
3810 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3811 NOREF(unk);
3812 }
3813
3814 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3815 * would break the lock order */
3816 alock.release();
3817
3818 /* uninitialize the created session machine on failure */
3819 if (FAILED(rc))
3820 sessionMachine->uninit();
3821
3822 }
3823
3824 if (SUCCEEDED(rc))
3825 {
3826 /*
3827 * tell the client watcher thread to update the set of
3828 * machines that have open sessions
3829 */
3830 mParent->updateClientWatcher();
3831
3832 if (oldState != SessionState_Locked)
3833 /* fire an event */
3834 mParent->onSessionStateChange(getId(), SessionState_Locked);
3835 }
3836
3837 return rc;
3838}
3839
3840/**
3841 * @note Locks objects!
3842 */
3843STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3844 IN_BSTR aFrontend,
3845 IN_BSTR aEnvironment,
3846 IProgress **aProgress)
3847{
3848 CheckComArgStr(aFrontend);
3849 Utf8Str strFrontend(aFrontend);
3850 Utf8Str strEnvironment(aEnvironment);
3851 /* "emergencystop" doesn't need the session, so skip the checks/interface
3852 * retrieval. This code doesn't quite fit in here, but introducing a
3853 * special API method would be even more effort, and would require explicit
3854 * support by every API client. It's better to hide the feature a bit. */
3855 if (strFrontend != "emergencystop")
3856 CheckComArgNotNull(aSession);
3857 CheckComArgOutPointerValid(aProgress);
3858
3859 AutoCaller autoCaller(this);
3860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3861
3862 HRESULT rc = S_OK;
3863 if (strFrontend.isEmpty())
3864 {
3865 Bstr bstrFrontend;
3866 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3867 if (FAILED(rc))
3868 return rc;
3869 strFrontend = bstrFrontend;
3870 if (strFrontend.isEmpty())
3871 {
3872 ComPtr<ISystemProperties> systemProperties;
3873 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3874 if (FAILED(rc))
3875 return rc;
3876 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3877 if (FAILED(rc))
3878 return rc;
3879 strFrontend = bstrFrontend;
3880 }
3881 /* paranoia - emergencystop is not a valid default */
3882 if (strFrontend == "emergencystop")
3883 strFrontend = Utf8Str::Empty;
3884 }
3885 /* default frontend: Qt GUI */
3886 if (strFrontend.isEmpty())
3887 strFrontend = "GUI/Qt";
3888
3889 if (strFrontend != "emergencystop")
3890 {
3891 /* check the session state */
3892 SessionState_T state;
3893 rc = aSession->COMGETTER(State)(&state);
3894 if (FAILED(rc))
3895 return rc;
3896
3897 if (state != SessionState_Unlocked)
3898 return setError(VBOX_E_INVALID_OBJECT_STATE,
3899 tr("The given session is busy"));
3900
3901 /* get the IInternalSessionControl interface */
3902 ComPtr<IInternalSessionControl> control(aSession);
3903 ComAssertMsgRet(!control.isNull(),
3904 ("No IInternalSessionControl interface"),
3905 E_INVALIDARG);
3906
3907 /* get the teleporter enable state for the progress object init. */
3908 BOOL fTeleporterEnabled;
3909 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3910 if (FAILED(rc))
3911 return rc;
3912
3913 /* create a progress object */
3914 ComObjPtr<ProgressProxy> progress;
3915 progress.createObject();
3916 rc = progress->init(mParent,
3917 static_cast<IMachine*>(this),
3918 Bstr(tr("Starting VM")).raw(),
3919 TRUE /* aCancelable */,
3920 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3921 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3922 2 /* uFirstOperationWeight */,
3923 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3924
3925 if (SUCCEEDED(rc))
3926 {
3927 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3928 if (SUCCEEDED(rc))
3929 {
3930 progress.queryInterfaceTo(aProgress);
3931
3932 /* signal the client watcher thread */
3933 mParent->updateClientWatcher();
3934
3935 /* fire an event */
3936 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3937 }
3938 }
3939 }
3940 else
3941 {
3942 /* no progress object - either instant success or failure */
3943 *aProgress = NULL;
3944
3945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3946
3947 if (mData->mSession.mState != SessionState_Locked)
3948 return setError(VBOX_E_INVALID_OBJECT_STATE,
3949 tr("The machine '%s' is not locked by a session"),
3950 mUserData->s.strName.c_str());
3951
3952 /* must have a VM process associated - do not kill normal API clients
3953 * with an open session */
3954 if (!Global::IsOnline(mData->mMachineState))
3955 return setError(VBOX_E_INVALID_OBJECT_STATE,
3956 tr("The machine '%s' does not have a VM process"),
3957 mUserData->s.strName.c_str());
3958
3959 /* forcibly terminate the VM process */
3960 if (mData->mSession.mPID != NIL_RTPROCESS)
3961 RTProcTerminate(mData->mSession.mPID);
3962
3963 /* signal the client watcher thread, as most likely the client has
3964 * been terminated */
3965 mParent->updateClientWatcher();
3966 }
3967
3968 return rc;
3969}
3970
3971STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3972{
3973 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3974 return setError(E_INVALIDARG,
3975 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3976 aPosition, SchemaDefs::MaxBootPosition);
3977
3978 if (aDevice == DeviceType_USB)
3979 return setError(E_NOTIMPL,
3980 tr("Booting from USB device is currently not supported"));
3981
3982 AutoCaller autoCaller(this);
3983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3984
3985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3986
3987 HRESULT rc = checkStateDependency(MutableStateDep);
3988 if (FAILED(rc)) return rc;
3989
3990 setModified(IsModified_MachineData);
3991 mHWData.backup();
3992 mHWData->mBootOrder[aPosition - 1] = aDevice;
3993
3994 return S_OK;
3995}
3996
3997STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3998{
3999 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
4000 return setError(E_INVALIDARG,
4001 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
4002 aPosition, SchemaDefs::MaxBootPosition);
4003
4004 AutoCaller autoCaller(this);
4005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4006
4007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4008
4009 *aDevice = mHWData->mBootOrder[aPosition - 1];
4010
4011 return S_OK;
4012}
4013
4014STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4015 LONG aControllerPort,
4016 LONG aDevice,
4017 DeviceType_T aType,
4018 IMedium *aMedium)
4019{
4020 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4021 aControllerName, aControllerPort, aDevice, aType, aMedium));
4022
4023 CheckComArgStrNotEmptyOrNull(aControllerName);
4024
4025 AutoCaller autoCaller(this);
4026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4027
4028 // request the host lock first, since might be calling Host methods for getting host drives;
4029 // next, protect the media tree all the while we're in here, as well as our member variables
4030 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4031 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4032
4033 HRESULT rc = checkStateDependency(MutableStateDep);
4034 if (FAILED(rc)) return rc;
4035
4036 /// @todo NEWMEDIA implicit machine registration
4037 if (!mData->mRegistered)
4038 return setError(VBOX_E_INVALID_OBJECT_STATE,
4039 tr("Cannot attach storage devices to an unregistered machine"));
4040
4041 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4042
4043 /* Check for an existing controller. */
4044 ComObjPtr<StorageController> ctl;
4045 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4046 if (FAILED(rc)) return rc;
4047
4048 StorageControllerType_T ctrlType;
4049 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4050 if (FAILED(rc))
4051 return setError(E_FAIL,
4052 tr("Could not get type of controller '%ls'"),
4053 aControllerName);
4054
4055 bool fSilent = false;
4056 Utf8Str strReconfig;
4057
4058 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4059 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4060 if ( mData->mMachineState == MachineState_Paused
4061 && strReconfig == "1")
4062 fSilent = true;
4063
4064 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4065 bool fHotplug = false;
4066 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4067 fHotplug = true;
4068
4069 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4070 return setError(VBOX_E_INVALID_VM_STATE,
4071 tr("Controller '%ls' does not support hotplugging"),
4072 aControllerName);
4073
4074 // check that the port and device are not out of range
4075 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
4076 if (FAILED(rc)) return rc;
4077
4078 /* check if the device slot is already busy */
4079 MediumAttachment *pAttachTemp;
4080 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4081 aControllerName,
4082 aControllerPort,
4083 aDevice)))
4084 {
4085 Medium *pMedium = pAttachTemp->i_getMedium();
4086 if (pMedium)
4087 {
4088 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4089 return setError(VBOX_E_OBJECT_IN_USE,
4090 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4091 pMedium->i_getLocationFull().c_str(),
4092 aControllerPort,
4093 aDevice,
4094 aControllerName);
4095 }
4096 else
4097 return setError(VBOX_E_OBJECT_IN_USE,
4098 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4099 aControllerPort, aDevice, aControllerName);
4100 }
4101
4102 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4103 if (aMedium && medium.isNull())
4104 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4105
4106 AutoCaller mediumCaller(medium);
4107 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4108
4109 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4110
4111 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4112 && !medium.isNull()
4113 )
4114 return setError(VBOX_E_OBJECT_IN_USE,
4115 tr("Medium '%s' is already attached to this virtual machine"),
4116 medium->i_getLocationFull().c_str());
4117
4118 if (!medium.isNull())
4119 {
4120 MediumType_T mtype = medium->i_getType();
4121 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4122 // For DVDs it's not written to the config file, so needs no global config
4123 // version bump. For floppies it's a new attribute "type", which is ignored
4124 // by older VirtualBox version, so needs no global config version bump either.
4125 // For hard disks this type is not accepted.
4126 if (mtype == MediumType_MultiAttach)
4127 {
4128 // This type is new with VirtualBox 4.0 and therefore requires settings
4129 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4130 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4131 // two reasons: The medium type is a property of the media registry tree, which
4132 // can reside in the global config file (for pre-4.0 media); we would therefore
4133 // possibly need to bump the global config version. We don't want to do that though
4134 // because that might make downgrading to pre-4.0 impossible.
4135 // As a result, we can only use these two new types if the medium is NOT in the
4136 // global registry:
4137 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4138 if ( medium->i_isInRegistry(uuidGlobalRegistry)
4139 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4140 )
4141 return setError(VBOX_E_INVALID_OBJECT_STATE,
4142 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4143 "to machines that were created with VirtualBox 4.0 or later"),
4144 medium->i_getLocationFull().c_str());
4145 }
4146 }
4147
4148 bool fIndirect = false;
4149 if (!medium.isNull())
4150 fIndirect = medium->i_isReadOnly();
4151 bool associate = true;
4152
4153 do
4154 {
4155 if ( aType == DeviceType_HardDisk
4156 && mMediaData.isBackedUp())
4157 {
4158 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4159
4160 /* check if the medium was attached to the VM before we started
4161 * changing attachments in which case the attachment just needs to
4162 * be restored */
4163 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4164 {
4165 AssertReturn(!fIndirect, E_FAIL);
4166
4167 /* see if it's the same bus/channel/device */
4168 if (pAttachTemp->i_matches(aControllerName, aControllerPort, aDevice))
4169 {
4170 /* the simplest case: restore the whole attachment
4171 * and return, nothing else to do */
4172 mMediaData->mAttachments.push_back(pAttachTemp);
4173
4174 /* Reattach the medium to the VM. */
4175 if (fHotplug || fSilent)
4176 {
4177 mediumLock.release();
4178 treeLock.release();
4179 alock.release();
4180
4181 MediumLockList *pMediumLockList(new MediumLockList());
4182
4183 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4184 true /* fMediumLockWrite */,
4185 NULL,
4186 *pMediumLockList);
4187 alock.acquire();
4188 if (FAILED(rc))
4189 delete pMediumLockList;
4190 else
4191 {
4192 mData->mSession.mLockedMedia.Unlock();
4193 alock.release();
4194 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4195 mData->mSession.mLockedMedia.Lock();
4196 alock.acquire();
4197 }
4198 alock.release();
4199
4200 if (SUCCEEDED(rc))
4201 {
4202 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4203 /* Remove lock list in case of error. */
4204 if (FAILED(rc))
4205 {
4206 mData->mSession.mLockedMedia.Unlock();
4207 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4208 mData->mSession.mLockedMedia.Lock();
4209 }
4210 }
4211 }
4212
4213 return S_OK;
4214 }
4215
4216 /* bus/channel/device differ; we need a new attachment object,
4217 * but don't try to associate it again */
4218 associate = false;
4219 break;
4220 }
4221 }
4222
4223 /* go further only if the attachment is to be indirect */
4224 if (!fIndirect)
4225 break;
4226
4227 /* perform the so called smart attachment logic for indirect
4228 * attachments. Note that smart attachment is only applicable to base
4229 * hard disks. */
4230
4231 if (medium->i_getParent().isNull())
4232 {
4233 /* first, investigate the backup copy of the current hard disk
4234 * attachments to make it possible to re-attach existing diffs to
4235 * another device slot w/o losing their contents */
4236 if (mMediaData.isBackedUp())
4237 {
4238 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4239
4240 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4241 uint32_t foundLevel = 0;
4242
4243 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4244 it != oldAtts.end();
4245 ++it)
4246 {
4247 uint32_t level = 0;
4248 MediumAttachment *pAttach = *it;
4249 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4250 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4251 if (pMedium.isNull())
4252 continue;
4253
4254 if (pMedium->i_getBase(&level) == medium)
4255 {
4256 /* skip the hard disk if its currently attached (we
4257 * cannot attach the same hard disk twice) */
4258 if (findAttachment(mMediaData->mAttachments,
4259 pMedium))
4260 continue;
4261
4262 /* matched device, channel and bus (i.e. attached to the
4263 * same place) will win and immediately stop the search;
4264 * otherwise the attachment that has the youngest
4265 * descendant of medium will be used
4266 */
4267 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
4268 {
4269 /* the simplest case: restore the whole attachment
4270 * and return, nothing else to do */
4271 mMediaData->mAttachments.push_back(*it);
4272
4273 /* Reattach the medium to the VM. */
4274 if (fHotplug || fSilent)
4275 {
4276 mediumLock.release();
4277 treeLock.release();
4278 alock.release();
4279
4280 MediumLockList *pMediumLockList(new MediumLockList());
4281
4282 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4283 true /* fMediumLockWrite */,
4284 NULL,
4285 *pMediumLockList);
4286 alock.acquire();
4287 if (FAILED(rc))
4288 delete pMediumLockList;
4289 else
4290 {
4291 mData->mSession.mLockedMedia.Unlock();
4292 alock.release();
4293 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4294 mData->mSession.mLockedMedia.Lock();
4295 alock.acquire();
4296 }
4297 alock.release();
4298
4299 if (SUCCEEDED(rc))
4300 {
4301 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4302 /* Remove lock list in case of error. */
4303 if (FAILED(rc))
4304 {
4305 mData->mSession.mLockedMedia.Unlock();
4306 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4307 mData->mSession.mLockedMedia.Lock();
4308 }
4309 }
4310 }
4311
4312 return S_OK;
4313 }
4314 else if ( foundIt == oldAtts.end()
4315 || level > foundLevel /* prefer younger */
4316 )
4317 {
4318 foundIt = it;
4319 foundLevel = level;
4320 }
4321 }
4322 }
4323
4324 if (foundIt != oldAtts.end())
4325 {
4326 /* use the previously attached hard disk */
4327 medium = (*foundIt)->i_getMedium();
4328 mediumCaller.attach(medium);
4329 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4330 mediumLock.attach(medium);
4331 /* not implicit, doesn't require association with this VM */
4332 fIndirect = false;
4333 associate = false;
4334 /* go right to the MediumAttachment creation */
4335 break;
4336 }
4337 }
4338
4339 /* must give up the medium lock and medium tree lock as below we
4340 * go over snapshots, which needs a lock with higher lock order. */
4341 mediumLock.release();
4342 treeLock.release();
4343
4344 /* then, search through snapshots for the best diff in the given
4345 * hard disk's chain to base the new diff on */
4346
4347 ComObjPtr<Medium> base;
4348 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4349 while (snap)
4350 {
4351 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4352
4353 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4354
4355 MediumAttachment *pAttachFound = NULL;
4356 uint32_t foundLevel = 0;
4357
4358 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4359 it != snapAtts.end();
4360 ++it)
4361 {
4362 MediumAttachment *pAttach = *it;
4363 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4364 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4365 if (pMedium.isNull())
4366 continue;
4367
4368 uint32_t level = 0;
4369 if (pMedium->i_getBase(&level) == medium)
4370 {
4371 /* matched device, channel and bus (i.e. attached to the
4372 * same place) will win and immediately stop the search;
4373 * otherwise the attachment that has the youngest
4374 * descendant of medium will be used
4375 */
4376 if ( pAttach->i_getDevice() == aDevice
4377 && pAttach->i_getPort() == aControllerPort
4378 && pAttach->i_getControllerName() == aControllerName
4379 )
4380 {
4381 pAttachFound = pAttach;
4382 break;
4383 }
4384 else if ( !pAttachFound
4385 || level > foundLevel /* prefer younger */
4386 )
4387 {
4388 pAttachFound = pAttach;
4389 foundLevel = level;
4390 }
4391 }
4392 }
4393
4394 if (pAttachFound)
4395 {
4396 base = pAttachFound->i_getMedium();
4397 break;
4398 }
4399
4400 snap = snap->i_getParent();
4401 }
4402
4403 /* re-lock medium tree and the medium, as we need it below */
4404 treeLock.acquire();
4405 mediumLock.acquire();
4406
4407 /* found a suitable diff, use it as a base */
4408 if (!base.isNull())
4409 {
4410 medium = base;
4411 mediumCaller.attach(medium);
4412 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4413 mediumLock.attach(medium);
4414 }
4415 }
4416
4417 Utf8Str strFullSnapshotFolder;
4418 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4419
4420 ComObjPtr<Medium> diff;
4421 diff.createObject();
4422 // store this diff in the same registry as the parent
4423 Guid uuidRegistryParent;
4424 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4425 {
4426 // parent image has no registry: this can happen if we're attaching a new immutable
4427 // image that has not yet been attached (medium then points to the base and we're
4428 // creating the diff image for the immutable, and the parent is not yet registered);
4429 // put the parent in the machine registry then
4430 mediumLock.release();
4431 treeLock.release();
4432 alock.release();
4433 addMediumToRegistry(medium);
4434 alock.acquire();
4435 treeLock.acquire();
4436 mediumLock.acquire();
4437 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4438 }
4439 rc = diff->init(mParent,
4440 medium->i_getPreferredDiffFormat(),
4441 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4442 uuidRegistryParent);
4443 if (FAILED(rc)) return rc;
4444
4445 /* Apply the normal locking logic to the entire chain. */
4446 MediumLockList *pMediumLockList(new MediumLockList());
4447 mediumLock.release();
4448 treeLock.release();
4449 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4450 true /* fMediumLockWrite */,
4451 medium,
4452 *pMediumLockList);
4453 treeLock.acquire();
4454 mediumLock.acquire();
4455 if (SUCCEEDED(rc))
4456 {
4457 mediumLock.release();
4458 treeLock.release();
4459 rc = pMediumLockList->Lock();
4460 treeLock.acquire();
4461 mediumLock.acquire();
4462 if (FAILED(rc))
4463 setError(rc,
4464 tr("Could not lock medium when creating diff '%s'"),
4465 diff->i_getLocationFull().c_str());
4466 else
4467 {
4468 /* will release the lock before the potentially lengthy
4469 * operation, so protect with the special state */
4470 MachineState_T oldState = mData->mMachineState;
4471 setMachineState(MachineState_SettingUp);
4472
4473 mediumLock.release();
4474 treeLock.release();
4475 alock.release();
4476
4477 rc = medium->i_createDiffStorage(diff,
4478 MediumVariant_Standard,
4479 pMediumLockList,
4480 NULL /* aProgress */,
4481 true /* aWait */);
4482
4483 alock.acquire();
4484 treeLock.acquire();
4485 mediumLock.acquire();
4486
4487 setMachineState(oldState);
4488 }
4489 }
4490
4491 /* Unlock the media and free the associated memory. */
4492 delete pMediumLockList;
4493
4494 if (FAILED(rc)) return rc;
4495
4496 /* use the created diff for the actual attachment */
4497 medium = diff;
4498 mediumCaller.attach(medium);
4499 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4500 mediumLock.attach(medium);
4501 }
4502 while (0);
4503
4504 ComObjPtr<MediumAttachment> attachment;
4505 attachment.createObject();
4506 rc = attachment->init(this,
4507 medium,
4508 aControllerName,
4509 aControllerPort,
4510 aDevice,
4511 aType,
4512 fIndirect,
4513 false /* fPassthrough */,
4514 false /* fTempEject */,
4515 false /* fNonRotational */,
4516 false /* fDiscard */,
4517 fHotplug /* fHotPluggable */,
4518 Utf8Str::Empty);
4519 if (FAILED(rc)) return rc;
4520
4521 if (associate && !medium.isNull())
4522 {
4523 // as the last step, associate the medium to the VM
4524 rc = medium->i_addBackReference(mData->mUuid);
4525 // here we can fail because of Deleting, or being in process of creating a Diff
4526 if (FAILED(rc)) return rc;
4527
4528 mediumLock.release();
4529 treeLock.release();
4530 alock.release();
4531 addMediumToRegistry(medium);
4532 alock.acquire();
4533 treeLock.acquire();
4534 mediumLock.acquire();
4535 }
4536
4537 /* success: finally remember the attachment */
4538 setModified(IsModified_Storage);
4539 mMediaData.backup();
4540 mMediaData->mAttachments.push_back(attachment);
4541
4542 mediumLock.release();
4543 treeLock.release();
4544 alock.release();
4545
4546 if (fHotplug || fSilent)
4547 {
4548 if (!medium.isNull())
4549 {
4550 MediumLockList *pMediumLockList(new MediumLockList());
4551
4552 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4553 true /* fMediumLockWrite */,
4554 NULL,
4555 *pMediumLockList);
4556 alock.acquire();
4557 if (FAILED(rc))
4558 delete pMediumLockList;
4559 else
4560 {
4561 mData->mSession.mLockedMedia.Unlock();
4562 alock.release();
4563 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4564 mData->mSession.mLockedMedia.Lock();
4565 alock.acquire();
4566 }
4567 alock.release();
4568 }
4569
4570 if (SUCCEEDED(rc))
4571 {
4572 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4573 /* Remove lock list in case of error. */
4574 if (FAILED(rc))
4575 {
4576 mData->mSession.mLockedMedia.Unlock();
4577 mData->mSession.mLockedMedia.Remove(attachment);
4578 mData->mSession.mLockedMedia.Lock();
4579 }
4580 }
4581 }
4582
4583 mParent->saveModifiedRegistries();
4584
4585 return rc;
4586}
4587
4588STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4589 LONG aDevice)
4590{
4591 CheckComArgStrNotEmptyOrNull(aControllerName);
4592
4593 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4594 aControllerName, aControllerPort, aDevice));
4595
4596 AutoCaller autoCaller(this);
4597 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4598
4599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4600
4601 HRESULT rc = checkStateDependency(MutableStateDep);
4602 if (FAILED(rc)) return rc;
4603
4604 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4605
4606 /* Check for an existing controller. */
4607 ComObjPtr<StorageController> ctl;
4608 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4609 if (FAILED(rc)) return rc;
4610
4611 StorageControllerType_T ctrlType;
4612 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4613 if (FAILED(rc))
4614 return setError(E_FAIL,
4615 tr("Could not get type of controller '%ls'"),
4616 aControllerName);
4617
4618 bool fSilent = false;
4619 Utf8Str strReconfig;
4620
4621 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4622 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4623 if ( mData->mMachineState == MachineState_Paused
4624 && strReconfig == "1")
4625 fSilent = true;
4626
4627 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4628 bool fHotplug = false;
4629 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4630 fHotplug = true;
4631
4632 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4633 return setError(VBOX_E_INVALID_VM_STATE,
4634 tr("Controller '%ls' does not support hotplugging"),
4635 aControllerName);
4636
4637 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4638 aControllerName,
4639 aControllerPort,
4640 aDevice);
4641 if (!pAttach)
4642 return setError(VBOX_E_OBJECT_NOT_FOUND,
4643 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4644 aDevice, aControllerPort, aControllerName);
4645
4646 if (fHotplug && !pAttach->i_getHotPluggable())
4647 return setError(VBOX_E_NOT_SUPPORTED,
4648 tr("The device slot %d on port %d of controller '%ls' does not support hotplugging"),
4649 aDevice, aControllerPort, aControllerName);
4650
4651 /*
4652 * The VM has to detach the device before we delete any implicit diffs.
4653 * If this fails we can roll back without loosing data.
4654 */
4655 if (fHotplug || fSilent)
4656 {
4657 alock.release();
4658 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4659 alock.acquire();
4660 }
4661 if (FAILED(rc)) return rc;
4662
4663 /* If we are here everything went well and we can delete the implicit now. */
4664 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4665
4666 alock.release();
4667
4668 mParent->saveModifiedRegistries();
4669
4670 return rc;
4671}
4672
4673STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4674 LONG aDevice, BOOL aPassthrough)
4675{
4676 CheckComArgStrNotEmptyOrNull(aControllerName);
4677
4678 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4679 aControllerName, aControllerPort, aDevice, aPassthrough));
4680
4681 AutoCaller autoCaller(this);
4682 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4683
4684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4685
4686 HRESULT rc = checkStateDependency(MutableStateDep);
4687 if (FAILED(rc)) return rc;
4688
4689 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4690
4691 if (Global::IsOnlineOrTransient(mData->mMachineState))
4692 return setError(VBOX_E_INVALID_VM_STATE,
4693 tr("Invalid machine state: %s"),
4694 Global::stringifyMachineState(mData->mMachineState));
4695
4696 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4697 aControllerName,
4698 aControllerPort,
4699 aDevice);
4700 if (!pAttach)
4701 return setError(VBOX_E_OBJECT_NOT_FOUND,
4702 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4703 aDevice, aControllerPort, aControllerName);
4704
4705
4706 setModified(IsModified_Storage);
4707 mMediaData.backup();
4708
4709 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4710
4711 if (pAttach->i_getType() != DeviceType_DVD)
4712 return setError(E_INVALIDARG,
4713 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4714 aDevice, aControllerPort, aControllerName);
4715 pAttach->i_updatePassthrough(!!aPassthrough);
4716
4717 return S_OK;
4718}
4719
4720STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4721 LONG aDevice, BOOL aTemporaryEject)
4722{
4723 CheckComArgStrNotEmptyOrNull(aControllerName);
4724
4725 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4726 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4727
4728 AutoCaller autoCaller(this);
4729 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4730
4731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4732
4733 HRESULT rc = checkStateDependency(MutableStateDep);
4734 if (FAILED(rc)) return rc;
4735
4736 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4737 aControllerName,
4738 aControllerPort,
4739 aDevice);
4740 if (!pAttach)
4741 return setError(VBOX_E_OBJECT_NOT_FOUND,
4742 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4743 aDevice, aControllerPort, aControllerName);
4744
4745
4746 setModified(IsModified_Storage);
4747 mMediaData.backup();
4748
4749 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4750
4751 if (pAttach->i_getType() != DeviceType_DVD)
4752 return setError(E_INVALIDARG,
4753 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4754 aDevice, aControllerPort, aControllerName);
4755 pAttach->i_updateTempEject(!!aTemporaryEject);
4756
4757 return S_OK;
4758}
4759
4760STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4761 LONG aDevice, BOOL aNonRotational)
4762{
4763 CheckComArgStrNotEmptyOrNull(aControllerName);
4764
4765 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4766 aControllerName, aControllerPort, aDevice, aNonRotational));
4767
4768 AutoCaller autoCaller(this);
4769 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4770
4771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4772
4773 HRESULT rc = checkStateDependency(MutableStateDep);
4774 if (FAILED(rc)) return rc;
4775
4776 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4777
4778 if (Global::IsOnlineOrTransient(mData->mMachineState))
4779 return setError(VBOX_E_INVALID_VM_STATE,
4780 tr("Invalid machine state: %s"),
4781 Global::stringifyMachineState(mData->mMachineState));
4782
4783 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4784 aControllerName,
4785 aControllerPort,
4786 aDevice);
4787 if (!pAttach)
4788 return setError(VBOX_E_OBJECT_NOT_FOUND,
4789 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4790 aDevice, aControllerPort, aControllerName);
4791
4792
4793 setModified(IsModified_Storage);
4794 mMediaData.backup();
4795
4796 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4797
4798 if (pAttach->i_getType() != DeviceType_HardDisk)
4799 return setError(E_INVALIDARG,
4800 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"),
4801 aDevice, aControllerPort, aControllerName);
4802 pAttach->i_updateNonRotational(!!aNonRotational);
4803
4804 return S_OK;
4805}
4806
4807STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4808 LONG aDevice, BOOL aDiscard)
4809{
4810 CheckComArgStrNotEmptyOrNull(aControllerName);
4811
4812 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4813 aControllerName, aControllerPort, aDevice, aDiscard));
4814
4815 AutoCaller autoCaller(this);
4816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4817
4818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4819
4820 HRESULT rc = checkStateDependency(MutableStateDep);
4821 if (FAILED(rc)) return rc;
4822
4823 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4824
4825 if (Global::IsOnlineOrTransient(mData->mMachineState))
4826 return setError(VBOX_E_INVALID_VM_STATE,
4827 tr("Invalid machine state: %s"),
4828 Global::stringifyMachineState(mData->mMachineState));
4829
4830 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4831 aControllerName,
4832 aControllerPort,
4833 aDevice);
4834 if (!pAttach)
4835 return setError(VBOX_E_OBJECT_NOT_FOUND,
4836 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4837 aDevice, aControllerPort, aControllerName);
4838
4839
4840 setModified(IsModified_Storage);
4841 mMediaData.backup();
4842
4843 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4844
4845 if (pAttach->i_getType() != DeviceType_HardDisk)
4846 return setError(E_INVALIDARG,
4847 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"),
4848 aDevice, aControllerPort, aControllerName);
4849 pAttach->i_updateDiscard(!!aDiscard);
4850
4851 return S_OK;
4852}
4853
4854STDMETHODIMP Machine::SetHotPluggableForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4855 LONG aDevice, BOOL aHotPluggable)
4856{
4857 CheckComArgStrNotEmptyOrNull(aControllerName);
4858
4859 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4860 aControllerName, aControllerPort, aDevice, aHotPluggable));
4861
4862 AutoCaller autoCaller(this);
4863 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4864
4865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4866
4867 HRESULT rc = checkStateDependency(MutableStateDep);
4868 if (FAILED(rc)) return rc;
4869
4870 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4871
4872 if (Global::IsOnlineOrTransient(mData->mMachineState))
4873 return setError(VBOX_E_INVALID_VM_STATE,
4874 tr("Invalid machine state: %s"),
4875 Global::stringifyMachineState(mData->mMachineState));
4876
4877 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4878 aControllerName,
4879 aControllerPort,
4880 aDevice);
4881 if (!pAttach)
4882 return setError(VBOX_E_OBJECT_NOT_FOUND,
4883 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4884 aDevice, aControllerPort, aControllerName);
4885
4886 /* Check for an existing controller. */
4887 ComObjPtr<StorageController> ctl;
4888 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4889 if (FAILED(rc)) return rc;
4890
4891 StorageControllerType_T ctrlType;
4892 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4893 if (FAILED(rc))
4894 return setError(E_FAIL,
4895 tr("Could not get type of controller '%ls'"),
4896 aControllerName);
4897
4898 if (!isControllerHotplugCapable(ctrlType))
4899 return setError(VBOX_E_NOT_SUPPORTED,
4900 tr("Controller '%ls' does not support changing the hot-pluggable device flag"),
4901 aControllerName);
4902
4903 setModified(IsModified_Storage);
4904 mMediaData.backup();
4905
4906 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4907
4908 if (pAttach->i_getType() == DeviceType_Floppy)
4909 return setError(E_INVALIDARG,
4910 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"),
4911 aDevice, aControllerPort, aControllerName);
4912 pAttach->i_updateHotPluggable(!!aHotPluggable);
4913
4914 return S_OK;
4915}
4916
4917STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4918 LONG aDevice)
4919{
4920 int rc = S_OK;
4921 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4922 aControllerName, aControllerPort, aDevice));
4923
4924 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4925
4926 return rc;
4927}
4928
4929STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4930 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4931{
4932 CheckComArgStrNotEmptyOrNull(aControllerName);
4933
4934 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4935 aControllerName, aControllerPort, aDevice));
4936
4937 AutoCaller autoCaller(this);
4938 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4939
4940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4941
4942 HRESULT rc = checkStateDependency(MutableStateDep);
4943 if (FAILED(rc)) return rc;
4944
4945 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4946
4947 if (Global::IsOnlineOrTransient(mData->mMachineState))
4948 return setError(VBOX_E_INVALID_VM_STATE,
4949 tr("Invalid machine state: %s"),
4950 Global::stringifyMachineState(mData->mMachineState));
4951
4952 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4953 aControllerName,
4954 aControllerPort,
4955 aDevice);
4956 if (!pAttach)
4957 return setError(VBOX_E_OBJECT_NOT_FOUND,
4958 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4959 aDevice, aControllerPort, aControllerName);
4960
4961
4962 setModified(IsModified_Storage);
4963 mMediaData.backup();
4964
4965 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4966 if (aBandwidthGroup && group.isNull())
4967 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4968
4969 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4970
4971 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4972 if (strBandwidthGroupOld.isNotEmpty())
4973 {
4974 /* Get the bandwidth group object and release it - this must not fail. */
4975 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4976 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4977 Assert(SUCCEEDED(rc));
4978
4979 pBandwidthGroupOld->i_release();
4980 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4981 }
4982
4983 if (!group.isNull())
4984 {
4985 group->i_reference();
4986 pAttach->i_updateBandwidthGroup(group->i_getName());
4987 }
4988
4989 return S_OK;
4990}
4991
4992STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4993 LONG aControllerPort,
4994 LONG aDevice,
4995 DeviceType_T aType)
4996{
4997 HRESULT rc = S_OK;
4998
4999 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
5000 aControllerName, aControllerPort, aDevice, aType));
5001
5002 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
5003
5004 return rc;
5005}
5006
5007
5008
5009STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
5010 LONG aControllerPort,
5011 LONG aDevice,
5012 BOOL aForce)
5013{
5014 int rc = S_OK;
5015 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
5016 aControllerName, aControllerPort, aForce));
5017
5018 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
5019
5020 return rc;
5021}
5022
5023STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
5024 LONG aControllerPort,
5025 LONG aDevice,
5026 IMedium *aMedium,
5027 BOOL aForce)
5028{
5029 int rc = S_OK;
5030 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
5031 aControllerName, aControllerPort, aDevice, aForce));
5032
5033 CheckComArgStrNotEmptyOrNull(aControllerName);
5034
5035 AutoCaller autoCaller(this);
5036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5037
5038 // request the host lock first, since might be calling Host methods for getting host drives;
5039 // next, protect the media tree all the while we're in here, as well as our member variables
5040 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
5041 this->lockHandle(),
5042 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5043
5044 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5045 aControllerName,
5046 aControllerPort,
5047 aDevice);
5048 if (pAttach.isNull())
5049 return setError(VBOX_E_OBJECT_NOT_FOUND,
5050 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
5051 aDevice, aControllerPort, aControllerName);
5052
5053 /* Remember previously mounted medium. The medium before taking the
5054 * backup is not necessarily the same thing. */
5055 ComObjPtr<Medium> oldmedium;
5056 oldmedium = pAttach->i_getMedium();
5057
5058 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
5059 if (aMedium && pMedium.isNull())
5060 return setError(E_INVALIDARG, "The given medium pointer is invalid");
5061
5062 AutoCaller mediumCaller(pMedium);
5063 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
5064
5065 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5066 if (pMedium)
5067 {
5068 DeviceType_T mediumType = pAttach->i_getType();
5069 switch (mediumType)
5070 {
5071 case DeviceType_DVD:
5072 case DeviceType_Floppy:
5073 break;
5074
5075 default:
5076 return setError(VBOX_E_INVALID_OBJECT_STATE,
5077 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5078 aControllerPort,
5079 aDevice,
5080 aControllerName);
5081 }
5082 }
5083
5084 setModified(IsModified_Storage);
5085 mMediaData.backup();
5086
5087 {
5088 // The backup operation makes the pAttach reference point to the
5089 // old settings. Re-get the correct reference.
5090 pAttach = findAttachment(mMediaData->mAttachments,
5091 aControllerName,
5092 aControllerPort,
5093 aDevice);
5094 if (!oldmedium.isNull())
5095 oldmedium->i_removeBackReference(mData->mUuid);
5096 if (!pMedium.isNull())
5097 {
5098 pMedium->i_addBackReference(mData->mUuid);
5099
5100 mediumLock.release();
5101 multiLock.release();
5102 addMediumToRegistry(pMedium);
5103 multiLock.acquire();
5104 mediumLock.acquire();
5105 }
5106
5107 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5108 pAttach->i_updateMedium(pMedium);
5109 }
5110
5111 setModified(IsModified_Storage);
5112
5113 mediumLock.release();
5114 multiLock.release();
5115 rc = onMediumChange(pAttach, aForce);
5116 multiLock.acquire();
5117 mediumLock.acquire();
5118
5119 /* On error roll back this change only. */
5120 if (FAILED(rc))
5121 {
5122 if (!pMedium.isNull())
5123 pMedium->i_removeBackReference(mData->mUuid);
5124 pAttach = findAttachment(mMediaData->mAttachments,
5125 aControllerName,
5126 aControllerPort,
5127 aDevice);
5128 /* If the attachment is gone in the meantime, bail out. */
5129 if (pAttach.isNull())
5130 return rc;
5131 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5132 if (!oldmedium.isNull())
5133 oldmedium->i_addBackReference(mData->mUuid);
5134 pAttach->i_updateMedium(oldmedium);
5135 }
5136
5137 mediumLock.release();
5138 multiLock.release();
5139
5140 mParent->saveModifiedRegistries();
5141
5142 return rc;
5143}
5144
5145STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5146 LONG aControllerPort,
5147 LONG aDevice,
5148 IMedium **aMedium)
5149{
5150 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5151 aControllerName, aControllerPort, aDevice));
5152
5153 CheckComArgStrNotEmptyOrNull(aControllerName);
5154 CheckComArgOutPointerValid(aMedium);
5155
5156 AutoCaller autoCaller(this);
5157 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5158
5159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5160
5161 *aMedium = NULL;
5162
5163 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5164 aControllerName,
5165 aControllerPort,
5166 aDevice);
5167 if (pAttach.isNull())
5168 return setError(VBOX_E_OBJECT_NOT_FOUND,
5169 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5170 aDevice, aControllerPort, aControllerName);
5171
5172 pAttach->i_getMedium().queryInterfaceTo(aMedium);
5173
5174 return S_OK;
5175}
5176
5177STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5178{
5179 CheckComArgOutPointerValid(port);
5180 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5181
5182 AutoCaller autoCaller(this);
5183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5184
5185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5186
5187 mSerialPorts[slot].queryInterfaceTo(port);
5188
5189 return S_OK;
5190}
5191
5192STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5193{
5194 CheckComArgOutPointerValid(port);
5195 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5196
5197 AutoCaller autoCaller(this);
5198 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5199
5200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5201
5202 mParallelPorts[slot].queryInterfaceTo(port);
5203
5204 return S_OK;
5205}
5206
5207STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5208{
5209 CheckComArgOutPointerValid(adapter);
5210 /* Do not assert if slot is out of range, just return the advertised
5211 status. testdriver/vbox.py triggers this in logVmInfo. */
5212 if (slot >= mNetworkAdapters.size())
5213 return setError(E_INVALIDARG,
5214 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5215 slot, mNetworkAdapters.size());
5216
5217 AutoCaller autoCaller(this);
5218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5219
5220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5221
5222 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5223
5224 return S_OK;
5225}
5226
5227STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5228{
5229 CheckComArgOutSafeArrayPointerValid(aKeys);
5230
5231 AutoCaller autoCaller(this);
5232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5233
5234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5235
5236 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5237 int i = 0;
5238 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5239 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5240 ++it, ++i)
5241 {
5242 const Utf8Str &strKey = it->first;
5243 strKey.cloneTo(&saKeys[i]);
5244 }
5245 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5246
5247 return S_OK;
5248 }
5249
5250 /**
5251 * @note Locks this object for reading.
5252 */
5253STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5254 BSTR *aValue)
5255{
5256 CheckComArgStrNotEmptyOrNull(aKey);
5257 CheckComArgOutPointerValid(aValue);
5258
5259 AutoCaller autoCaller(this);
5260 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5261
5262 /* start with nothing found */
5263 Bstr bstrResult("");
5264
5265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5266
5267 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5268 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5269 // found:
5270 bstrResult = it->second; // source is a Utf8Str
5271
5272 /* return the result to caller (may be empty) */
5273 bstrResult.cloneTo(aValue);
5274
5275 return S_OK;
5276}
5277
5278 /**
5279 * @note Locks mParent for writing + this object for writing.
5280 */
5281STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5282{
5283 CheckComArgStrNotEmptyOrNull(aKey);
5284
5285 AutoCaller autoCaller(this);
5286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5287
5288 Utf8Str strKey(aKey);
5289 Utf8Str strValue(aValue);
5290 Utf8Str strOldValue; // empty
5291
5292 // locking note: we only hold the read lock briefly to look up the old value,
5293 // then release it and call the onExtraCanChange callbacks. There is a small
5294 // chance of a race insofar as the callback might be called twice if two callers
5295 // change the same key at the same time, but that's a much better solution
5296 // than the deadlock we had here before. The actual changing of the extradata
5297 // is then performed under the write lock and race-free.
5298
5299 // look up the old value first; if nothing has changed then we need not do anything
5300 {
5301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5302 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5303 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5304 strOldValue = it->second;
5305 }
5306
5307 bool fChanged;
5308 if ((fChanged = (strOldValue != strValue)))
5309 {
5310 // ask for permission from all listeners outside the locks;
5311 // onExtraDataCanChange() only briefly requests the VirtualBox
5312 // lock to copy the list of callbacks to invoke
5313 Bstr error;
5314 Bstr bstrValue(aValue);
5315
5316 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5317 {
5318 const char *sep = error.isEmpty() ? "" : ": ";
5319 CBSTR err = error.raw();
5320 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5321 sep, err));
5322 return setError(E_ACCESSDENIED,
5323 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5324 aKey,
5325 bstrValue.raw(),
5326 sep,
5327 err);
5328 }
5329
5330 // data is changing and change not vetoed: then write it out under the lock
5331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5332
5333 if (isSnapshotMachine())
5334 {
5335 HRESULT rc = checkStateDependency(MutableStateDep);
5336 if (FAILED(rc)) return rc;
5337 }
5338
5339 if (strValue.isEmpty())
5340 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5341 else
5342 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5343 // creates a new key if needed
5344
5345 bool fNeedsGlobalSaveSettings = false;
5346 saveSettings(&fNeedsGlobalSaveSettings);
5347
5348 if (fNeedsGlobalSaveSettings)
5349 {
5350 // save the global settings; for that we should hold only the VirtualBox lock
5351 alock.release();
5352 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5353 mParent->saveSettings();
5354 }
5355 }
5356
5357 // fire notification outside the lock
5358 if (fChanged)
5359 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5360
5361 return S_OK;
5362}
5363
5364STDMETHODIMP Machine::SetSettingsFilePath(IN_BSTR aFilePath, IProgress **aProgress)
5365{
5366 CheckComArgStrNotEmptyOrNull(aFilePath);
5367 CheckComArgOutPointerValid(aProgress);
5368
5369 AutoCaller autoCaller(this);
5370 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5371
5372 *aProgress = NULL;
5373 ReturnComNotImplemented();
5374}
5375
5376STDMETHODIMP Machine::SaveSettings()
5377{
5378 AutoCaller autoCaller(this);
5379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5380
5381 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5382
5383 /* when there was auto-conversion, we want to save the file even if
5384 * the VM is saved */
5385 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5386 if (FAILED(rc)) return rc;
5387
5388 /* the settings file path may never be null */
5389 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5390
5391 /* save all VM data excluding snapshots */
5392 bool fNeedsGlobalSaveSettings = false;
5393 rc = saveSettings(&fNeedsGlobalSaveSettings);
5394 mlock.release();
5395
5396 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5397 {
5398 // save the global settings; for that we should hold only the VirtualBox lock
5399 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5400 rc = mParent->saveSettings();
5401 }
5402
5403 return rc;
5404}
5405
5406STDMETHODIMP Machine::DiscardSettings()
5407{
5408 AutoCaller autoCaller(this);
5409 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5410
5411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5412
5413 HRESULT rc = checkStateDependency(MutableStateDep);
5414 if (FAILED(rc)) return rc;
5415
5416 /*
5417 * during this rollback, the session will be notified if data has
5418 * been actually changed
5419 */
5420 rollback(true /* aNotify */);
5421
5422 return S_OK;
5423}
5424
5425/** @note Locks objects! */
5426STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5427 ComSafeArrayOut(IMedium*, aMedia))
5428{
5429 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5430 AutoLimitedCaller autoCaller(this);
5431 AssertComRCReturnRC(autoCaller.rc());
5432
5433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5434
5435 Guid id(getId());
5436
5437 if (mData->mSession.mState != SessionState_Unlocked)
5438 return setError(VBOX_E_INVALID_OBJECT_STATE,
5439 tr("Cannot unregister the machine '%s' while it is locked"),
5440 mUserData->s.strName.c_str());
5441
5442 // wait for state dependents to drop to zero
5443 ensureNoStateDependencies();
5444
5445 if (!mData->mAccessible)
5446 {
5447 // inaccessible maschines can only be unregistered; uninitialize ourselves
5448 // here because currently there may be no unregistered that are inaccessible
5449 // (this state combination is not supported). Note releasing the caller and
5450 // leaving the lock before calling uninit()
5451 alock.release();
5452 autoCaller.release();
5453
5454 uninit();
5455
5456 mParent->unregisterMachine(this, id);
5457 // calls VirtualBox::saveSettings()
5458
5459 return S_OK;
5460 }
5461
5462 HRESULT rc = S_OK;
5463
5464 // discard saved state
5465 if (mData->mMachineState == MachineState_Saved)
5466 {
5467 // add the saved state file to the list of files the caller should delete
5468 Assert(!mSSData->strStateFilePath.isEmpty());
5469 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5470
5471 mSSData->strStateFilePath.setNull();
5472
5473 // unconditionally set the machine state to powered off, we now
5474 // know no session has locked the machine
5475 mData->mMachineState = MachineState_PoweredOff;
5476 }
5477
5478 size_t cSnapshots = 0;
5479 if (mData->mFirstSnapshot)
5480 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5481 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5482 // fail now before we start detaching media
5483 return setError(VBOX_E_INVALID_OBJECT_STATE,
5484 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5485 mUserData->s.strName.c_str(), cSnapshots);
5486
5487 // This list collects the medium objects from all medium attachments
5488 // which we will detach from the machine and its snapshots, in a specific
5489 // order which allows for closing all media without getting "media in use"
5490 // errors, simply by going through the list from the front to the back:
5491 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5492 // and must be closed before the parent media from the snapshots, or closing the parents
5493 // will fail because they still have children);
5494 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5495 // the root ("first") snapshot of the machine.
5496 MediaList llMedia;
5497
5498 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5499 && mMediaData->mAttachments.size()
5500 )
5501 {
5502 // we have media attachments: detach them all and add the Medium objects to our list
5503 if (cleanupMode != CleanupMode_UnregisterOnly)
5504 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5505 else
5506 return setError(VBOX_E_INVALID_OBJECT_STATE,
5507 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5508 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5509 }
5510
5511 if (cSnapshots)
5512 {
5513 // autoCleanup must be true here, or we would have failed above
5514
5515 // add the media from the medium attachments of the snapshots to llMedia
5516 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5517 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5518 // into the children first
5519
5520 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5521 MachineState_T oldState = mData->mMachineState;
5522 mData->mMachineState = MachineState_DeletingSnapshot;
5523
5524 // make a copy of the first snapshot so the refcount does not drop to 0
5525 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5526 // because of the AutoCaller voodoo)
5527 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5528
5529 // GO!
5530 pFirstSnapshot->i_uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5531
5532 mData->mMachineState = oldState;
5533 }
5534
5535 if (FAILED(rc))
5536 {
5537 rollbackMedia();
5538 return rc;
5539 }
5540
5541 // commit all the media changes made above
5542 commitMedia();
5543
5544 mData->mRegistered = false;
5545
5546 // machine lock no longer needed
5547 alock.release();
5548
5549 // return media to caller
5550 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5551 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5552
5553 mParent->unregisterMachine(this, id);
5554 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5555
5556 return S_OK;
5557}
5558
5559struct Machine::DeleteTask
5560{
5561 ComObjPtr<Machine> pMachine;
5562 RTCList<ComPtr<IMedium> > llMediums;
5563 StringsList llFilesToDelete;
5564 ComObjPtr<Progress> pProgress;
5565};
5566
5567STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5568{
5569 LogFlowFuncEnter();
5570
5571 AutoCaller autoCaller(this);
5572 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5573
5574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5575
5576 HRESULT rc = checkStateDependency(MutableStateDep);
5577 if (FAILED(rc)) return rc;
5578
5579 if (mData->mRegistered)
5580 return setError(VBOX_E_INVALID_VM_STATE,
5581 tr("Cannot delete settings of a registered machine"));
5582
5583 DeleteTask *pTask = new DeleteTask;
5584 pTask->pMachine = this;
5585 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5586
5587 // collect files to delete
5588 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5589
5590 for (size_t i = 0; i < sfaMedia.size(); ++i)
5591 {
5592 IMedium *pIMedium(sfaMedia[i]);
5593 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5594 if (pMedium.isNull())
5595 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5596 SafeArray<BSTR> ids;
5597 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5598 if (FAILED(rc)) return rc;
5599 /* At this point the medium should not have any back references
5600 * anymore. If it has it is attached to another VM and *must* not
5601 * deleted. */
5602 if (ids.size() < 1)
5603 pTask->llMediums.append(pMedium);
5604 }
5605 if (mData->pMachineConfigFile->fileExists())
5606 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5607
5608 pTask->pProgress.createObject();
5609 pTask->pProgress->init(getVirtualBox(),
5610 static_cast<IMachine*>(this) /* aInitiator */,
5611 Bstr(tr("Deleting files")).raw(),
5612 true /* fCancellable */,
5613 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5614 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5615
5616 int vrc = RTThreadCreate(NULL,
5617 Machine::deleteThread,
5618 (void*)pTask,
5619 0,
5620 RTTHREADTYPE_MAIN_WORKER,
5621 0,
5622 "MachineDelete");
5623
5624 pTask->pProgress.queryInterfaceTo(aProgress);
5625
5626 if (RT_FAILURE(vrc))
5627 {
5628 delete pTask;
5629 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5630 }
5631
5632 LogFlowFuncLeave();
5633
5634 return S_OK;
5635}
5636
5637/**
5638 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5639 * calls Machine::deleteTaskWorker() on the actual machine object.
5640 * @param Thread
5641 * @param pvUser
5642 * @return
5643 */
5644/*static*/
5645DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5646{
5647 LogFlowFuncEnter();
5648
5649 DeleteTask *pTask = (DeleteTask*)pvUser;
5650 Assert(pTask);
5651 Assert(pTask->pMachine);
5652 Assert(pTask->pProgress);
5653
5654 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5655 pTask->pProgress->notifyComplete(rc);
5656
5657 delete pTask;
5658
5659 LogFlowFuncLeave();
5660
5661 NOREF(Thread);
5662
5663 return VINF_SUCCESS;
5664}
5665
5666/**
5667 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5668 * @param task
5669 * @return
5670 */
5671HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5672{
5673 AutoCaller autoCaller(this);
5674 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5675
5676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5677
5678 HRESULT rc = S_OK;
5679
5680 try
5681 {
5682 ULONG uLogHistoryCount = 3;
5683 ComPtr<ISystemProperties> systemProperties;
5684 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5685 if (FAILED(rc)) throw rc;
5686
5687 if (!systemProperties.isNull())
5688 {
5689 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5690 if (FAILED(rc)) throw rc;
5691 }
5692
5693 MachineState_T oldState = mData->mMachineState;
5694 setMachineState(MachineState_SettingUp);
5695 alock.release();
5696 for (size_t i = 0; i < task.llMediums.size(); ++i)
5697 {
5698 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5699 {
5700 AutoCaller mac(pMedium);
5701 if (FAILED(mac.rc())) throw mac.rc();
5702 Utf8Str strLocation = pMedium->i_getLocationFull();
5703 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5704 if (FAILED(rc)) throw rc;
5705 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5706 }
5707 ComPtr<IProgress> pProgress2;
5708 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5709 if (FAILED(rc)) throw rc;
5710 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5711 if (FAILED(rc)) throw rc;
5712 /* Check the result of the asynchronous process. */
5713 LONG iRc;
5714 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5715 if (FAILED(rc)) throw rc;
5716 /* If the thread of the progress object has an error, then
5717 * retrieve the error info from there, or it'll be lost. */
5718 if (FAILED(iRc))
5719 throw setError(ProgressErrorInfo(pProgress2));
5720
5721 /* Close the medium, deliberately without checking the return
5722 * code, and without leaving any trace in the error info, as
5723 * a failure here is a very minor issue, which shouldn't happen
5724 * as above we even managed to delete the medium. */
5725 {
5726 ErrorInfoKeeper eik;
5727 pMedium->Close();
5728 }
5729 }
5730 setMachineState(oldState);
5731 alock.acquire();
5732
5733 // delete the files pushed on the task list by Machine::Delete()
5734 // (this includes saved states of the machine and snapshots and
5735 // medium storage files from the IMedium list passed in, and the
5736 // machine XML file)
5737 StringsList::const_iterator it = task.llFilesToDelete.begin();
5738 while (it != task.llFilesToDelete.end())
5739 {
5740 const Utf8Str &strFile = *it;
5741 LogFunc(("Deleting file %s\n", strFile.c_str()));
5742 int vrc = RTFileDelete(strFile.c_str());
5743 if (RT_FAILURE(vrc))
5744 throw setError(VBOX_E_IPRT_ERROR,
5745 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5746
5747 ++it;
5748 if (it == task.llFilesToDelete.end())
5749 {
5750 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5751 if (FAILED(rc)) throw rc;
5752 break;
5753 }
5754
5755 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5756 if (FAILED(rc)) throw rc;
5757 }
5758
5759 /* delete the settings only when the file actually exists */
5760 if (mData->pMachineConfigFile->fileExists())
5761 {
5762 /* Delete any backup or uncommitted XML files. Ignore failures.
5763 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5764 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5765 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5766 RTFileDelete(otherXml.c_str());
5767 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5768 RTFileDelete(otherXml.c_str());
5769
5770 /* delete the Logs folder, nothing important should be left
5771 * there (we don't check for errors because the user might have
5772 * some private files there that we don't want to delete) */
5773 Utf8Str logFolder;
5774 getLogFolder(logFolder);
5775 Assert(logFolder.length());
5776 if (RTDirExists(logFolder.c_str()))
5777 {
5778 /* Delete all VBox.log[.N] files from the Logs folder
5779 * (this must be in sync with the rotation logic in
5780 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5781 * files that may have been created by the GUI. */
5782 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5783 logFolder.c_str(), RTPATH_DELIMITER);
5784 RTFileDelete(log.c_str());
5785 log = Utf8StrFmt("%s%cVBox.png",
5786 logFolder.c_str(), RTPATH_DELIMITER);
5787 RTFileDelete(log.c_str());
5788 for (int i = uLogHistoryCount; i > 0; i--)
5789 {
5790 log = Utf8StrFmt("%s%cVBox.log.%d",
5791 logFolder.c_str(), RTPATH_DELIMITER, i);
5792 RTFileDelete(log.c_str());
5793 log = Utf8StrFmt("%s%cVBox.png.%d",
5794 logFolder.c_str(), RTPATH_DELIMITER, i);
5795 RTFileDelete(log.c_str());
5796 }
5797
5798 RTDirRemove(logFolder.c_str());
5799 }
5800
5801 /* delete the Snapshots folder, nothing important should be left
5802 * there (we don't check for errors because the user might have
5803 * some private files there that we don't want to delete) */
5804 Utf8Str strFullSnapshotFolder;
5805 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5806 Assert(!strFullSnapshotFolder.isEmpty());
5807 if (RTDirExists(strFullSnapshotFolder.c_str()))
5808 RTDirRemove(strFullSnapshotFolder.c_str());
5809
5810 // delete the directory that contains the settings file, but only
5811 // if it matches the VM name
5812 Utf8Str settingsDir;
5813 if (isInOwnDir(&settingsDir))
5814 RTDirRemove(settingsDir.c_str());
5815 }
5816
5817 alock.release();
5818
5819 mParent->saveModifiedRegistries();
5820 }
5821 catch (HRESULT aRC) { rc = aRC; }
5822
5823 return rc;
5824}
5825
5826STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5827{
5828 CheckComArgOutPointerValid(aSnapshot);
5829
5830 AutoCaller autoCaller(this);
5831 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5832
5833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5834
5835 ComObjPtr<Snapshot> pSnapshot;
5836 HRESULT rc;
5837
5838 if (!aNameOrId || !*aNameOrId)
5839 // null case (caller wants root snapshot): findSnapshotById() handles this
5840 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5841 else
5842 {
5843 Guid uuid(aNameOrId);
5844 if (uuid.isValid())
5845 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5846 else
5847 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5848 }
5849 pSnapshot.queryInterfaceTo(aSnapshot);
5850
5851 return rc;
5852}
5853
5854STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5855{
5856 CheckComArgStrNotEmptyOrNull(aName);
5857 CheckComArgStrNotEmptyOrNull(aHostPath);
5858
5859 AutoCaller autoCaller(this);
5860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5861
5862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5863
5864 HRESULT rc = checkStateDependency(MutableStateDep);
5865 if (FAILED(rc)) return rc;
5866
5867 Utf8Str strName(aName);
5868
5869 ComObjPtr<SharedFolder> sharedFolder;
5870 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5871 if (SUCCEEDED(rc))
5872 return setError(VBOX_E_OBJECT_IN_USE,
5873 tr("Shared folder named '%s' already exists"),
5874 strName.c_str());
5875
5876 sharedFolder.createObject();
5877 rc = sharedFolder->init(getMachine(),
5878 strName,
5879 aHostPath,
5880 !!aWritable,
5881 !!aAutoMount,
5882 true /* fFailOnError */);
5883 if (FAILED(rc)) return rc;
5884
5885 setModified(IsModified_SharedFolders);
5886 mHWData.backup();
5887 mHWData->mSharedFolders.push_back(sharedFolder);
5888
5889 /* inform the direct session if any */
5890 alock.release();
5891 onSharedFolderChange();
5892
5893 return S_OK;
5894}
5895
5896STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5897{
5898 CheckComArgStrNotEmptyOrNull(aName);
5899
5900 AutoCaller autoCaller(this);
5901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5902
5903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5904
5905 HRESULT rc = checkStateDependency(MutableStateDep);
5906 if (FAILED(rc)) return rc;
5907
5908 ComObjPtr<SharedFolder> sharedFolder;
5909 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5910 if (FAILED(rc)) return rc;
5911
5912 setModified(IsModified_SharedFolders);
5913 mHWData.backup();
5914 mHWData->mSharedFolders.remove(sharedFolder);
5915
5916 /* inform the direct session if any */
5917 alock.release();
5918 onSharedFolderChange();
5919
5920 return S_OK;
5921}
5922
5923STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5924{
5925 CheckComArgOutPointerValid(aCanShow);
5926
5927 /* start with No */
5928 *aCanShow = FALSE;
5929
5930 AutoCaller autoCaller(this);
5931 AssertComRCReturnRC(autoCaller.rc());
5932
5933 ComPtr<IInternalSessionControl> directControl;
5934 {
5935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5936
5937 if (mData->mSession.mState != SessionState_Locked)
5938 return setError(VBOX_E_INVALID_VM_STATE,
5939 tr("Machine is not locked for session (session state: %s)"),
5940 Global::stringifySessionState(mData->mSession.mState));
5941
5942 directControl = mData->mSession.mDirectControl;
5943 }
5944
5945 /* ignore calls made after #OnSessionEnd() is called */
5946 if (!directControl)
5947 return S_OK;
5948
5949 LONG64 dummy;
5950 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5951}
5952
5953STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5954{
5955 CheckComArgOutPointerValid(aWinId);
5956
5957 AutoCaller autoCaller(this);
5958 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5959
5960 ComPtr<IInternalSessionControl> directControl;
5961 {
5962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5963
5964 if (mData->mSession.mState != SessionState_Locked)
5965 return setError(E_FAIL,
5966 tr("Machine is not locked for session (session state: %s)"),
5967 Global::stringifySessionState(mData->mSession.mState));
5968
5969 directControl = mData->mSession.mDirectControl;
5970 }
5971
5972 /* ignore calls made after #OnSessionEnd() is called */
5973 if (!directControl)
5974 return S_OK;
5975
5976 BOOL dummy;
5977 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5978}
5979
5980#ifdef VBOX_WITH_GUEST_PROPS
5981/**
5982 * Look up a guest property in VBoxSVC's internal structures.
5983 */
5984HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5985 BSTR *aValue,
5986 LONG64 *aTimestamp,
5987 BSTR *aFlags) const
5988{
5989 using namespace guestProp;
5990
5991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5992 Utf8Str strName(aName);
5993 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5994
5995 if (it != mHWData->mGuestProperties.end())
5996 {
5997 char szFlags[MAX_FLAGS_LEN + 1];
5998 it->second.strValue.cloneTo(aValue);
5999 *aTimestamp = it->second.mTimestamp;
6000 writeFlags(it->second.mFlags, szFlags);
6001 Bstr(szFlags).cloneTo(aFlags);
6002 }
6003
6004 return S_OK;
6005}
6006
6007/**
6008 * Query the VM that a guest property belongs to for the property.
6009 * @returns E_ACCESSDENIED if the VM process is not available or not
6010 * currently handling queries and the lookup should then be done in
6011 * VBoxSVC.
6012 */
6013HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
6014 BSTR *aValue,
6015 LONG64 *aTimestamp,
6016 BSTR *aFlags) const
6017{
6018 HRESULT rc;
6019 ComPtr<IInternalSessionControl> directControl;
6020 directControl = mData->mSession.mDirectControl;
6021
6022 /* fail if we were called after #OnSessionEnd() is called. This is a
6023 * silly race condition. */
6024
6025 /** @todo This code is bothering API clients (like python script clients) with
6026 * the AccessGuestProperty call, creating unncessary IPC. Need to
6027 * have a way of figuring out which kind of direct session it is... */
6028 if (!directControl)
6029 rc = E_ACCESSDENIED;
6030 else
6031 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
6032 false /* isSetter */,
6033 aValue, aTimestamp, aFlags);
6034 return rc;
6035}
6036#endif // VBOX_WITH_GUEST_PROPS
6037
6038STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
6039 BSTR *aValue,
6040 LONG64 *aTimestamp,
6041 BSTR *aFlags)
6042{
6043#ifndef VBOX_WITH_GUEST_PROPS
6044 ReturnComNotImplemented();
6045#else // VBOX_WITH_GUEST_PROPS
6046 CheckComArgStrNotEmptyOrNull(aName);
6047 CheckComArgOutPointerValid(aValue);
6048 CheckComArgOutPointerValid(aTimestamp);
6049 CheckComArgOutPointerValid(aFlags);
6050
6051 AutoCaller autoCaller(this);
6052 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6053
6054 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
6055 if (rc == E_ACCESSDENIED)
6056 /* The VM is not running or the service is not (yet) accessible */
6057 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
6058 return rc;
6059#endif // VBOX_WITH_GUEST_PROPS
6060}
6061
6062STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
6063{
6064 LONG64 dummyTimestamp;
6065 Bstr dummyFlags;
6066 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
6067}
6068
6069STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
6070{
6071 Bstr dummyValue;
6072 Bstr dummyFlags;
6073 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
6074}
6075
6076#ifdef VBOX_WITH_GUEST_PROPS
6077/**
6078 * Set a guest property in VBoxSVC's internal structures.
6079 */
6080HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
6081 IN_BSTR aFlags)
6082{
6083 using namespace guestProp;
6084
6085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6086 HRESULT rc = S_OK;
6087
6088 rc = checkStateDependency(MutableStateDep);
6089 if (FAILED(rc)) return rc;
6090
6091 try
6092 {
6093 Utf8Str utf8Name(aName);
6094 Utf8Str utf8Flags(aFlags);
6095 uint32_t fFlags = NILFLAG;
6096 if ( aFlags != NULL
6097 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6098 return setError(E_INVALIDARG,
6099 tr("Invalid guest property flag values: '%ls'"),
6100 aFlags);
6101
6102 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6103 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6104 if (it == mHWData->mGuestProperties.end())
6105 {
6106 if (!fDelete)
6107 {
6108 setModified(IsModified_MachineData);
6109 mHWData.backupEx();
6110
6111 RTTIMESPEC time;
6112 HWData::GuestProperty prop;
6113 prop.strValue = aValue;
6114 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6115 prop.mFlags = fFlags;
6116 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6117 }
6118 }
6119 else
6120 {
6121 if (it->second.mFlags & (RDONLYHOST))
6122 {
6123 rc = setError(E_ACCESSDENIED,
6124 tr("The property '%ls' cannot be changed by the host"),
6125 aName);
6126 }
6127 else
6128 {
6129 setModified(IsModified_MachineData);
6130 mHWData.backupEx();
6131
6132 /* The backupEx() operation invalidates our iterator,
6133 * so get a new one. */
6134 it = mHWData->mGuestProperties.find(utf8Name);
6135 Assert(it != mHWData->mGuestProperties.end());
6136
6137 if (!fDelete)
6138 {
6139 RTTIMESPEC time;
6140 it->second.strValue = aValue;
6141 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6142 it->second.mFlags = fFlags;
6143 }
6144 else
6145 mHWData->mGuestProperties.erase(it);
6146 }
6147 }
6148
6149 if ( SUCCEEDED(rc)
6150 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6151 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6152 RTSTR_MAX,
6153 utf8Name.c_str(),
6154 RTSTR_MAX,
6155 NULL)
6156 )
6157 )
6158 {
6159 alock.release();
6160
6161 mParent->onGuestPropertyChange(mData->mUuid, aName,
6162 aValue ? aValue : Bstr("").raw(),
6163 aFlags ? aFlags : Bstr("").raw());
6164 }
6165 }
6166 catch (std::bad_alloc &)
6167 {
6168 rc = E_OUTOFMEMORY;
6169 }
6170
6171 return rc;
6172}
6173
6174/**
6175 * Set a property on the VM that that property belongs to.
6176 * @returns E_ACCESSDENIED if the VM process is not available or not
6177 * currently handling queries and the setting should then be done in
6178 * VBoxSVC.
6179 */
6180HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6181 IN_BSTR aFlags)
6182{
6183 HRESULT rc;
6184
6185 try
6186 {
6187 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6188
6189 BSTR dummy = NULL; /* will not be changed (setter) */
6190 LONG64 dummy64;
6191 if (!directControl)
6192 rc = E_ACCESSDENIED;
6193 else
6194 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6195 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6196 true /* isSetter */,
6197 &dummy, &dummy64, &dummy);
6198 }
6199 catch (std::bad_alloc &)
6200 {
6201 rc = E_OUTOFMEMORY;
6202 }
6203
6204 return rc;
6205}
6206#endif // VBOX_WITH_GUEST_PROPS
6207
6208STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6209 IN_BSTR aFlags)
6210{
6211#ifndef VBOX_WITH_GUEST_PROPS
6212 ReturnComNotImplemented();
6213#else // VBOX_WITH_GUEST_PROPS
6214 CheckComArgStrNotEmptyOrNull(aName);
6215 CheckComArgMaybeNull(aFlags);
6216 CheckComArgMaybeNull(aValue);
6217
6218 AutoCaller autoCaller(this);
6219 if (FAILED(autoCaller.rc()))
6220 return autoCaller.rc();
6221
6222 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6223 if (rc == E_ACCESSDENIED)
6224 /* The VM is not running or the service is not (yet) accessible */
6225 rc = setGuestPropertyToService(aName, aValue, aFlags);
6226 return rc;
6227#endif // VBOX_WITH_GUEST_PROPS
6228}
6229
6230STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6231{
6232 return SetGuestProperty(aName, aValue, NULL);
6233}
6234
6235STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6236{
6237 return SetGuestProperty(aName, NULL, NULL);
6238}
6239
6240#ifdef VBOX_WITH_GUEST_PROPS
6241/**
6242 * Enumerate the guest properties in VBoxSVC's internal structures.
6243 */
6244HRESULT Machine::enumerateGuestPropertiesInService
6245 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6246 ComSafeArrayOut(BSTR, aValues),
6247 ComSafeArrayOut(LONG64, aTimestamps),
6248 ComSafeArrayOut(BSTR, aFlags))
6249{
6250 using namespace guestProp;
6251
6252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6253 Utf8Str strPatterns(aPatterns);
6254
6255 HWData::GuestPropertyMap propMap;
6256
6257 /*
6258 * Look for matching patterns and build up a list.
6259 */
6260 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6261 while (it != mHWData->mGuestProperties.end())
6262 {
6263 if ( strPatterns.isEmpty()
6264 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6265 RTSTR_MAX,
6266 it->first.c_str(),
6267 RTSTR_MAX,
6268 NULL)
6269 )
6270 {
6271 propMap.insert(*it);
6272 }
6273
6274 it++;
6275 }
6276
6277 alock.release();
6278
6279 /*
6280 * And build up the arrays for returning the property information.
6281 */
6282 size_t cEntries = propMap.size();
6283 SafeArray<BSTR> names(cEntries);
6284 SafeArray<BSTR> values(cEntries);
6285 SafeArray<LONG64> timestamps(cEntries);
6286 SafeArray<BSTR> flags(cEntries);
6287 size_t iProp = 0;
6288
6289 it = propMap.begin();
6290 while (it != propMap.end())
6291 {
6292 char szFlags[MAX_FLAGS_LEN + 1];
6293 it->first.cloneTo(&names[iProp]);
6294 it->second.strValue.cloneTo(&values[iProp]);
6295 timestamps[iProp] = it->second.mTimestamp;
6296 writeFlags(it->second.mFlags, szFlags);
6297 Bstr(szFlags).cloneTo(&flags[iProp++]);
6298 it++;
6299 }
6300 names.detachTo(ComSafeArrayOutArg(aNames));
6301 values.detachTo(ComSafeArrayOutArg(aValues));
6302 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6303 flags.detachTo(ComSafeArrayOutArg(aFlags));
6304 return S_OK;
6305}
6306
6307/**
6308 * Enumerate the properties managed by a VM.
6309 * @returns E_ACCESSDENIED if the VM process is not available or not
6310 * currently handling queries and the setting should then be done in
6311 * VBoxSVC.
6312 */
6313HRESULT Machine::enumerateGuestPropertiesOnVM
6314 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6315 ComSafeArrayOut(BSTR, aValues),
6316 ComSafeArrayOut(LONG64, aTimestamps),
6317 ComSafeArrayOut(BSTR, aFlags))
6318{
6319 HRESULT rc;
6320 ComPtr<IInternalSessionControl> directControl;
6321 directControl = mData->mSession.mDirectControl;
6322
6323 if (!directControl)
6324 rc = E_ACCESSDENIED;
6325 else
6326 rc = directControl->EnumerateGuestProperties
6327 (aPatterns, ComSafeArrayOutArg(aNames),
6328 ComSafeArrayOutArg(aValues),
6329 ComSafeArrayOutArg(aTimestamps),
6330 ComSafeArrayOutArg(aFlags));
6331 return rc;
6332}
6333#endif // VBOX_WITH_GUEST_PROPS
6334
6335STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6336 ComSafeArrayOut(BSTR, aNames),
6337 ComSafeArrayOut(BSTR, aValues),
6338 ComSafeArrayOut(LONG64, aTimestamps),
6339 ComSafeArrayOut(BSTR, aFlags))
6340{
6341#ifndef VBOX_WITH_GUEST_PROPS
6342 ReturnComNotImplemented();
6343#else // VBOX_WITH_GUEST_PROPS
6344 CheckComArgMaybeNull(aPatterns);
6345 CheckComArgOutSafeArrayPointerValid(aNames);
6346 CheckComArgOutSafeArrayPointerValid(aValues);
6347 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6348 CheckComArgOutSafeArrayPointerValid(aFlags);
6349
6350 AutoCaller autoCaller(this);
6351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6352
6353 HRESULT rc = enumerateGuestPropertiesOnVM
6354 (aPatterns, ComSafeArrayOutArg(aNames),
6355 ComSafeArrayOutArg(aValues),
6356 ComSafeArrayOutArg(aTimestamps),
6357 ComSafeArrayOutArg(aFlags));
6358 if (rc == E_ACCESSDENIED)
6359 /* The VM is not running or the service is not (yet) accessible */
6360 rc = enumerateGuestPropertiesInService
6361 (aPatterns, ComSafeArrayOutArg(aNames),
6362 ComSafeArrayOutArg(aValues),
6363 ComSafeArrayOutArg(aTimestamps),
6364 ComSafeArrayOutArg(aFlags));
6365 return rc;
6366#endif // VBOX_WITH_GUEST_PROPS
6367}
6368
6369STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6370 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6371{
6372 MediaData::AttachmentList atts;
6373
6374 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6375 if (FAILED(rc)) return rc;
6376
6377 SafeIfaceArray<IMediumAttachment> attachments(atts);
6378 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6379
6380 return S_OK;
6381}
6382
6383STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6384 LONG aControllerPort,
6385 LONG aDevice,
6386 IMediumAttachment **aAttachment)
6387{
6388 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6389 aControllerName, aControllerPort, aDevice));
6390
6391 CheckComArgStrNotEmptyOrNull(aControllerName);
6392 CheckComArgOutPointerValid(aAttachment);
6393
6394 AutoCaller autoCaller(this);
6395 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6396
6397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6398
6399 *aAttachment = NULL;
6400
6401 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6402 aControllerName,
6403 aControllerPort,
6404 aDevice);
6405 if (pAttach.isNull())
6406 return setError(VBOX_E_OBJECT_NOT_FOUND,
6407 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6408 aDevice, aControllerPort, aControllerName);
6409
6410 pAttach.queryInterfaceTo(aAttachment);
6411
6412 return S_OK;
6413}
6414
6415STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6416 StorageBus_T aConnectionType,
6417 IStorageController **controller)
6418{
6419 CheckComArgStrNotEmptyOrNull(aName);
6420
6421 if ( (aConnectionType <= StorageBus_Null)
6422 || (aConnectionType > StorageBus_USB))
6423 return setError(E_INVALIDARG,
6424 tr("Invalid connection type: %d"),
6425 aConnectionType);
6426
6427 AutoCaller autoCaller(this);
6428 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6429
6430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6431
6432 HRESULT rc = checkStateDependency(MutableStateDep);
6433 if (FAILED(rc)) return rc;
6434
6435 /* try to find one with the name first. */
6436 ComObjPtr<StorageController> ctrl;
6437
6438 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6439 if (SUCCEEDED(rc))
6440 return setError(VBOX_E_OBJECT_IN_USE,
6441 tr("Storage controller named '%ls' already exists"),
6442 aName);
6443
6444 ctrl.createObject();
6445
6446 /* get a new instance number for the storage controller */
6447 ULONG ulInstance = 0;
6448 bool fBootable = true;
6449 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6450 it != mStorageControllers->end();
6451 ++it)
6452 {
6453 if ((*it)->i_getStorageBus() == aConnectionType)
6454 {
6455 ULONG ulCurInst = (*it)->i_getInstance();
6456
6457 if (ulCurInst >= ulInstance)
6458 ulInstance = ulCurInst + 1;
6459
6460 /* Only one controller of each type can be marked as bootable. */
6461 if ((*it)->i_getBootable())
6462 fBootable = false;
6463 }
6464 }
6465
6466 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6467 if (FAILED(rc)) return rc;
6468
6469 setModified(IsModified_Storage);
6470 mStorageControllers.backup();
6471 mStorageControllers->push_back(ctrl);
6472
6473 ctrl.queryInterfaceTo(controller);
6474
6475 /* inform the direct session if any */
6476 alock.release();
6477 onStorageControllerChange();
6478
6479 return S_OK;
6480}
6481
6482STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6483 IStorageController **aStorageController)
6484{
6485 CheckComArgStrNotEmptyOrNull(aName);
6486
6487 AutoCaller autoCaller(this);
6488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6489
6490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6491
6492 ComObjPtr<StorageController> ctrl;
6493
6494 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6495 if (SUCCEEDED(rc))
6496 ctrl.queryInterfaceTo(aStorageController);
6497
6498 return rc;
6499}
6500
6501STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6502 IStorageController **aStorageController)
6503{
6504 AutoCaller autoCaller(this);
6505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6506
6507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6508
6509 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6510 it != mStorageControllers->end();
6511 ++it)
6512 {
6513 if ((*it)->i_getInstance() == aInstance)
6514 {
6515 (*it).queryInterfaceTo(aStorageController);
6516 return S_OK;
6517 }
6518 }
6519
6520 return setError(VBOX_E_OBJECT_NOT_FOUND,
6521 tr("Could not find a storage controller with instance number '%lu'"),
6522 aInstance);
6523}
6524
6525STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6526{
6527 AutoCaller autoCaller(this);
6528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6529
6530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6531
6532 HRESULT rc = checkStateDependency(MutableStateDep);
6533 if (FAILED(rc)) return rc;
6534
6535 ComObjPtr<StorageController> ctrl;
6536
6537 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6538 if (SUCCEEDED(rc))
6539 {
6540 /* Ensure that only one controller of each type is marked as bootable. */
6541 if (fBootable == TRUE)
6542 {
6543 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6544 it != mStorageControllers->end();
6545 ++it)
6546 {
6547 ComObjPtr<StorageController> aCtrl = (*it);
6548
6549 if ( (aCtrl->i_getName() != Utf8Str(aName))
6550 && aCtrl->i_getBootable() == TRUE
6551 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6552 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6553 {
6554 aCtrl->i_setBootable(FALSE);
6555 break;
6556 }
6557 }
6558 }
6559
6560 if (SUCCEEDED(rc))
6561 {
6562 ctrl->i_setBootable(fBootable);
6563 setModified(IsModified_Storage);
6564 }
6565 }
6566
6567 if (SUCCEEDED(rc))
6568 {
6569 /* inform the direct session if any */
6570 alock.release();
6571 onStorageControllerChange();
6572 }
6573
6574 return rc;
6575}
6576
6577STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6578{
6579 CheckComArgStrNotEmptyOrNull(aName);
6580
6581 AutoCaller autoCaller(this);
6582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6583
6584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6585
6586 HRESULT rc = checkStateDependency(MutableStateDep);
6587 if (FAILED(rc)) return rc;
6588
6589 ComObjPtr<StorageController> ctrl;
6590 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6591 if (FAILED(rc)) return rc;
6592
6593 {
6594 /* find all attached devices to the appropriate storage controller and detach them all */
6595 // make a temporary list because detachDevice invalidates iterators into
6596 // mMediaData->mAttachments
6597 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6598
6599 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6600 it != llAttachments2.end();
6601 ++it)
6602 {
6603 MediumAttachment *pAttachTemp = *it;
6604
6605 AutoCaller localAutoCaller(pAttachTemp);
6606 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6607
6608 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6609
6610 if (pAttachTemp->i_getControllerName() == aName)
6611 {
6612 rc = detachDevice(pAttachTemp, alock, NULL);
6613 if (FAILED(rc)) return rc;
6614 }
6615 }
6616 }
6617
6618 /* We can remove it now. */
6619 setModified(IsModified_Storage);
6620 mStorageControllers.backup();
6621
6622 ctrl->i_unshare();
6623
6624 mStorageControllers->remove(ctrl);
6625
6626 /* inform the direct session if any */
6627 alock.release();
6628 onStorageControllerChange();
6629
6630 return S_OK;
6631}
6632
6633STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6634 IUSBController **controller)
6635{
6636 if ( (aType <= USBControllerType_Null)
6637 || (aType >= USBControllerType_Last))
6638 return setError(E_INVALIDARG,
6639 tr("Invalid USB controller type: %d"),
6640 aType);
6641
6642 AutoCaller autoCaller(this);
6643 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6644
6645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6646
6647 HRESULT rc = checkStateDependency(MutableStateDep);
6648 if (FAILED(rc)) return rc;
6649
6650 /* try to find one with the same type first. */
6651 ComObjPtr<USBController> ctrl;
6652
6653 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6654 if (SUCCEEDED(rc))
6655 return setError(VBOX_E_OBJECT_IN_USE,
6656 tr("USB controller named '%ls' already exists"),
6657 aName);
6658
6659 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6660 ULONG maxInstances;
6661 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6662 if (FAILED(rc))
6663 return rc;
6664
6665 ULONG cInstances = getUSBControllerCountByType(aType);
6666 if (cInstances >= maxInstances)
6667 return setError(E_INVALIDARG,
6668 tr("Too many USB controllers of this type"));
6669
6670 ctrl.createObject();
6671
6672 rc = ctrl->init(this, aName, aType);
6673 if (FAILED(rc)) return rc;
6674
6675 setModified(IsModified_USB);
6676 mUSBControllers.backup();
6677 mUSBControllers->push_back(ctrl);
6678
6679 ctrl.queryInterfaceTo(controller);
6680
6681 /* inform the direct session if any */
6682 alock.release();
6683 onUSBControllerChange();
6684
6685 return S_OK;
6686}
6687
6688STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6689{
6690 CheckComArgStrNotEmptyOrNull(aName);
6691
6692 AutoCaller autoCaller(this);
6693 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6694
6695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6696
6697 ComObjPtr<USBController> ctrl;
6698
6699 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6700 if (SUCCEEDED(rc))
6701 ctrl.queryInterfaceTo(aUSBController);
6702
6703 return rc;
6704}
6705
6706STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6707 ULONG *aControllers)
6708{
6709 CheckComArgOutPointerValid(aControllers);
6710
6711 if ( (aType <= USBControllerType_Null)
6712 || (aType >= USBControllerType_Last))
6713 return setError(E_INVALIDARG,
6714 tr("Invalid USB controller type: %d"),
6715 aType);
6716
6717 AutoCaller autoCaller(this);
6718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6719
6720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6721
6722 ComObjPtr<USBController> ctrl;
6723
6724 *aControllers = getUSBControllerCountByType(aType);
6725
6726 return S_OK;
6727}
6728
6729STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6730{
6731 CheckComArgStrNotEmptyOrNull(aName);
6732
6733 AutoCaller autoCaller(this);
6734 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6735
6736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6737
6738 HRESULT rc = checkStateDependency(MutableStateDep);
6739 if (FAILED(rc)) return rc;
6740
6741 ComObjPtr<USBController> ctrl;
6742 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6743 if (FAILED(rc)) return rc;
6744
6745 setModified(IsModified_USB);
6746 mUSBControllers.backup();
6747
6748 ctrl->i_unshare();
6749
6750 mUSBControllers->remove(ctrl);
6751
6752 /* inform the direct session if any */
6753 alock.release();
6754 onUSBControllerChange();
6755
6756 return S_OK;
6757}
6758
6759STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6760 ULONG *puOriginX,
6761 ULONG *puOriginY,
6762 ULONG *puWidth,
6763 ULONG *puHeight,
6764 BOOL *pfEnabled)
6765{
6766 LogFlowThisFunc(("\n"));
6767
6768 CheckComArgNotNull(puOriginX);
6769 CheckComArgNotNull(puOriginY);
6770 CheckComArgNotNull(puWidth);
6771 CheckComArgNotNull(puHeight);
6772 CheckComArgNotNull(pfEnabled);
6773
6774 uint32_t u32OriginX= 0;
6775 uint32_t u32OriginY= 0;
6776 uint32_t u32Width = 0;
6777 uint32_t u32Height = 0;
6778 uint16_t u16Flags = 0;
6779
6780 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6781 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6782 if (RT_FAILURE(vrc))
6783 {
6784#ifdef RT_OS_WINDOWS
6785 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6786 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6787 * So just assign fEnable to TRUE again.
6788 * The right fix would be to change GUI API wrappers to make sure that parameters
6789 * are changed only if API succeeds.
6790 */
6791 *pfEnabled = TRUE;
6792#endif
6793 return setError(VBOX_E_IPRT_ERROR,
6794 tr("Saved guest size is not available (%Rrc)"),
6795 vrc);
6796 }
6797
6798 *puOriginX = u32OriginX;
6799 *puOriginY = u32OriginY;
6800 *puWidth = u32Width;
6801 *puHeight = u32Height;
6802 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6803
6804 return S_OK;
6805}
6806
6807STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6808{
6809 LogFlowThisFunc(("\n"));
6810
6811 CheckComArgNotNull(aSize);
6812 CheckComArgNotNull(aWidth);
6813 CheckComArgNotNull(aHeight);
6814
6815 if (aScreenId != 0)
6816 return E_NOTIMPL;
6817
6818 AutoCaller autoCaller(this);
6819 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6820
6821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6822
6823 uint8_t *pu8Data = NULL;
6824 uint32_t cbData = 0;
6825 uint32_t u32Width = 0;
6826 uint32_t u32Height = 0;
6827
6828 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6829
6830 if (RT_FAILURE(vrc))
6831 return setError(VBOX_E_IPRT_ERROR,
6832 tr("Saved screenshot data is not available (%Rrc)"),
6833 vrc);
6834
6835 *aSize = cbData;
6836 *aWidth = u32Width;
6837 *aHeight = u32Height;
6838
6839 freeSavedDisplayScreenshot(pu8Data);
6840
6841 return S_OK;
6842}
6843
6844STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6845{
6846 LogFlowThisFunc(("\n"));
6847
6848 CheckComArgNotNull(aWidth);
6849 CheckComArgNotNull(aHeight);
6850 CheckComArgOutSafeArrayPointerValid(aData);
6851
6852 if (aScreenId != 0)
6853 return E_NOTIMPL;
6854
6855 AutoCaller autoCaller(this);
6856 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6857
6858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6859
6860 uint8_t *pu8Data = NULL;
6861 uint32_t cbData = 0;
6862 uint32_t u32Width = 0;
6863 uint32_t u32Height = 0;
6864
6865 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6866
6867 if (RT_FAILURE(vrc))
6868 return setError(VBOX_E_IPRT_ERROR,
6869 tr("Saved screenshot data is not available (%Rrc)"),
6870 vrc);
6871
6872 *aWidth = u32Width;
6873 *aHeight = u32Height;
6874
6875 com::SafeArray<BYTE> bitmap(cbData);
6876 /* Convert pixels to format expected by the API caller. */
6877 if (aBGR)
6878 {
6879 /* [0] B, [1] G, [2] R, [3] A. */
6880 for (unsigned i = 0; i < cbData; i += 4)
6881 {
6882 bitmap[i] = pu8Data[i];
6883 bitmap[i + 1] = pu8Data[i + 1];
6884 bitmap[i + 2] = pu8Data[i + 2];
6885 bitmap[i + 3] = 0xff;
6886 }
6887 }
6888 else
6889 {
6890 /* [0] R, [1] G, [2] B, [3] A. */
6891 for (unsigned i = 0; i < cbData; i += 4)
6892 {
6893 bitmap[i] = pu8Data[i + 2];
6894 bitmap[i + 1] = pu8Data[i + 1];
6895 bitmap[i + 2] = pu8Data[i];
6896 bitmap[i + 3] = 0xff;
6897 }
6898 }
6899 bitmap.detachTo(ComSafeArrayOutArg(aData));
6900
6901 freeSavedDisplayScreenshot(pu8Data);
6902
6903 return S_OK;
6904}
6905
6906
6907STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6908{
6909 LogFlowThisFunc(("\n"));
6910
6911 CheckComArgNotNull(aWidth);
6912 CheckComArgNotNull(aHeight);
6913 CheckComArgOutSafeArrayPointerValid(aData);
6914
6915 if (aScreenId != 0)
6916 return E_NOTIMPL;
6917
6918 AutoCaller autoCaller(this);
6919 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6920
6921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6922
6923 uint8_t *pu8Data = NULL;
6924 uint32_t cbData = 0;
6925 uint32_t u32Width = 0;
6926 uint32_t u32Height = 0;
6927
6928 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6929
6930 if (RT_FAILURE(vrc))
6931 return setError(VBOX_E_IPRT_ERROR,
6932 tr("Saved screenshot data is not available (%Rrc)"),
6933 vrc);
6934
6935 *aWidth = u32Width;
6936 *aHeight = u32Height;
6937
6938 HRESULT rc = S_OK;
6939 uint8_t *pu8PNG = NULL;
6940 uint32_t cbPNG = 0;
6941 uint32_t cxPNG = 0;
6942 uint32_t cyPNG = 0;
6943
6944 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6945
6946 if (RT_SUCCESS(vrc))
6947 {
6948 com::SafeArray<BYTE> screenData(cbPNG);
6949 screenData.initFrom(pu8PNG, cbPNG);
6950 if (pu8PNG)
6951 RTMemFree(pu8PNG);
6952 screenData.detachTo(ComSafeArrayOutArg(aData));
6953 }
6954 else
6955 {
6956 if (pu8PNG)
6957 RTMemFree(pu8PNG);
6958 return setError(VBOX_E_IPRT_ERROR,
6959 tr("Could not convert screenshot to PNG (%Rrc)"),
6960 vrc);
6961 }
6962
6963 freeSavedDisplayScreenshot(pu8Data);
6964
6965 return rc;
6966}
6967
6968STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6969{
6970 LogFlowThisFunc(("\n"));
6971
6972 CheckComArgNotNull(aSize);
6973 CheckComArgNotNull(aWidth);
6974 CheckComArgNotNull(aHeight);
6975
6976 if (aScreenId != 0)
6977 return E_NOTIMPL;
6978
6979 AutoCaller autoCaller(this);
6980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6981
6982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6983
6984 uint8_t *pu8Data = NULL;
6985 uint32_t cbData = 0;
6986 uint32_t u32Width = 0;
6987 uint32_t u32Height = 0;
6988
6989 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6990
6991 if (RT_FAILURE(vrc))
6992 return setError(VBOX_E_IPRT_ERROR,
6993 tr("Saved screenshot data is not available (%Rrc)"),
6994 vrc);
6995
6996 *aSize = cbData;
6997 *aWidth = u32Width;
6998 *aHeight = u32Height;
6999
7000 freeSavedDisplayScreenshot(pu8Data);
7001
7002 return S_OK;
7003}
7004
7005STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
7006{
7007 LogFlowThisFunc(("\n"));
7008
7009 CheckComArgNotNull(aWidth);
7010 CheckComArgNotNull(aHeight);
7011 CheckComArgOutSafeArrayPointerValid(aData);
7012
7013 if (aScreenId != 0)
7014 return E_NOTIMPL;
7015
7016 AutoCaller autoCaller(this);
7017 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7018
7019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7020
7021 uint8_t *pu8Data = NULL;
7022 uint32_t cbData = 0;
7023 uint32_t u32Width = 0;
7024 uint32_t u32Height = 0;
7025
7026 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
7027
7028 if (RT_FAILURE(vrc))
7029 return setError(VBOX_E_IPRT_ERROR,
7030 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
7031 vrc);
7032
7033 *aWidth = u32Width;
7034 *aHeight = u32Height;
7035
7036 com::SafeArray<BYTE> png(cbData);
7037 png.initFrom(pu8Data, cbData);
7038 png.detachTo(ComSafeArrayOutArg(aData));
7039
7040 freeSavedDisplayScreenshot(pu8Data);
7041
7042 return S_OK;
7043}
7044
7045STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
7046{
7047 HRESULT rc = S_OK;
7048 LogFlowThisFunc(("\n"));
7049
7050 AutoCaller autoCaller(this);
7051 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7052
7053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7054
7055 if (!mHWData->mCPUHotPlugEnabled)
7056 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7057
7058 if (aCpu >= mHWData->mCPUCount)
7059 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
7060
7061 if (mHWData->mCPUAttached[aCpu])
7062 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
7063
7064 alock.release();
7065 rc = onCPUChange(aCpu, false);
7066 alock.acquire();
7067 if (FAILED(rc)) return rc;
7068
7069 setModified(IsModified_MachineData);
7070 mHWData.backup();
7071 mHWData->mCPUAttached[aCpu] = true;
7072
7073 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7074 if (Global::IsOnline(mData->mMachineState))
7075 saveSettings(NULL);
7076
7077 return S_OK;
7078}
7079
7080STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
7081{
7082 HRESULT rc = S_OK;
7083 LogFlowThisFunc(("\n"));
7084
7085 AutoCaller autoCaller(this);
7086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7087
7088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7089
7090 if (!mHWData->mCPUHotPlugEnabled)
7091 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7092
7093 if (aCpu >= SchemaDefs::MaxCPUCount)
7094 return setError(E_INVALIDARG,
7095 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7096 SchemaDefs::MaxCPUCount);
7097
7098 if (!mHWData->mCPUAttached[aCpu])
7099 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7100
7101 /* CPU 0 can't be detached */
7102 if (aCpu == 0)
7103 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7104
7105 alock.release();
7106 rc = onCPUChange(aCpu, true);
7107 alock.acquire();
7108 if (FAILED(rc)) return rc;
7109
7110 setModified(IsModified_MachineData);
7111 mHWData.backup();
7112 mHWData->mCPUAttached[aCpu] = false;
7113
7114 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7115 if (Global::IsOnline(mData->mMachineState))
7116 saveSettings(NULL);
7117
7118 return S_OK;
7119}
7120
7121STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7122{
7123 LogFlowThisFunc(("\n"));
7124
7125 CheckComArgNotNull(aCpuAttached);
7126
7127 *aCpuAttached = false;
7128
7129 AutoCaller autoCaller(this);
7130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7131
7132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7133
7134 /* If hotplug is enabled the CPU is always enabled. */
7135 if (!mHWData->mCPUHotPlugEnabled)
7136 {
7137 if (aCpu < mHWData->mCPUCount)
7138 *aCpuAttached = true;
7139 }
7140 else
7141 {
7142 if (aCpu < SchemaDefs::MaxCPUCount)
7143 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7144 }
7145
7146 return S_OK;
7147}
7148
7149STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7150{
7151 CheckComArgOutPointerValid(aName);
7152
7153 AutoCaller autoCaller(this);
7154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7155
7156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7157
7158 Utf8Str log = queryLogFilename(aIdx);
7159 if (!RTFileExists(log.c_str()))
7160 log.setNull();
7161 log.cloneTo(aName);
7162
7163 return S_OK;
7164}
7165
7166STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7167{
7168 LogFlowThisFunc(("\n"));
7169 CheckComArgOutSafeArrayPointerValid(aData);
7170 if (aSize < 0)
7171 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7172
7173 AutoCaller autoCaller(this);
7174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7175
7176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7177
7178 HRESULT rc = S_OK;
7179 Utf8Str log = queryLogFilename(aIdx);
7180
7181 /* do not unnecessarily hold the lock while doing something which does
7182 * not need the lock and potentially takes a long time. */
7183 alock.release();
7184
7185 /* Limit the chunk size to 32K for now, as that gives better performance
7186 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7187 * One byte expands to approx. 25 bytes of breathtaking XML. */
7188 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7189 com::SafeArray<BYTE> logData(cbData);
7190
7191 RTFILE LogFile;
7192 int vrc = RTFileOpen(&LogFile, log.c_str(),
7193 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7194 if (RT_SUCCESS(vrc))
7195 {
7196 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7197 if (RT_SUCCESS(vrc))
7198 logData.resize(cbData);
7199 else
7200 rc = setError(VBOX_E_IPRT_ERROR,
7201 tr("Could not read log file '%s' (%Rrc)"),
7202 log.c_str(), vrc);
7203 RTFileClose(LogFile);
7204 }
7205 else
7206 rc = setError(VBOX_E_IPRT_ERROR,
7207 tr("Could not open log file '%s' (%Rrc)"),
7208 log.c_str(), vrc);
7209
7210 if (FAILED(rc))
7211 logData.resize(0);
7212 logData.detachTo(ComSafeArrayOutArg(aData));
7213
7214 return rc;
7215}
7216
7217
7218/**
7219 * Currently this method doesn't attach device to the running VM,
7220 * just makes sure it's plugged on next VM start.
7221 */
7222STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7223{
7224 AutoCaller autoCaller(this);
7225 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7226
7227 // lock scope
7228 {
7229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7230
7231 HRESULT rc = checkStateDependency(MutableStateDep);
7232 if (FAILED(rc)) return rc;
7233
7234 ChipsetType_T aChipset = ChipsetType_PIIX3;
7235 COMGETTER(ChipsetType)(&aChipset);
7236
7237 if (aChipset != ChipsetType_ICH9)
7238 {
7239 return setError(E_INVALIDARG,
7240 tr("Host PCI attachment only supported with ICH9 chipset"));
7241 }
7242
7243 // check if device with this host PCI address already attached
7244 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7245 it != mHWData->mPCIDeviceAssignments.end();
7246 ++it)
7247 {
7248 LONG iHostAddress = -1;
7249 ComPtr<PCIDeviceAttachment> pAttach;
7250 pAttach = *it;
7251 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7252 if (iHostAddress == hostAddress)
7253 return setError(E_INVALIDARG,
7254 tr("Device with host PCI address already attached to this VM"));
7255 }
7256
7257 ComObjPtr<PCIDeviceAttachment> pda;
7258 char name[32];
7259
7260 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7261 Bstr bname(name);
7262 pda.createObject();
7263 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7264 setModified(IsModified_MachineData);
7265 mHWData.backup();
7266 mHWData->mPCIDeviceAssignments.push_back(pda);
7267 }
7268
7269 return S_OK;
7270}
7271
7272/**
7273 * Currently this method doesn't detach device from the running VM,
7274 * just makes sure it's not plugged on next VM start.
7275 */
7276STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7277{
7278 AutoCaller autoCaller(this);
7279 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7280
7281 ComObjPtr<PCIDeviceAttachment> pAttach;
7282 bool fRemoved = false;
7283 HRESULT rc;
7284
7285 // lock scope
7286 {
7287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7288
7289 rc = checkStateDependency(MutableStateDep);
7290 if (FAILED(rc)) return rc;
7291
7292 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7293 it != mHWData->mPCIDeviceAssignments.end();
7294 ++it)
7295 {
7296 LONG iHostAddress = -1;
7297 pAttach = *it;
7298 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7299 if (iHostAddress != -1 && iHostAddress == hostAddress)
7300 {
7301 setModified(IsModified_MachineData);
7302 mHWData.backup();
7303 mHWData->mPCIDeviceAssignments.remove(pAttach);
7304 fRemoved = true;
7305 break;
7306 }
7307 }
7308 }
7309
7310
7311 /* Fire event outside of the lock */
7312 if (fRemoved)
7313 {
7314 Assert(!pAttach.isNull());
7315 ComPtr<IEventSource> es;
7316 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7317 Assert(SUCCEEDED(rc));
7318 Bstr mid;
7319 rc = this->COMGETTER(Id)(mid.asOutParam());
7320 Assert(SUCCEEDED(rc));
7321 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7322 }
7323
7324 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7325 tr("No host PCI device %08x attached"),
7326 hostAddress
7327 );
7328}
7329
7330STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7331{
7332 CheckComArgOutSafeArrayPointerValid(aAssignments);
7333
7334 AutoCaller autoCaller(this);
7335 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7336
7337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7338
7339 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7340 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7341
7342 return S_OK;
7343}
7344
7345STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7346{
7347 CheckComArgOutPointerValid(aBandwidthControl);
7348
7349 AutoCaller autoCaller(this);
7350 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7351
7352 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7353
7354 return S_OK;
7355}
7356
7357STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7358{
7359 CheckComArgOutPointerValid(pfEnabled);
7360 AutoCaller autoCaller(this);
7361 HRESULT hrc = autoCaller.rc();
7362 if (SUCCEEDED(hrc))
7363 {
7364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7365 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7366 }
7367 return hrc;
7368}
7369
7370STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7371{
7372 AutoCaller autoCaller(this);
7373 HRESULT hrc = autoCaller.rc();
7374 if (SUCCEEDED(hrc))
7375 {
7376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7377 hrc = checkStateDependency(MutableStateDep);
7378 if (SUCCEEDED(hrc))
7379 {
7380 hrc = mHWData.backupEx();
7381 if (SUCCEEDED(hrc))
7382 {
7383 setModified(IsModified_MachineData);
7384 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7385 }
7386 }
7387 }
7388 return hrc;
7389}
7390
7391STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7392{
7393 CheckComArgOutPointerValid(pbstrConfig);
7394 AutoCaller autoCaller(this);
7395 HRESULT hrc = autoCaller.rc();
7396 if (SUCCEEDED(hrc))
7397 {
7398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7399 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7400 }
7401 return hrc;
7402}
7403
7404STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7405{
7406 CheckComArgStr(bstrConfig);
7407 AutoCaller autoCaller(this);
7408 HRESULT hrc = autoCaller.rc();
7409 if (SUCCEEDED(hrc))
7410 {
7411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7412 hrc = checkStateDependency(MutableStateDep);
7413 if (SUCCEEDED(hrc))
7414 {
7415 hrc = mHWData.backupEx();
7416 if (SUCCEEDED(hrc))
7417 {
7418 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7419 if (SUCCEEDED(hrc))
7420 setModified(IsModified_MachineData);
7421 }
7422 }
7423 }
7424 return hrc;
7425
7426}
7427
7428STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7429{
7430 CheckComArgOutPointerValid(pfAllow);
7431 AutoCaller autoCaller(this);
7432 HRESULT hrc = autoCaller.rc();
7433 if (SUCCEEDED(hrc))
7434 {
7435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7436 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7437 }
7438 return hrc;
7439}
7440
7441STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7442{
7443 AutoCaller autoCaller(this);
7444 HRESULT hrc = autoCaller.rc();
7445 if (SUCCEEDED(hrc))
7446 {
7447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7448 hrc = checkStateDependency(MutableStateDep);
7449 if (SUCCEEDED(hrc))
7450 {
7451 hrc = mHWData.backupEx();
7452 if (SUCCEEDED(hrc))
7453 {
7454 setModified(IsModified_MachineData);
7455 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7456 }
7457 }
7458 }
7459 return hrc;
7460}
7461
7462STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7463{
7464 CheckComArgOutPointerValid(pfEnabled);
7465 AutoCaller autoCaller(this);
7466 HRESULT hrc = autoCaller.rc();
7467 if (SUCCEEDED(hrc))
7468 {
7469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7470 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7471 }
7472 return hrc;
7473}
7474
7475STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7476{
7477 AutoCaller autoCaller(this);
7478 HRESULT hrc = autoCaller.rc();
7479 if (SUCCEEDED(hrc))
7480 {
7481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7482 hrc = checkStateDependency(MutableStateDep);
7483 if ( SUCCEEDED(hrc)
7484 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7485 {
7486 AutostartDb *autostartDb = mParent->getAutostartDb();
7487 int vrc;
7488
7489 if (fEnabled)
7490 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7491 else
7492 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7493
7494 if (RT_SUCCESS(vrc))
7495 {
7496 hrc = mHWData.backupEx();
7497 if (SUCCEEDED(hrc))
7498 {
7499 setModified(IsModified_MachineData);
7500 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7501 }
7502 }
7503 else if (vrc == VERR_NOT_SUPPORTED)
7504 hrc = setError(VBOX_E_NOT_SUPPORTED,
7505 tr("The VM autostart feature is not supported on this platform"));
7506 else if (vrc == VERR_PATH_NOT_FOUND)
7507 hrc = setError(E_FAIL,
7508 tr("The path to the autostart database is not set"));
7509 else
7510 hrc = setError(E_UNEXPECTED,
7511 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7512 fEnabled ? "Adding" : "Removing",
7513 mUserData->s.strName.c_str(), vrc);
7514 }
7515 }
7516 return hrc;
7517}
7518
7519STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7520{
7521 CheckComArgOutPointerValid(puDelay);
7522 AutoCaller autoCaller(this);
7523 HRESULT hrc = autoCaller.rc();
7524 if (SUCCEEDED(hrc))
7525 {
7526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7527 *puDelay = mHWData->mAutostart.uAutostartDelay;
7528 }
7529 return hrc;
7530}
7531
7532STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7533{
7534 AutoCaller autoCaller(this);
7535 HRESULT hrc = autoCaller.rc();
7536 if (SUCCEEDED(hrc))
7537 {
7538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7539 hrc = checkStateDependency(MutableStateDep);
7540 if (SUCCEEDED(hrc))
7541 {
7542 hrc = mHWData.backupEx();
7543 if (SUCCEEDED(hrc))
7544 {
7545 setModified(IsModified_MachineData);
7546 mHWData->mAutostart.uAutostartDelay = uDelay;
7547 }
7548 }
7549 }
7550 return hrc;
7551}
7552
7553STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7554{
7555 CheckComArgOutPointerValid(penmAutostopType);
7556 AutoCaller autoCaller(this);
7557 HRESULT hrc = autoCaller.rc();
7558 if (SUCCEEDED(hrc))
7559 {
7560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7561 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7562 }
7563 return hrc;
7564}
7565
7566STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7567{
7568 AutoCaller autoCaller(this);
7569 HRESULT hrc = autoCaller.rc();
7570 if (SUCCEEDED(hrc))
7571 {
7572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7573 hrc = checkStateDependency(MutableStateDep);
7574 if ( SUCCEEDED(hrc)
7575 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7576 {
7577 AutostartDb *autostartDb = mParent->getAutostartDb();
7578 int vrc;
7579
7580 if (enmAutostopType != AutostopType_Disabled)
7581 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7582 else
7583 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7584
7585 if (RT_SUCCESS(vrc))
7586 {
7587 hrc = mHWData.backupEx();
7588 if (SUCCEEDED(hrc))
7589 {
7590 setModified(IsModified_MachineData);
7591 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7592 }
7593 }
7594 else if (vrc == VERR_NOT_SUPPORTED)
7595 hrc = setError(VBOX_E_NOT_SUPPORTED,
7596 tr("The VM autostop feature is not supported on this platform"));
7597 else if (vrc == VERR_PATH_NOT_FOUND)
7598 hrc = setError(E_FAIL,
7599 tr("The path to the autostart database is not set"));
7600 else
7601 hrc = setError(E_UNEXPECTED,
7602 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7603 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7604 mUserData->s.strName.c_str(), vrc);
7605 }
7606 }
7607 return hrc;
7608}
7609
7610STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7611{
7612 CheckComArgOutPointerValid(aDefaultFrontend);
7613 AutoCaller autoCaller(this);
7614 HRESULT hrc = autoCaller.rc();
7615 if (SUCCEEDED(hrc))
7616 {
7617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7618 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7619 }
7620 return hrc;
7621}
7622
7623STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7624{
7625 CheckComArgStr(aDefaultFrontend);
7626 AutoCaller autoCaller(this);
7627 HRESULT hrc = autoCaller.rc();
7628 if (SUCCEEDED(hrc))
7629 {
7630 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7631 hrc = checkStateDependency(MutableOrSavedStateDep);
7632 if (SUCCEEDED(hrc))
7633 {
7634 hrc = mHWData.backupEx();
7635 if (SUCCEEDED(hrc))
7636 {
7637 setModified(IsModified_MachineData);
7638 mHWData->mDefaultFrontend = aDefaultFrontend;
7639 }
7640 }
7641 }
7642 return hrc;
7643}
7644
7645STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7646{
7647 CheckComArgSafeArrayNotNull(aIcon);
7648 CheckComArgOutSafeArrayPointerValid(aIcon);
7649 AutoCaller autoCaller(this);
7650 HRESULT hrc = autoCaller.rc();
7651 if (SUCCEEDED(hrc))
7652 {
7653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7654 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7655 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7656 icon.detachTo(ComSafeArrayOutArg(aIcon));
7657 }
7658 return hrc;
7659}
7660
7661STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7662{
7663 CheckComArgSafeArrayNotNull(aIcon);
7664 AutoCaller autoCaller(this);
7665 HRESULT hrc = autoCaller.rc();
7666 if (SUCCEEDED(hrc))
7667 {
7668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7669 hrc = checkStateDependency(MutableOrSavedStateDep);
7670 if (SUCCEEDED(hrc))
7671 {
7672 setModified(IsModified_MachineData);
7673 mUserData.backup();
7674 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7675 mUserData->mIcon.resize(icon.size());
7676 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7677 }
7678 }
7679 return hrc;
7680}
7681
7682STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7683{
7684 CheckComArgOutPointerValid(aAvailable);
7685
7686 AutoCaller autoCaller(this);
7687 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7688
7689#ifdef VBOX_WITH_USB
7690 *aAvailable = true;
7691#else
7692 *aAvailable = false;
7693#endif
7694 return S_OK;
7695}
7696
7697STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7698{
7699 LogFlowFuncEnter();
7700
7701 CheckComArgNotNull(pTarget);
7702 CheckComArgOutPointerValid(pProgress);
7703
7704 /* Convert the options. */
7705 RTCList<CloneOptions_T> optList;
7706 if (options != NULL)
7707 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7708
7709 if (optList.contains(CloneOptions_Link))
7710 {
7711 if (!isSnapshotMachine())
7712 return setError(E_INVALIDARG,
7713 tr("Linked clone can only be created from a snapshot"));
7714 if (mode != CloneMode_MachineState)
7715 return setError(E_INVALIDARG,
7716 tr("Linked clone can only be created for a single machine state"));
7717 }
7718 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7719
7720 AutoCaller autoCaller(this);
7721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7722
7723
7724 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7725
7726 HRESULT rc = pWorker->start(pProgress);
7727
7728 LogFlowFuncLeave();
7729
7730 return rc;
7731}
7732
7733// public methods for internal purposes
7734/////////////////////////////////////////////////////////////////////////////
7735
7736/**
7737 * Adds the given IsModified_* flag to the dirty flags of the machine.
7738 * This must be called either during loadSettings or under the machine write lock.
7739 * @param fl
7740 */
7741void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7742{
7743 mData->flModifications |= fl;
7744 if (fAllowStateModification && isStateModificationAllowed())
7745 mData->mCurrentStateModified = true;
7746}
7747
7748/**
7749 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7750 * care of the write locking.
7751 *
7752 * @param fModifications The flag to add.
7753 */
7754void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7755{
7756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7757 setModified(fModification, fAllowStateModification);
7758}
7759
7760/**
7761 * Saves the registry entry of this machine to the given configuration node.
7762 *
7763 * @param aEntryNode Node to save the registry entry to.
7764 *
7765 * @note locks this object for reading.
7766 */
7767HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7768{
7769 AutoLimitedCaller autoCaller(this);
7770 AssertComRCReturnRC(autoCaller.rc());
7771
7772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7773
7774 data.uuid = mData->mUuid;
7775 data.strSettingsFile = mData->m_strConfigFile;
7776
7777 return S_OK;
7778}
7779
7780/**
7781 * Calculates the absolute path of the given path taking the directory of the
7782 * machine settings file as the current directory.
7783 *
7784 * @param aPath Path to calculate the absolute path for.
7785 * @param aResult Where to put the result (used only on success, can be the
7786 * same Utf8Str instance as passed in @a aPath).
7787 * @return IPRT result.
7788 *
7789 * @note Locks this object for reading.
7790 */
7791int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7792{
7793 AutoCaller autoCaller(this);
7794 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7795
7796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7797
7798 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7799
7800 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7801
7802 strSettingsDir.stripFilename();
7803 char folder[RTPATH_MAX];
7804 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7805 if (RT_SUCCESS(vrc))
7806 aResult = folder;
7807
7808 return vrc;
7809}
7810
7811/**
7812 * Copies strSource to strTarget, making it relative to the machine folder
7813 * if it is a subdirectory thereof, or simply copying it otherwise.
7814 *
7815 * @param strSource Path to evaluate and copy.
7816 * @param strTarget Buffer to receive target path.
7817 *
7818 * @note Locks this object for reading.
7819 */
7820void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7821 Utf8Str &strTarget)
7822{
7823 AutoCaller autoCaller(this);
7824 AssertComRCReturn(autoCaller.rc(), (void)0);
7825
7826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7827
7828 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7829 // use strTarget as a temporary buffer to hold the machine settings dir
7830 strTarget = mData->m_strConfigFileFull;
7831 strTarget.stripFilename();
7832 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7833 {
7834 // is relative: then append what's left
7835 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7836 // for empty paths (only possible for subdirs) use "." to avoid
7837 // triggering default settings for not present config attributes.
7838 if (strTarget.isEmpty())
7839 strTarget = ".";
7840 }
7841 else
7842 // is not relative: then overwrite
7843 strTarget = strSource;
7844}
7845
7846/**
7847 * Returns the full path to the machine's log folder in the
7848 * \a aLogFolder argument.
7849 */
7850void Machine::getLogFolder(Utf8Str &aLogFolder)
7851{
7852 AutoCaller autoCaller(this);
7853 AssertComRCReturnVoid(autoCaller.rc());
7854
7855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7856
7857 char szTmp[RTPATH_MAX];
7858 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7859 if (RT_SUCCESS(vrc))
7860 {
7861 if (szTmp[0] && !mUserData.isNull())
7862 {
7863 char szTmp2[RTPATH_MAX];
7864 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7865 if (RT_SUCCESS(vrc))
7866 aLogFolder = BstrFmt("%s%c%s",
7867 szTmp2,
7868 RTPATH_DELIMITER,
7869 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7870 }
7871 else
7872 vrc = VERR_PATH_IS_RELATIVE;
7873 }
7874
7875 if (RT_FAILURE(vrc))
7876 {
7877 // fallback if VBOX_USER_LOGHOME is not set or invalid
7878 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7879 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7880 aLogFolder.append(RTPATH_DELIMITER);
7881 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7882 }
7883}
7884
7885/**
7886 * Returns the full path to the machine's log file for an given index.
7887 */
7888Utf8Str Machine::queryLogFilename(ULONG idx)
7889{
7890 Utf8Str logFolder;
7891 getLogFolder(logFolder);
7892 Assert(logFolder.length());
7893 Utf8Str log;
7894 if (idx == 0)
7895 log = Utf8StrFmt("%s%cVBox.log",
7896 logFolder.c_str(), RTPATH_DELIMITER);
7897 else
7898 log = Utf8StrFmt("%s%cVBox.log.%d",
7899 logFolder.c_str(), RTPATH_DELIMITER, idx);
7900 return log;
7901}
7902
7903/**
7904 * Composes a unique saved state filename based on the current system time. The filename is
7905 * granular to the second so this will work so long as no more than one snapshot is taken on
7906 * a machine per second.
7907 *
7908 * Before version 4.1, we used this formula for saved state files:
7909 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7910 * which no longer works because saved state files can now be shared between the saved state of the
7911 * "saved" machine and an online snapshot, and the following would cause problems:
7912 * 1) save machine
7913 * 2) create online snapshot from that machine state --> reusing saved state file
7914 * 3) save machine again --> filename would be reused, breaking the online snapshot
7915 *
7916 * So instead we now use a timestamp.
7917 *
7918 * @param str
7919 */
7920void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7921{
7922 AutoCaller autoCaller(this);
7923 AssertComRCReturnVoid(autoCaller.rc());
7924
7925 {
7926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7927 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7928 }
7929
7930 RTTIMESPEC ts;
7931 RTTimeNow(&ts);
7932 RTTIME time;
7933 RTTimeExplode(&time, &ts);
7934
7935 strStateFilePath += RTPATH_DELIMITER;
7936 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7937 time.i32Year, time.u8Month, time.u8MonthDay,
7938 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7939}
7940
7941/**
7942 * Returns the full path to the default video capture file.
7943 */
7944void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7945{
7946 AutoCaller autoCaller(this);
7947 AssertComRCReturnVoid(autoCaller.rc());
7948
7949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7950
7951 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7952 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7953 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7954}
7955
7956/**
7957 * Returns whether at least one USB controller is present for the VM.
7958 */
7959bool Machine::isUSBControllerPresent()
7960{
7961 AutoCaller autoCaller(this);
7962 AssertComRCReturn(autoCaller.rc(), false);
7963
7964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7965
7966 return (mUSBControllers->size() > 0);
7967}
7968
7969/**
7970 * @note Locks this object for writing, calls the client process
7971 * (inside the lock).
7972 */
7973HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7974 const Utf8Str &strFrontend,
7975 const Utf8Str &strEnvironment,
7976 ProgressProxy *aProgress)
7977{
7978 LogFlowThisFuncEnter();
7979
7980 AssertReturn(aControl, E_FAIL);
7981 AssertReturn(aProgress, E_FAIL);
7982 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7983
7984 AutoCaller autoCaller(this);
7985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7986
7987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7988
7989 if (!mData->mRegistered)
7990 return setError(E_UNEXPECTED,
7991 tr("The machine '%s' is not registered"),
7992 mUserData->s.strName.c_str());
7993
7994 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7995
7996 if ( mData->mSession.mState == SessionState_Locked
7997 || mData->mSession.mState == SessionState_Spawning
7998 || mData->mSession.mState == SessionState_Unlocking)
7999 return setError(VBOX_E_INVALID_OBJECT_STATE,
8000 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
8001 mUserData->s.strName.c_str());
8002
8003 /* may not be busy */
8004 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
8005
8006 /* get the path to the executable */
8007 char szPath[RTPATH_MAX];
8008 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
8009 size_t sz = strlen(szPath);
8010 szPath[sz++] = RTPATH_DELIMITER;
8011 szPath[sz] = 0;
8012 char *cmd = szPath + sz;
8013 sz = sizeof(szPath) - sz;
8014
8015 int vrc = VINF_SUCCESS;
8016 RTPROCESS pid = NIL_RTPROCESS;
8017
8018 RTENV env = RTENV_DEFAULT;
8019
8020 if (!strEnvironment.isEmpty())
8021 {
8022 char *newEnvStr = NULL;
8023
8024 do
8025 {
8026 /* clone the current environment */
8027 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
8028 AssertRCBreakStmt(vrc2, vrc = vrc2);
8029
8030 newEnvStr = RTStrDup(strEnvironment.c_str());
8031 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
8032
8033 /* put new variables to the environment
8034 * (ignore empty variable names here since RTEnv API
8035 * intentionally doesn't do that) */
8036 char *var = newEnvStr;
8037 for (char *p = newEnvStr; *p; ++p)
8038 {
8039 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
8040 {
8041 *p = '\0';
8042 if (*var)
8043 {
8044 char *val = strchr(var, '=');
8045 if (val)
8046 {
8047 *val++ = '\0';
8048 vrc2 = RTEnvSetEx(env, var, val);
8049 }
8050 else
8051 vrc2 = RTEnvUnsetEx(env, var);
8052 if (RT_FAILURE(vrc2))
8053 break;
8054 }
8055 var = p + 1;
8056 }
8057 }
8058 if (RT_SUCCESS(vrc2) && *var)
8059 vrc2 = RTEnvPutEx(env, var);
8060
8061 AssertRCBreakStmt(vrc2, vrc = vrc2);
8062 }
8063 while (0);
8064
8065 if (newEnvStr != NULL)
8066 RTStrFree(newEnvStr);
8067 }
8068
8069#ifdef VBOX_WITH_QTGUI
8070 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
8071 {
8072# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
8073 /* Modify the base path so that we don't need to use ".." below. */
8074 RTPathStripTrailingSlash(szPath);
8075 RTPathStripFilename(szPath);
8076 sz = strlen(szPath);
8077 cmd = szPath + sz;
8078 sz = sizeof(szPath) - sz;
8079
8080#define OSX_APP_NAME "VirtualBoxVM"
8081#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
8082
8083 Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
8084 if ( strAppOverride.contains(".")
8085 || strAppOverride.contains("/")
8086 || strAppOverride.contains("\\")
8087 || strAppOverride.contains(":"))
8088 strAppOverride.setNull();
8089 Utf8Str strAppPath;
8090 if (!strAppOverride.isEmpty())
8091 {
8092 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
8093 Utf8Str strFullPath(szPath);
8094 strFullPath.append(strAppPath);
8095 /* there is a race, but people using this deserve the failure */
8096 if (!RTFileExists(strFullPath.c_str()))
8097 strAppOverride.setNull();
8098 }
8099 if (strAppOverride.isEmpty())
8100 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
8101 const char *VirtualBox_exe = strAppPath.c_str();
8102 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
8103# else
8104 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8105 Assert(sz >= sizeof(VirtualBox_exe));
8106# endif
8107 strcpy(cmd, VirtualBox_exe);
8108
8109 Utf8Str idStr = mData->mUuid.toString();
8110 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8111 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8112 }
8113#else /* !VBOX_WITH_QTGUI */
8114 if (0)
8115 ;
8116#endif /* VBOX_WITH_QTGUI */
8117
8118 else
8119
8120#ifdef VBOX_WITH_VBOXSDL
8121 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8122 {
8123 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8124 Assert(sz >= sizeof(VBoxSDL_exe));
8125 strcpy(cmd, VBoxSDL_exe);
8126
8127 Utf8Str idStr = mData->mUuid.toString();
8128 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8129 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8130 }
8131#else /* !VBOX_WITH_VBOXSDL */
8132 if (0)
8133 ;
8134#endif /* !VBOX_WITH_VBOXSDL */
8135
8136 else
8137
8138#ifdef VBOX_WITH_HEADLESS
8139 if ( strFrontend == "headless"
8140 || strFrontend == "capture"
8141 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8142 )
8143 {
8144 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8145 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8146 * and a VM works even if the server has not been installed.
8147 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8148 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8149 * differently in 4.0 and 3.x.
8150 */
8151 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8152 Assert(sz >= sizeof(VBoxHeadless_exe));
8153 strcpy(cmd, VBoxHeadless_exe);
8154
8155 Utf8Str idStr = mData->mUuid.toString();
8156 /* Leave space for "--capture" arg. */
8157 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8158 "--startvm", idStr.c_str(),
8159 "--vrde", "config",
8160 0, /* For "--capture". */
8161 0 };
8162 if (strFrontend == "capture")
8163 {
8164 unsigned pos = RT_ELEMENTS(args) - 2;
8165 args[pos] = "--capture";
8166 }
8167 vrc = RTProcCreate(szPath, args, env,
8168#ifdef RT_OS_WINDOWS
8169 RTPROC_FLAGS_NO_WINDOW
8170#else
8171 0
8172#endif
8173 , &pid);
8174 }
8175#else /* !VBOX_WITH_HEADLESS */
8176 if (0)
8177 ;
8178#endif /* !VBOX_WITH_HEADLESS */
8179 else
8180 {
8181 RTEnvDestroy(env);
8182 return setError(E_INVALIDARG,
8183 tr("Invalid frontend name: '%s'"),
8184 strFrontend.c_str());
8185 }
8186
8187 RTEnvDestroy(env);
8188
8189 if (RT_FAILURE(vrc))
8190 return setError(VBOX_E_IPRT_ERROR,
8191 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8192 mUserData->s.strName.c_str(), vrc);
8193
8194 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8195
8196 /*
8197 * Note that we don't release the lock here before calling the client,
8198 * because it doesn't need to call us back if called with a NULL argument.
8199 * Releasing the lock here is dangerous because we didn't prepare the
8200 * launch data yet, but the client we've just started may happen to be
8201 * too fast and call LockMachine() that will fail (because of PID, etc.),
8202 * so that the Machine will never get out of the Spawning session state.
8203 */
8204
8205 /* inform the session that it will be a remote one */
8206 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8207#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8208 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8209#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8210 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8211#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8212 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8213
8214 if (FAILED(rc))
8215 {
8216 /* restore the session state */
8217 mData->mSession.mState = SessionState_Unlocked;
8218 alock.release();
8219 mParent->addProcessToReap(pid);
8220 /* The failure may occur w/o any error info (from RPC), so provide one */
8221 return setError(VBOX_E_VM_ERROR,
8222 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8223 }
8224
8225 /* attach launch data to the machine */
8226 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8227 mData->mSession.mRemoteControls.push_back(aControl);
8228 mData->mSession.mProgress = aProgress;
8229 mData->mSession.mPID = pid;
8230 mData->mSession.mState = SessionState_Spawning;
8231 mData->mSession.mType = strFrontend;
8232
8233 alock.release();
8234 mParent->addProcessToReap(pid);
8235
8236 LogFlowThisFuncLeave();
8237 return S_OK;
8238}
8239
8240/**
8241 * Returns @c true if the given session machine instance has an open direct
8242 * session (and optionally also for direct sessions which are closing) and
8243 * returns the session control machine instance if so.
8244 *
8245 * Note that when the method returns @c false, the arguments remain unchanged.
8246 *
8247 * @param aMachine Session machine object.
8248 * @param aControl Direct session control object (optional).
8249 * @param aAllowClosing If true then additionally a session which is currently
8250 * being closed will also be allowed.
8251 *
8252 * @note locks this object for reading.
8253 */
8254bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8255 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8256 bool aAllowClosing /*= false*/)
8257{
8258 AutoLimitedCaller autoCaller(this);
8259 AssertComRCReturn(autoCaller.rc(), false);
8260
8261 /* just return false for inaccessible machines */
8262 if (autoCaller.state() != Ready)
8263 return false;
8264
8265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8266
8267 if ( mData->mSession.mState == SessionState_Locked
8268 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8269 )
8270 {
8271 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8272
8273 aMachine = mData->mSession.mMachine;
8274
8275 if (aControl != NULL)
8276 *aControl = mData->mSession.mDirectControl;
8277
8278 return true;
8279 }
8280
8281 return false;
8282}
8283
8284/**
8285 * Returns @c true if the given machine has an spawning direct session.
8286 *
8287 * @note locks this object for reading.
8288 */
8289bool Machine::isSessionSpawning()
8290{
8291 AutoLimitedCaller autoCaller(this);
8292 AssertComRCReturn(autoCaller.rc(), false);
8293
8294 /* just return false for inaccessible machines */
8295 if (autoCaller.state() != Ready)
8296 return false;
8297
8298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8299
8300 if (mData->mSession.mState == SessionState_Spawning)
8301 return true;
8302
8303 return false;
8304}
8305
8306/**
8307 * Called from the client watcher thread to check for unexpected client process
8308 * death during Session_Spawning state (e.g. before it successfully opened a
8309 * direct session).
8310 *
8311 * On Win32 and on OS/2, this method is called only when we've got the
8312 * direct client's process termination notification, so it always returns @c
8313 * true.
8314 *
8315 * On other platforms, this method returns @c true if the client process is
8316 * terminated and @c false if it's still alive.
8317 *
8318 * @note Locks this object for writing.
8319 */
8320bool Machine::checkForSpawnFailure()
8321{
8322 AutoCaller autoCaller(this);
8323 if (!autoCaller.isOk())
8324 {
8325 /* nothing to do */
8326 LogFlowThisFunc(("Already uninitialized!\n"));
8327 return true;
8328 }
8329
8330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8331
8332 if (mData->mSession.mState != SessionState_Spawning)
8333 {
8334 /* nothing to do */
8335 LogFlowThisFunc(("Not spawning any more!\n"));
8336 return true;
8337 }
8338
8339 HRESULT rc = S_OK;
8340
8341 /* PID not yet initialized, skip check. */
8342 if (mData->mSession.mPID == NIL_RTPROCESS)
8343 return false;
8344
8345 RTPROCSTATUS status;
8346 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8347
8348 if (vrc != VERR_PROCESS_RUNNING)
8349 {
8350 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8351 rc = setError(E_FAIL,
8352 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8353 getName().c_str(), status.iStatus);
8354 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8355 rc = setError(E_FAIL,
8356 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8357 getName().c_str(), status.iStatus);
8358 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8359 rc = setError(E_FAIL,
8360 tr("The virtual machine '%s' has terminated abnormally"),
8361 getName().c_str(), status.iStatus);
8362 else
8363 rc = setError(E_FAIL,
8364 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8365 getName().c_str(), vrc);
8366 }
8367
8368 if (FAILED(rc))
8369 {
8370 /* Close the remote session, remove the remote control from the list
8371 * and reset session state to Closed (@note keep the code in sync with
8372 * the relevant part in LockMachine()). */
8373
8374 Assert(mData->mSession.mRemoteControls.size() == 1);
8375 if (mData->mSession.mRemoteControls.size() == 1)
8376 {
8377 ErrorInfoKeeper eik;
8378 mData->mSession.mRemoteControls.front()->Uninitialize();
8379 }
8380
8381 mData->mSession.mRemoteControls.clear();
8382 mData->mSession.mState = SessionState_Unlocked;
8383
8384 /* finalize the progress after setting the state */
8385 if (!mData->mSession.mProgress.isNull())
8386 {
8387 mData->mSession.mProgress->notifyComplete(rc);
8388 mData->mSession.mProgress.setNull();
8389 }
8390
8391 mData->mSession.mPID = NIL_RTPROCESS;
8392
8393 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8394 return true;
8395 }
8396
8397 return false;
8398}
8399
8400/**
8401 * Checks whether the machine can be registered. If so, commits and saves
8402 * all settings.
8403 *
8404 * @note Must be called from mParent's write lock. Locks this object and
8405 * children for writing.
8406 */
8407HRESULT Machine::prepareRegister()
8408{
8409 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8410
8411 AutoLimitedCaller autoCaller(this);
8412 AssertComRCReturnRC(autoCaller.rc());
8413
8414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8415
8416 /* wait for state dependents to drop to zero */
8417 ensureNoStateDependencies();
8418
8419 if (!mData->mAccessible)
8420 return setError(VBOX_E_INVALID_OBJECT_STATE,
8421 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8422 mUserData->s.strName.c_str(),
8423 mData->mUuid.toString().c_str());
8424
8425 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8426
8427 if (mData->mRegistered)
8428 return setError(VBOX_E_INVALID_OBJECT_STATE,
8429 tr("The machine '%s' with UUID {%s} is already registered"),
8430 mUserData->s.strName.c_str(),
8431 mData->mUuid.toString().c_str());
8432
8433 HRESULT rc = S_OK;
8434
8435 // Ensure the settings are saved. If we are going to be registered and
8436 // no config file exists yet, create it by calling saveSettings() too.
8437 if ( (mData->flModifications)
8438 || (!mData->pMachineConfigFile->fileExists())
8439 )
8440 {
8441 rc = saveSettings(NULL);
8442 // no need to check whether VirtualBox.xml needs saving too since
8443 // we can't have a machine XML file rename pending
8444 if (FAILED(rc)) return rc;
8445 }
8446
8447 /* more config checking goes here */
8448
8449 if (SUCCEEDED(rc))
8450 {
8451 /* we may have had implicit modifications we want to fix on success */
8452 commit();
8453
8454 mData->mRegistered = true;
8455 }
8456 else
8457 {
8458 /* we may have had implicit modifications we want to cancel on failure*/
8459 rollback(false /* aNotify */);
8460 }
8461
8462 return rc;
8463}
8464
8465/**
8466 * Increases the number of objects dependent on the machine state or on the
8467 * registered state. Guarantees that these two states will not change at least
8468 * until #releaseStateDependency() is called.
8469 *
8470 * Depending on the @a aDepType value, additional state checks may be made.
8471 * These checks will set extended error info on failure. See
8472 * #checkStateDependency() for more info.
8473 *
8474 * If this method returns a failure, the dependency is not added and the caller
8475 * is not allowed to rely on any particular machine state or registration state
8476 * value and may return the failed result code to the upper level.
8477 *
8478 * @param aDepType Dependency type to add.
8479 * @param aState Current machine state (NULL if not interested).
8480 * @param aRegistered Current registered state (NULL if not interested).
8481 *
8482 * @note Locks this object for writing.
8483 */
8484HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8485 MachineState_T *aState /* = NULL */,
8486 BOOL *aRegistered /* = NULL */)
8487{
8488 AutoCaller autoCaller(this);
8489 AssertComRCReturnRC(autoCaller.rc());
8490
8491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8492
8493 HRESULT rc = checkStateDependency(aDepType);
8494 if (FAILED(rc)) return rc;
8495
8496 {
8497 if (mData->mMachineStateChangePending != 0)
8498 {
8499 /* ensureNoStateDependencies() is waiting for state dependencies to
8500 * drop to zero so don't add more. It may make sense to wait a bit
8501 * and retry before reporting an error (since the pending state
8502 * transition should be really quick) but let's just assert for
8503 * now to see if it ever happens on practice. */
8504
8505 AssertFailed();
8506
8507 return setError(E_ACCESSDENIED,
8508 tr("Machine state change is in progress. Please retry the operation later."));
8509 }
8510
8511 ++mData->mMachineStateDeps;
8512 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8513 }
8514
8515 if (aState)
8516 *aState = mData->mMachineState;
8517 if (aRegistered)
8518 *aRegistered = mData->mRegistered;
8519
8520 return S_OK;
8521}
8522
8523/**
8524 * Decreases the number of objects dependent on the machine state.
8525 * Must always complete the #addStateDependency() call after the state
8526 * dependency is no more necessary.
8527 */
8528void Machine::releaseStateDependency()
8529{
8530 AutoCaller autoCaller(this);
8531 AssertComRCReturnVoid(autoCaller.rc());
8532
8533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8534
8535 /* releaseStateDependency() w/o addStateDependency()? */
8536 AssertReturnVoid(mData->mMachineStateDeps != 0);
8537 -- mData->mMachineStateDeps;
8538
8539 if (mData->mMachineStateDeps == 0)
8540 {
8541 /* inform ensureNoStateDependencies() that there are no more deps */
8542 if (mData->mMachineStateChangePending != 0)
8543 {
8544 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8545 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8546 }
8547 }
8548}
8549
8550Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8551{
8552 /* start with nothing found */
8553 Utf8Str strResult("");
8554
8555 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8556
8557 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8558 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8559 // found:
8560 strResult = it->second; // source is a Utf8Str
8561
8562 return strResult;
8563}
8564
8565// protected methods
8566/////////////////////////////////////////////////////////////////////////////
8567
8568/**
8569 * Performs machine state checks based on the @a aDepType value. If a check
8570 * fails, this method will set extended error info, otherwise it will return
8571 * S_OK. It is supposed, that on failure, the caller will immediately return
8572 * the return value of this method to the upper level.
8573 *
8574 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8575 *
8576 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8577 * current state of this machine object allows to change settings of the
8578 * machine (i.e. the machine is not registered, or registered but not running
8579 * and not saved). It is useful to call this method from Machine setters
8580 * before performing any change.
8581 *
8582 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8583 * as for MutableStateDep except that if the machine is saved, S_OK is also
8584 * returned. This is useful in setters which allow changing machine
8585 * properties when it is in the saved state.
8586 *
8587 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8588 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8589 * Aborted).
8590 *
8591 * @param aDepType Dependency type to check.
8592 *
8593 * @note Non Machine based classes should use #addStateDependency() and
8594 * #releaseStateDependency() methods or the smart AutoStateDependency
8595 * template.
8596 *
8597 * @note This method must be called from under this object's read or write
8598 * lock.
8599 */
8600HRESULT Machine::checkStateDependency(StateDependency aDepType)
8601{
8602 switch (aDepType)
8603 {
8604 case AnyStateDep:
8605 {
8606 break;
8607 }
8608 case MutableStateDep:
8609 {
8610 if ( mData->mRegistered
8611 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8612 || ( mData->mMachineState != MachineState_Paused
8613 && mData->mMachineState != MachineState_Running
8614 && mData->mMachineState != MachineState_Aborted
8615 && mData->mMachineState != MachineState_Teleported
8616 && mData->mMachineState != MachineState_PoweredOff
8617 )
8618 )
8619 )
8620 return setError(VBOX_E_INVALID_VM_STATE,
8621 tr("The machine is not mutable (state is %s)"),
8622 Global::stringifyMachineState(mData->mMachineState));
8623 break;
8624 }
8625 case MutableOrSavedStateDep:
8626 {
8627 if ( mData->mRegistered
8628 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8629 || ( mData->mMachineState != MachineState_Paused
8630 && mData->mMachineState != MachineState_Running
8631 && mData->mMachineState != MachineState_Aborted
8632 && mData->mMachineState != MachineState_Teleported
8633 && mData->mMachineState != MachineState_Saved
8634 && mData->mMachineState != MachineState_PoweredOff
8635 )
8636 )
8637 )
8638 return setError(VBOX_E_INVALID_VM_STATE,
8639 tr("The machine is not mutable (state is %s)"),
8640 Global::stringifyMachineState(mData->mMachineState));
8641 break;
8642 }
8643 case OfflineStateDep:
8644 {
8645 if ( mData->mRegistered
8646 && ( !isSessionMachine()
8647 || ( mData->mMachineState != MachineState_PoweredOff
8648 && mData->mMachineState != MachineState_Saved
8649 && mData->mMachineState != MachineState_Aborted
8650 && mData->mMachineState != MachineState_Teleported
8651 )
8652 )
8653 )
8654 return setError(VBOX_E_INVALID_VM_STATE,
8655 tr("The machine is not offline (state is %s)"),
8656 Global::stringifyMachineState(mData->mMachineState));
8657 break;
8658 }
8659 }
8660
8661 return S_OK;
8662}
8663
8664/**
8665 * Helper to initialize all associated child objects and allocate data
8666 * structures.
8667 *
8668 * This method must be called as a part of the object's initialization procedure
8669 * (usually done in the #init() method).
8670 *
8671 * @note Must be called only from #init() or from #registeredInit().
8672 */
8673HRESULT Machine::initDataAndChildObjects()
8674{
8675 AutoCaller autoCaller(this);
8676 AssertComRCReturnRC(autoCaller.rc());
8677 AssertComRCReturn(autoCaller.state() == InInit ||
8678 autoCaller.state() == Limited, E_FAIL);
8679
8680 AssertReturn(!mData->mAccessible, E_FAIL);
8681
8682 /* allocate data structures */
8683 mSSData.allocate();
8684 mUserData.allocate();
8685 mHWData.allocate();
8686 mMediaData.allocate();
8687 mStorageControllers.allocate();
8688 mUSBControllers.allocate();
8689
8690 /* initialize mOSTypeId */
8691 mUserData->s.strOsType = mParent->getUnknownOSType()->i_id();
8692
8693 /* create associated BIOS settings object */
8694 unconst(mBIOSSettings).createObject();
8695 mBIOSSettings->init(this);
8696
8697 /* create an associated VRDE object (default is disabled) */
8698 unconst(mVRDEServer).createObject();
8699 mVRDEServer->init(this);
8700
8701 /* create associated serial port objects */
8702 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8703 {
8704 unconst(mSerialPorts[slot]).createObject();
8705 mSerialPorts[slot]->init(this, slot);
8706 }
8707
8708 /* create associated parallel port objects */
8709 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8710 {
8711 unconst(mParallelPorts[slot]).createObject();
8712 mParallelPorts[slot]->init(this, slot);
8713 }
8714
8715 /* create the audio adapter object (always present, default is disabled) */
8716 unconst(mAudioAdapter).createObject();
8717 mAudioAdapter->init(this);
8718
8719 /* create the USB device filters object (always present) */
8720 unconst(mUSBDeviceFilters).createObject();
8721 mUSBDeviceFilters->init(this);
8722
8723 /* create associated network adapter objects */
8724 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8725 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8726 {
8727 unconst(mNetworkAdapters[slot]).createObject();
8728 mNetworkAdapters[slot]->init(this, slot);
8729 }
8730
8731 /* create the bandwidth control */
8732 unconst(mBandwidthControl).createObject();
8733 mBandwidthControl->init(this);
8734
8735 return S_OK;
8736}
8737
8738/**
8739 * Helper to uninitialize all associated child objects and to free all data
8740 * structures.
8741 *
8742 * This method must be called as a part of the object's uninitialization
8743 * procedure (usually done in the #uninit() method).
8744 *
8745 * @note Must be called only from #uninit() or from #registeredInit().
8746 */
8747void Machine::uninitDataAndChildObjects()
8748{
8749 AutoCaller autoCaller(this);
8750 AssertComRCReturnVoid(autoCaller.rc());
8751 AssertComRCReturnVoid( autoCaller.state() == InUninit
8752 || autoCaller.state() == Limited);
8753
8754 /* tell all our other child objects we've been uninitialized */
8755 if (mBandwidthControl)
8756 {
8757 mBandwidthControl->uninit();
8758 unconst(mBandwidthControl).setNull();
8759 }
8760
8761 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8762 {
8763 if (mNetworkAdapters[slot])
8764 {
8765 mNetworkAdapters[slot]->uninit();
8766 unconst(mNetworkAdapters[slot]).setNull();
8767 }
8768 }
8769
8770 if (mUSBDeviceFilters)
8771 {
8772 mUSBDeviceFilters->uninit();
8773 unconst(mUSBDeviceFilters).setNull();
8774 }
8775
8776 if (mAudioAdapter)
8777 {
8778 mAudioAdapter->uninit();
8779 unconst(mAudioAdapter).setNull();
8780 }
8781
8782 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8783 {
8784 if (mParallelPorts[slot])
8785 {
8786 mParallelPorts[slot]->uninit();
8787 unconst(mParallelPorts[slot]).setNull();
8788 }
8789 }
8790
8791 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8792 {
8793 if (mSerialPorts[slot])
8794 {
8795 mSerialPorts[slot]->uninit();
8796 unconst(mSerialPorts[slot]).setNull();
8797 }
8798 }
8799
8800 if (mVRDEServer)
8801 {
8802 mVRDEServer->uninit();
8803 unconst(mVRDEServer).setNull();
8804 }
8805
8806 if (mBIOSSettings)
8807 {
8808 mBIOSSettings->uninit();
8809 unconst(mBIOSSettings).setNull();
8810 }
8811
8812 /* Deassociate media (only when a real Machine or a SnapshotMachine
8813 * instance is uninitialized; SessionMachine instances refer to real
8814 * Machine media). This is necessary for a clean re-initialization of
8815 * the VM after successfully re-checking the accessibility state. Note
8816 * that in case of normal Machine or SnapshotMachine uninitialization (as
8817 * a result of unregistering or deleting the snapshot), outdated media
8818 * attachments will already be uninitialized and deleted, so this
8819 * code will not affect them. */
8820 if ( !!mMediaData
8821 && (!isSessionMachine())
8822 )
8823 {
8824 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8825 it != mMediaData->mAttachments.end();
8826 ++it)
8827 {
8828 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8829 if (pMedium.isNull())
8830 continue;
8831 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, getSnapshotId());
8832 AssertComRC(rc);
8833 }
8834 }
8835
8836 if (!isSessionMachine() && !isSnapshotMachine())
8837 {
8838 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8839 if (mData->mFirstSnapshot)
8840 {
8841 // snapshots tree is protected by machine write lock; strictly
8842 // this isn't necessary here since we're deleting the entire
8843 // machine, but otherwise we assert in Snapshot::uninit()
8844 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8845 mData->mFirstSnapshot->uninit();
8846 mData->mFirstSnapshot.setNull();
8847 }
8848
8849 mData->mCurrentSnapshot.setNull();
8850 }
8851
8852 /* free data structures (the essential mData structure is not freed here
8853 * since it may be still in use) */
8854 mMediaData.free();
8855 mStorageControllers.free();
8856 mUSBControllers.free();
8857 mHWData.free();
8858 mUserData.free();
8859 mSSData.free();
8860}
8861
8862/**
8863 * Returns a pointer to the Machine object for this machine that acts like a
8864 * parent for complex machine data objects such as shared folders, etc.
8865 *
8866 * For primary Machine objects and for SnapshotMachine objects, returns this
8867 * object's pointer itself. For SessionMachine objects, returns the peer
8868 * (primary) machine pointer.
8869 */
8870Machine* Machine::getMachine()
8871{
8872 if (isSessionMachine())
8873 return (Machine*)mPeer;
8874 return this;
8875}
8876
8877/**
8878 * Makes sure that there are no machine state dependents. If necessary, waits
8879 * for the number of dependents to drop to zero.
8880 *
8881 * Make sure this method is called from under this object's write lock to
8882 * guarantee that no new dependents may be added when this method returns
8883 * control to the caller.
8884 *
8885 * @note Locks this object for writing. The lock will be released while waiting
8886 * (if necessary).
8887 *
8888 * @warning To be used only in methods that change the machine state!
8889 */
8890void Machine::ensureNoStateDependencies()
8891{
8892 AssertReturnVoid(isWriteLockOnCurrentThread());
8893
8894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8895
8896 /* Wait for all state dependents if necessary */
8897 if (mData->mMachineStateDeps != 0)
8898 {
8899 /* lazy semaphore creation */
8900 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8901 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8902
8903 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8904 mData->mMachineStateDeps));
8905
8906 ++mData->mMachineStateChangePending;
8907
8908 /* reset the semaphore before waiting, the last dependent will signal
8909 * it */
8910 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8911
8912 alock.release();
8913
8914 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8915
8916 alock.acquire();
8917
8918 -- mData->mMachineStateChangePending;
8919 }
8920}
8921
8922/**
8923 * Changes the machine state and informs callbacks.
8924 *
8925 * This method is not intended to fail so it either returns S_OK or asserts (and
8926 * returns a failure).
8927 *
8928 * @note Locks this object for writing.
8929 */
8930HRESULT Machine::setMachineState(MachineState_T aMachineState)
8931{
8932 LogFlowThisFuncEnter();
8933 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8934
8935 AutoCaller autoCaller(this);
8936 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8937
8938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8939
8940 /* wait for state dependents to drop to zero */
8941 ensureNoStateDependencies();
8942
8943 if (mData->mMachineState != aMachineState)
8944 {
8945 mData->mMachineState = aMachineState;
8946
8947 RTTimeNow(&mData->mLastStateChange);
8948
8949 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8950 }
8951
8952 LogFlowThisFuncLeave();
8953 return S_OK;
8954}
8955
8956/**
8957 * Searches for a shared folder with the given logical name
8958 * in the collection of shared folders.
8959 *
8960 * @param aName logical name of the shared folder
8961 * @param aSharedFolder where to return the found object
8962 * @param aSetError whether to set the error info if the folder is
8963 * not found
8964 * @return
8965 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8966 *
8967 * @note
8968 * must be called from under the object's lock!
8969 */
8970HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8971 ComObjPtr<SharedFolder> &aSharedFolder,
8972 bool aSetError /* = false */)
8973{
8974 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8975 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8976 it != mHWData->mSharedFolders.end();
8977 ++it)
8978 {
8979 SharedFolder *pSF = *it;
8980 AutoCaller autoCaller(pSF);
8981 if (pSF->getName() == aName)
8982 {
8983 aSharedFolder = pSF;
8984 rc = S_OK;
8985 break;
8986 }
8987 }
8988
8989 if (aSetError && FAILED(rc))
8990 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8991
8992 return rc;
8993}
8994
8995/**
8996 * Initializes all machine instance data from the given settings structures
8997 * from XML. The exception is the machine UUID which needs special handling
8998 * depending on the caller's use case, so the caller needs to set that herself.
8999 *
9000 * This gets called in several contexts during machine initialization:
9001 *
9002 * -- When machine XML exists on disk already and needs to be loaded into memory,
9003 * for example, from registeredInit() to load all registered machines on
9004 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
9005 * attached to the machine should be part of some media registry already.
9006 *
9007 * -- During OVF import, when a machine config has been constructed from an
9008 * OVF file. In this case, puuidRegistry is set to the machine UUID to
9009 * ensure that the media listed as attachments in the config (which have
9010 * been imported from the OVF) receive the correct registry ID.
9011 *
9012 * -- During VM cloning.
9013 *
9014 * @param config Machine settings from XML.
9015 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
9016 * @return
9017 */
9018HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9019 const Guid *puuidRegistry)
9020{
9021 // copy name, description, OS type, teleporter, UTC etc.
9022 mUserData->s = config.machineUserData;
9023
9024 // Decode the Icon overide data from config userdata and set onto Machine.
9025 #define DECODE_STR_MAX _1M
9026 const char* pszStr = config.machineUserData.ovIcon.c_str();
9027 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
9028 if (cbOut > DECODE_STR_MAX)
9029 return setError(E_FAIL,
9030 tr("Icon Data too long.'%d' > '%d'"),
9031 cbOut,
9032 DECODE_STR_MAX);
9033 com::SafeArray<BYTE> iconByte(cbOut);
9034 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
9035 if (FAILED(rc))
9036 return setError(E_FAIL,
9037 tr("Failure to Decode Icon Data. '%s' (%d)"),
9038 pszStr,
9039 rc);
9040 mUserData->mIcon.resize(iconByte.size());
9041 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
9042
9043 // look up the object by Id to check it is valid
9044 ComPtr<IGuestOSType> guestOSType;
9045 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
9046 guestOSType.asOutParam());
9047 if (FAILED(rc)) return rc;
9048
9049 // stateFile (optional)
9050 if (config.strStateFile.isEmpty())
9051 mSSData->strStateFilePath.setNull();
9052 else
9053 {
9054 Utf8Str stateFilePathFull(config.strStateFile);
9055 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
9056 if (RT_FAILURE(vrc))
9057 return setError(E_FAIL,
9058 tr("Invalid saved state file path '%s' (%Rrc)"),
9059 config.strStateFile.c_str(),
9060 vrc);
9061 mSSData->strStateFilePath = stateFilePathFull;
9062 }
9063
9064 // snapshot folder needs special processing so set it again
9065 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9066 if (FAILED(rc)) return rc;
9067
9068 /* Copy the extra data items (Not in any case config is already the same as
9069 * mData->pMachineConfigFile, like when the xml files are read from disk. So
9070 * make sure the extra data map is copied). */
9071 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9072
9073 /* currentStateModified (optional, default is true) */
9074 mData->mCurrentStateModified = config.fCurrentStateModified;
9075
9076 mData->mLastStateChange = config.timeLastStateChange;
9077
9078 /*
9079 * note: all mUserData members must be assigned prior this point because
9080 * we need to commit changes in order to let mUserData be shared by all
9081 * snapshot machine instances.
9082 */
9083 mUserData.commitCopy();
9084
9085 // machine registry, if present (must be loaded before snapshots)
9086 if (config.canHaveOwnMediaRegistry())
9087 {
9088 // determine machine folder
9089 Utf8Str strMachineFolder = getSettingsFileFull();
9090 strMachineFolder.stripFilename();
9091 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
9092 config.mediaRegistry,
9093 strMachineFolder);
9094 if (FAILED(rc)) return rc;
9095 }
9096
9097 /* Snapshot node (optional) */
9098 size_t cRootSnapshots;
9099 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9100 {
9101 // there must be only one root snapshot
9102 Assert(cRootSnapshots == 1);
9103
9104 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9105
9106 rc = loadSnapshot(snap,
9107 config.uuidCurrentSnapshot,
9108 NULL); // no parent == first snapshot
9109 if (FAILED(rc)) return rc;
9110 }
9111
9112 // hardware data
9113 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9114 if (FAILED(rc)) return rc;
9115
9116 // load storage controllers
9117 rc = loadStorageControllers(config.storageMachine,
9118 puuidRegistry,
9119 NULL /* puuidSnapshot */);
9120 if (FAILED(rc)) return rc;
9121
9122 /*
9123 * NOTE: the assignment below must be the last thing to do,
9124 * otherwise it will be not possible to change the settings
9125 * somewhere in the code above because all setters will be
9126 * blocked by checkStateDependency(MutableStateDep).
9127 */
9128
9129 /* set the machine state to Aborted or Saved when appropriate */
9130 if (config.fAborted)
9131 {
9132 mSSData->strStateFilePath.setNull();
9133
9134 /* no need to use setMachineState() during init() */
9135 mData->mMachineState = MachineState_Aborted;
9136 }
9137 else if (!mSSData->strStateFilePath.isEmpty())
9138 {
9139 /* no need to use setMachineState() during init() */
9140 mData->mMachineState = MachineState_Saved;
9141 }
9142
9143 // after loading settings, we are no longer different from the XML on disk
9144 mData->flModifications = 0;
9145
9146 return S_OK;
9147}
9148
9149/**
9150 * Recursively loads all snapshots starting from the given.
9151 *
9152 * @param aNode <Snapshot> node.
9153 * @param aCurSnapshotId Current snapshot ID from the settings file.
9154 * @param aParentSnapshot Parent snapshot.
9155 */
9156HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9157 const Guid &aCurSnapshotId,
9158 Snapshot *aParentSnapshot)
9159{
9160 AssertReturn(!isSnapshotMachine(), E_FAIL);
9161 AssertReturn(!isSessionMachine(), E_FAIL);
9162
9163 HRESULT rc = S_OK;
9164
9165 Utf8Str strStateFile;
9166 if (!data.strStateFile.isEmpty())
9167 {
9168 /* optional */
9169 strStateFile = data.strStateFile;
9170 int vrc = calculateFullPath(strStateFile, strStateFile);
9171 if (RT_FAILURE(vrc))
9172 return setError(E_FAIL,
9173 tr("Invalid saved state file path '%s' (%Rrc)"),
9174 strStateFile.c_str(),
9175 vrc);
9176 }
9177
9178 /* create a snapshot machine object */
9179 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9180 pSnapshotMachine.createObject();
9181 rc = pSnapshotMachine->initFromSettings(this,
9182 data.hardware,
9183 &data.debugging,
9184 &data.autostart,
9185 data.storage,
9186 data.uuid.ref(),
9187 strStateFile);
9188 if (FAILED(rc)) return rc;
9189
9190 /* create a snapshot object */
9191 ComObjPtr<Snapshot> pSnapshot;
9192 pSnapshot.createObject();
9193 /* initialize the snapshot */
9194 rc = pSnapshot->init(mParent, // VirtualBox object
9195 data.uuid,
9196 data.strName,
9197 data.strDescription,
9198 data.timestamp,
9199 pSnapshotMachine,
9200 aParentSnapshot);
9201 if (FAILED(rc)) return rc;
9202
9203 /* memorize the first snapshot if necessary */
9204 if (!mData->mFirstSnapshot)
9205 mData->mFirstSnapshot = pSnapshot;
9206
9207 /* memorize the current snapshot when appropriate */
9208 if ( !mData->mCurrentSnapshot
9209 && pSnapshot->i_getId() == aCurSnapshotId
9210 )
9211 mData->mCurrentSnapshot = pSnapshot;
9212
9213 // now create the children
9214 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9215 it != data.llChildSnapshots.end();
9216 ++it)
9217 {
9218 const settings::Snapshot &childData = *it;
9219 // recurse
9220 rc = loadSnapshot(childData,
9221 aCurSnapshotId,
9222 pSnapshot); // parent = the one we created above
9223 if (FAILED(rc)) return rc;
9224 }
9225
9226 return rc;
9227}
9228
9229/**
9230 * Loads settings into mHWData.
9231 *
9232 * @param data Reference to the hardware settings.
9233 * @param pDbg Pointer to the debugging settings.
9234 * @param pAutostart Pointer to the autostart settings.
9235 */
9236HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9237 const settings::Autostart *pAutostart)
9238{
9239 AssertReturn(!isSessionMachine(), E_FAIL);
9240
9241 HRESULT rc = S_OK;
9242
9243 try
9244 {
9245 /* The hardware version attribute (optional). */
9246 mHWData->mHWVersion = data.strVersion;
9247 mHWData->mHardwareUUID = data.uuid;
9248
9249 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9250 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9251 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9252 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9253 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9254 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9255 mHWData->mPAEEnabled = data.fPAE;
9256 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9257 mHWData->mLongMode = data.enmLongMode;
9258 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9259 mHWData->mCPUCount = data.cCPUs;
9260 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9261 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9262
9263 // cpu
9264 if (mHWData->mCPUHotPlugEnabled)
9265 {
9266 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9267 it != data.llCpus.end();
9268 ++it)
9269 {
9270 const settings::Cpu &cpu = *it;
9271
9272 mHWData->mCPUAttached[cpu.ulId] = true;
9273 }
9274 }
9275
9276 // cpuid leafs
9277 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9278 it != data.llCpuIdLeafs.end();
9279 ++it)
9280 {
9281 const settings::CpuIdLeaf &leaf = *it;
9282
9283 switch (leaf.ulId)
9284 {
9285 case 0x0:
9286 case 0x1:
9287 case 0x2:
9288 case 0x3:
9289 case 0x4:
9290 case 0x5:
9291 case 0x6:
9292 case 0x7:
9293 case 0x8:
9294 case 0x9:
9295 case 0xA:
9296 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9297 break;
9298
9299 case 0x80000000:
9300 case 0x80000001:
9301 case 0x80000002:
9302 case 0x80000003:
9303 case 0x80000004:
9304 case 0x80000005:
9305 case 0x80000006:
9306 case 0x80000007:
9307 case 0x80000008:
9308 case 0x80000009:
9309 case 0x8000000A:
9310 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9311 break;
9312
9313 default:
9314 /* just ignore */
9315 break;
9316 }
9317 }
9318
9319 mHWData->mMemorySize = data.ulMemorySizeMB;
9320 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9321
9322 // boot order
9323 for (size_t i = 0;
9324 i < RT_ELEMENTS(mHWData->mBootOrder);
9325 i++)
9326 {
9327 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9328 if (it == data.mapBootOrder.end())
9329 mHWData->mBootOrder[i] = DeviceType_Null;
9330 else
9331 mHWData->mBootOrder[i] = it->second;
9332 }
9333
9334 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9335 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9336 mHWData->mMonitorCount = data.cMonitors;
9337 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9338 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9339 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9340 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9341 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9342 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9343 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9344 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9345 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9346 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9347 if (!data.strVideoCaptureFile.isEmpty())
9348 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9349 else
9350 mHWData->mVideoCaptureFile.setNull();
9351 mHWData->mFirmwareType = data.firmwareType;
9352 mHWData->mPointingHIDType = data.pointingHIDType;
9353 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9354 mHWData->mChipsetType = data.chipsetType;
9355 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9356 mHWData->mHPETEnabled = data.fHPETEnabled;
9357
9358 /* VRDEServer */
9359 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9360 if (FAILED(rc)) return rc;
9361
9362 /* BIOS */
9363 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9364 if (FAILED(rc)) return rc;
9365
9366 // Bandwidth control (must come before network adapters)
9367 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9368 if (FAILED(rc)) return rc;
9369
9370 /* Shared folders */
9371 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9372 it != data.usbSettings.llUSBControllers.end();
9373 ++it)
9374 {
9375 const settings::USBController &settingsCtrl = *it;
9376 ComObjPtr<USBController> newCtrl;
9377
9378 newCtrl.createObject();
9379 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9380 mUSBControllers->push_back(newCtrl);
9381 }
9382
9383 /* USB device filters */
9384 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9385 if (FAILED(rc)) return rc;
9386
9387 // network adapters
9388 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9389 uint32_t oldCount = mNetworkAdapters.size();
9390 if (newCount > oldCount)
9391 {
9392 mNetworkAdapters.resize(newCount);
9393 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9394 {
9395 unconst(mNetworkAdapters[slot]).createObject();
9396 mNetworkAdapters[slot]->init(this, slot);
9397 }
9398 }
9399 else if (newCount < oldCount)
9400 mNetworkAdapters.resize(newCount);
9401 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9402 it != data.llNetworkAdapters.end();
9403 ++it)
9404 {
9405 const settings::NetworkAdapter &nic = *it;
9406
9407 /* slot unicity is guaranteed by XML Schema */
9408 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9409 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9410 if (FAILED(rc)) return rc;
9411 }
9412
9413 // serial ports
9414 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9415 it != data.llSerialPorts.end();
9416 ++it)
9417 {
9418 const settings::SerialPort &s = *it;
9419
9420 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9421 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9422 if (FAILED(rc)) return rc;
9423 }
9424
9425 // parallel ports (optional)
9426 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9427 it != data.llParallelPorts.end();
9428 ++it)
9429 {
9430 const settings::ParallelPort &p = *it;
9431
9432 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9433 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9434 if (FAILED(rc)) return rc;
9435 }
9436
9437 /* AudioAdapter */
9438 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9439 if (FAILED(rc)) return rc;
9440
9441 /* Shared folders */
9442 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9443 it != data.llSharedFolders.end();
9444 ++it)
9445 {
9446 const settings::SharedFolder &sf = *it;
9447
9448 ComObjPtr<SharedFolder> sharedFolder;
9449 /* Check for double entries. Not allowed! */
9450 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9451 if (SUCCEEDED(rc))
9452 return setError(VBOX_E_OBJECT_IN_USE,
9453 tr("Shared folder named '%s' already exists"),
9454 sf.strName.c_str());
9455
9456 /* Create the new shared folder. Don't break on error. This will be
9457 * reported when the machine starts. */
9458 sharedFolder.createObject();
9459 rc = sharedFolder->init(getMachine(),
9460 sf.strName,
9461 sf.strHostPath,
9462 RT_BOOL(sf.fWritable),
9463 RT_BOOL(sf.fAutoMount),
9464 false /* fFailOnError */);
9465 if (FAILED(rc)) return rc;
9466 mHWData->mSharedFolders.push_back(sharedFolder);
9467 }
9468
9469 // Clipboard
9470 mHWData->mClipboardMode = data.clipboardMode;
9471
9472 // drag'n'drop
9473 mHWData->mDragAndDropMode = data.dragAndDropMode;
9474
9475 // guest settings
9476 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9477
9478 // IO settings
9479 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9480 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9481
9482 // Host PCI devices
9483 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9484 it != data.pciAttachments.end();
9485 ++it)
9486 {
9487 const settings::HostPCIDeviceAttachment &hpda = *it;
9488 ComObjPtr<PCIDeviceAttachment> pda;
9489
9490 pda.createObject();
9491 pda->loadSettings(this, hpda);
9492 mHWData->mPCIDeviceAssignments.push_back(pda);
9493 }
9494
9495 /*
9496 * (The following isn't really real hardware, but it lives in HWData
9497 * for reasons of convenience.)
9498 */
9499
9500#ifdef VBOX_WITH_GUEST_PROPS
9501 /* Guest properties (optional) */
9502 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9503 it != data.llGuestProperties.end();
9504 ++it)
9505 {
9506 const settings::GuestProperty &prop = *it;
9507 uint32_t fFlags = guestProp::NILFLAG;
9508 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9509 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9510 mHWData->mGuestProperties[prop.strName] = property;
9511 }
9512
9513 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9514#endif /* VBOX_WITH_GUEST_PROPS defined */
9515
9516 rc = loadDebugging(pDbg);
9517 if (FAILED(rc))
9518 return rc;
9519
9520 mHWData->mAutostart = *pAutostart;
9521
9522 /* default frontend */
9523 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9524 }
9525 catch(std::bad_alloc &)
9526 {
9527 return E_OUTOFMEMORY;
9528 }
9529
9530 AssertComRC(rc);
9531 return rc;
9532}
9533
9534/**
9535 * Called from Machine::loadHardware() to load the debugging settings of the
9536 * machine.
9537 *
9538 * @param pDbg Pointer to the settings.
9539 */
9540HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9541{
9542 mHWData->mDebugging = *pDbg;
9543 /* no more processing currently required, this will probably change. */
9544 return S_OK;
9545}
9546
9547/**
9548 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9549 *
9550 * @param data
9551 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9552 * @param puuidSnapshot
9553 * @return
9554 */
9555HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9556 const Guid *puuidRegistry,
9557 const Guid *puuidSnapshot)
9558{
9559 AssertReturn(!isSessionMachine(), E_FAIL);
9560
9561 HRESULT rc = S_OK;
9562
9563 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9564 it != data.llStorageControllers.end();
9565 ++it)
9566 {
9567 const settings::StorageController &ctlData = *it;
9568
9569 ComObjPtr<StorageController> pCtl;
9570 /* Try to find one with the name first. */
9571 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9572 if (SUCCEEDED(rc))
9573 return setError(VBOX_E_OBJECT_IN_USE,
9574 tr("Storage controller named '%s' already exists"),
9575 ctlData.strName.c_str());
9576
9577 pCtl.createObject();
9578 rc = pCtl->init(this,
9579 ctlData.strName,
9580 ctlData.storageBus,
9581 ctlData.ulInstance,
9582 ctlData.fBootable);
9583 if (FAILED(rc)) return rc;
9584
9585 mStorageControllers->push_back(pCtl);
9586
9587 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9588 if (FAILED(rc)) return rc;
9589
9590 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9591 if (FAILED(rc)) return rc;
9592
9593 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9594 if (FAILED(rc)) return rc;
9595
9596 /* Set IDE emulation settings (only for AHCI controller). */
9597 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9598 {
9599 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9600 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9601 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9602 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9603 )
9604 return rc;
9605 }
9606
9607 /* Load the attached devices now. */
9608 rc = loadStorageDevices(pCtl,
9609 ctlData,
9610 puuidRegistry,
9611 puuidSnapshot);
9612 if (FAILED(rc)) return rc;
9613 }
9614
9615 return S_OK;
9616}
9617
9618/**
9619 * Called from loadStorageControllers for a controller's devices.
9620 *
9621 * @param aStorageController
9622 * @param data
9623 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9624 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9625 * @return
9626 */
9627HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9628 const settings::StorageController &data,
9629 const Guid *puuidRegistry,
9630 const Guid *puuidSnapshot)
9631{
9632 HRESULT rc = S_OK;
9633
9634 /* paranoia: detect duplicate attachments */
9635 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9636 it != data.llAttachedDevices.end();
9637 ++it)
9638 {
9639 const settings::AttachedDevice &ad = *it;
9640
9641 for (settings::AttachedDevicesList::const_iterator it2 = it;
9642 it2 != data.llAttachedDevices.end();
9643 ++it2)
9644 {
9645 if (it == it2)
9646 continue;
9647
9648 const settings::AttachedDevice &ad2 = *it2;
9649
9650 if ( ad.lPort == ad2.lPort
9651 && ad.lDevice == ad2.lDevice)
9652 {
9653 return setError(E_FAIL,
9654 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9655 aStorageController->i_getName().c_str(),
9656 ad.lPort,
9657 ad.lDevice,
9658 mUserData->s.strName.c_str());
9659 }
9660 }
9661 }
9662
9663 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9664 it != data.llAttachedDevices.end();
9665 ++it)
9666 {
9667 const settings::AttachedDevice &dev = *it;
9668 ComObjPtr<Medium> medium;
9669
9670 switch (dev.deviceType)
9671 {
9672 case DeviceType_Floppy:
9673 case DeviceType_DVD:
9674 if (dev.strHostDriveSrc.isNotEmpty())
9675 rc = mParent->host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9676 else
9677 rc = mParent->findRemoveableMedium(dev.deviceType,
9678 dev.uuid,
9679 false /* fRefresh */,
9680 false /* aSetError */,
9681 medium);
9682 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9683 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9684 rc = S_OK;
9685 break;
9686
9687 case DeviceType_HardDisk:
9688 {
9689 /* find a hard disk by UUID */
9690 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9691 if (FAILED(rc))
9692 {
9693 if (isSnapshotMachine())
9694 {
9695 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9696 // so the user knows that the bad disk is in a snapshot somewhere
9697 com::ErrorInfo info;
9698 return setError(E_FAIL,
9699 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9700 puuidSnapshot->raw(),
9701 info.getText().raw());
9702 }
9703 else
9704 return rc;
9705 }
9706
9707 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9708
9709 if (medium->i_getType() == MediumType_Immutable)
9710 {
9711 if (isSnapshotMachine())
9712 return setError(E_FAIL,
9713 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9714 "of the virtual machine '%s' ('%s')"),
9715 medium->i_getLocationFull().c_str(),
9716 dev.uuid.raw(),
9717 puuidSnapshot->raw(),
9718 mUserData->s.strName.c_str(),
9719 mData->m_strConfigFileFull.c_str());
9720
9721 return setError(E_FAIL,
9722 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9723 medium->i_getLocationFull().c_str(),
9724 dev.uuid.raw(),
9725 mUserData->s.strName.c_str(),
9726 mData->m_strConfigFileFull.c_str());
9727 }
9728
9729 if (medium->i_getType() == MediumType_MultiAttach)
9730 {
9731 if (isSnapshotMachine())
9732 return setError(E_FAIL,
9733 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9734 "of the virtual machine '%s' ('%s')"),
9735 medium->i_getLocationFull().c_str(),
9736 dev.uuid.raw(),
9737 puuidSnapshot->raw(),
9738 mUserData->s.strName.c_str(),
9739 mData->m_strConfigFileFull.c_str());
9740
9741 return setError(E_FAIL,
9742 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9743 medium->i_getLocationFull().c_str(),
9744 dev.uuid.raw(),
9745 mUserData->s.strName.c_str(),
9746 mData->m_strConfigFileFull.c_str());
9747 }
9748
9749 if ( !isSnapshotMachine()
9750 && medium->i_getChildren().size() != 0
9751 )
9752 return setError(E_FAIL,
9753 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9754 "because it has %d differencing child hard disks"),
9755 medium->i_getLocationFull().c_str(),
9756 dev.uuid.raw(),
9757 mUserData->s.strName.c_str(),
9758 mData->m_strConfigFileFull.c_str(),
9759 medium->i_getChildren().size());
9760
9761 if (findAttachment(mMediaData->mAttachments,
9762 medium))
9763 return setError(E_FAIL,
9764 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9765 medium->i_getLocationFull().c_str(),
9766 dev.uuid.raw(),
9767 mUserData->s.strName.c_str(),
9768 mData->m_strConfigFileFull.c_str());
9769
9770 break;
9771 }
9772
9773 default:
9774 return setError(E_FAIL,
9775 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9776 medium->i_getLocationFull().c_str(),
9777 mUserData->s.strName.c_str(),
9778 mData->m_strConfigFileFull.c_str());
9779 }
9780
9781 if (FAILED(rc))
9782 break;
9783
9784 /* Bandwidth groups are loaded at this point. */
9785 ComObjPtr<BandwidthGroup> pBwGroup;
9786
9787 if (!dev.strBwGroup.isEmpty())
9788 {
9789 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9790 if (FAILED(rc))
9791 return setError(E_FAIL,
9792 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9793 medium->i_getLocationFull().c_str(),
9794 dev.strBwGroup.c_str(),
9795 mUserData->s.strName.c_str(),
9796 mData->m_strConfigFileFull.c_str());
9797 pBwGroup->i_reference();
9798 }
9799
9800 const Bstr controllerName = aStorageController->i_getName();
9801 ComObjPtr<MediumAttachment> pAttachment;
9802 pAttachment.createObject();
9803 rc = pAttachment->init(this,
9804 medium,
9805 controllerName,
9806 dev.lPort,
9807 dev.lDevice,
9808 dev.deviceType,
9809 false,
9810 dev.fPassThrough,
9811 dev.fTempEject,
9812 dev.fNonRotational,
9813 dev.fDiscard,
9814 dev.fHotPluggable,
9815 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9816 if (FAILED(rc)) break;
9817
9818 /* associate the medium with this machine and snapshot */
9819 if (!medium.isNull())
9820 {
9821 AutoCaller medCaller(medium);
9822 if (FAILED(medCaller.rc())) return medCaller.rc();
9823 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9824
9825 if (isSnapshotMachine())
9826 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9827 else
9828 rc = medium->i_addBackReference(mData->mUuid);
9829 /* If the medium->addBackReference fails it sets an appropriate
9830 * error message, so no need to do any guesswork here. */
9831
9832 if (puuidRegistry)
9833 // caller wants registry ID to be set on all attached media (OVF import case)
9834 medium->i_addRegistry(*puuidRegistry, false /* fRecurse */);
9835 }
9836
9837 if (FAILED(rc))
9838 break;
9839
9840 /* back up mMediaData to let registeredInit() properly rollback on failure
9841 * (= limited accessibility) */
9842 setModified(IsModified_Storage);
9843 mMediaData.backup();
9844 mMediaData->mAttachments.push_back(pAttachment);
9845 }
9846
9847 return rc;
9848}
9849
9850/**
9851 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9852 *
9853 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9854 * @param aSnapshot where to return the found snapshot
9855 * @param aSetError true to set extended error info on failure
9856 */
9857HRESULT Machine::findSnapshotById(const Guid &aId,
9858 ComObjPtr<Snapshot> &aSnapshot,
9859 bool aSetError /* = false */)
9860{
9861 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9862
9863 if (!mData->mFirstSnapshot)
9864 {
9865 if (aSetError)
9866 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9867 return E_FAIL;
9868 }
9869
9870 if (aId.isZero())
9871 aSnapshot = mData->mFirstSnapshot;
9872 else
9873 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9874
9875 if (!aSnapshot)
9876 {
9877 if (aSetError)
9878 return setError(E_FAIL,
9879 tr("Could not find a snapshot with UUID {%s}"),
9880 aId.toString().c_str());
9881 return E_FAIL;
9882 }
9883
9884 return S_OK;
9885}
9886
9887/**
9888 * Returns the snapshot with the given name or fails of no such snapshot.
9889 *
9890 * @param aName snapshot name to find
9891 * @param aSnapshot where to return the found snapshot
9892 * @param aSetError true to set extended error info on failure
9893 */
9894HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9895 ComObjPtr<Snapshot> &aSnapshot,
9896 bool aSetError /* = false */)
9897{
9898 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9899
9900 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9901
9902 if (!mData->mFirstSnapshot)
9903 {
9904 if (aSetError)
9905 return setError(VBOX_E_OBJECT_NOT_FOUND,
9906 tr("This machine does not have any snapshots"));
9907 return VBOX_E_OBJECT_NOT_FOUND;
9908 }
9909
9910 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9911
9912 if (!aSnapshot)
9913 {
9914 if (aSetError)
9915 return setError(VBOX_E_OBJECT_NOT_FOUND,
9916 tr("Could not find a snapshot named '%s'"), strName.c_str());
9917 return VBOX_E_OBJECT_NOT_FOUND;
9918 }
9919
9920 return S_OK;
9921}
9922
9923/**
9924 * Returns a storage controller object with the given name.
9925 *
9926 * @param aName storage controller name to find
9927 * @param aStorageController where to return the found storage controller
9928 * @param aSetError true to set extended error info on failure
9929 */
9930HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9931 ComObjPtr<StorageController> &aStorageController,
9932 bool aSetError /* = false */)
9933{
9934 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9935
9936 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9937 it != mStorageControllers->end();
9938 ++it)
9939 {
9940 if ((*it)->i_getName() == aName)
9941 {
9942 aStorageController = (*it);
9943 return S_OK;
9944 }
9945 }
9946
9947 if (aSetError)
9948 return setError(VBOX_E_OBJECT_NOT_FOUND,
9949 tr("Could not find a storage controller named '%s'"),
9950 aName.c_str());
9951 return VBOX_E_OBJECT_NOT_FOUND;
9952}
9953
9954/**
9955 * Returns a USB controller object with the given name.
9956 *
9957 * @param aName USB controller name to find
9958 * @param aUSBController where to return the found USB controller
9959 * @param aSetError true to set extended error info on failure
9960 */
9961HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9962 ComObjPtr<USBController> &aUSBController,
9963 bool aSetError /* = false */)
9964{
9965 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9966
9967 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9968 it != mUSBControllers->end();
9969 ++it)
9970 {
9971 if ((*it)->i_getName() == aName)
9972 {
9973 aUSBController = (*it);
9974 return S_OK;
9975 }
9976 }
9977
9978 if (aSetError)
9979 return setError(VBOX_E_OBJECT_NOT_FOUND,
9980 tr("Could not find a storage controller named '%s'"),
9981 aName.c_str());
9982 return VBOX_E_OBJECT_NOT_FOUND;
9983}
9984
9985/**
9986 * Returns the number of USB controller instance of the given type.
9987 *
9988 * @param enmType USB controller type.
9989 */
9990ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9991{
9992 ULONG cCtrls = 0;
9993
9994 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9995 it != mUSBControllers->end();
9996 ++it)
9997 {
9998 if ((*it)->i_getControllerType() == enmType)
9999 cCtrls++;
10000 }
10001
10002 return cCtrls;
10003}
10004
10005HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
10006 MediaData::AttachmentList &atts)
10007{
10008 AutoCaller autoCaller(this);
10009 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10010
10011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10012
10013 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
10014 it != mMediaData->mAttachments.end();
10015 ++it)
10016 {
10017 const ComObjPtr<MediumAttachment> &pAtt = *it;
10018
10019 // should never happen, but deal with NULL pointers in the list.
10020 AssertStmt(!pAtt.isNull(), continue);
10021
10022 // getControllerName() needs caller+read lock
10023 AutoCaller autoAttCaller(pAtt);
10024 if (FAILED(autoAttCaller.rc()))
10025 {
10026 atts.clear();
10027 return autoAttCaller.rc();
10028 }
10029 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10030
10031 if (pAtt->i_getControllerName() == aName)
10032 atts.push_back(pAtt);
10033 }
10034
10035 return S_OK;
10036}
10037
10038/**
10039 * Helper for #saveSettings. Cares about renaming the settings directory and
10040 * file if the machine name was changed and about creating a new settings file
10041 * if this is a new machine.
10042 *
10043 * @note Must be never called directly but only from #saveSettings().
10044 */
10045HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
10046{
10047 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10048
10049 HRESULT rc = S_OK;
10050
10051 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10052
10053 /// @todo need to handle primary group change, too
10054
10055 /* attempt to rename the settings file if machine name is changed */
10056 if ( mUserData->s.fNameSync
10057 && mUserData.isBackedUp()
10058 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10059 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10060 )
10061 {
10062 bool dirRenamed = false;
10063 bool fileRenamed = false;
10064
10065 Utf8Str configFile, newConfigFile;
10066 Utf8Str configFilePrev, newConfigFilePrev;
10067 Utf8Str configDir, newConfigDir;
10068
10069 do
10070 {
10071 int vrc = VINF_SUCCESS;
10072
10073 Utf8Str name = mUserData.backedUpData()->s.strName;
10074 Utf8Str newName = mUserData->s.strName;
10075 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10076 if (group == "/")
10077 group.setNull();
10078 Utf8Str newGroup = mUserData->s.llGroups.front();
10079 if (newGroup == "/")
10080 newGroup.setNull();
10081
10082 configFile = mData->m_strConfigFileFull;
10083
10084 /* first, rename the directory if it matches the group and machine name */
10085 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
10086 group.c_str(), RTPATH_DELIMITER, name.c_str());
10087 /** @todo hack, make somehow use of ComposeMachineFilename */
10088 if (mUserData->s.fDirectoryIncludesUUID)
10089 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10090 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
10091 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10092 /** @todo hack, make somehow use of ComposeMachineFilename */
10093 if (mUserData->s.fDirectoryIncludesUUID)
10094 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10095 configDir = configFile;
10096 configDir.stripFilename();
10097 newConfigDir = configDir;
10098 if ( configDir.length() >= groupPlusName.length()
10099 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
10100 {
10101 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10102 Utf8Str newConfigBaseDir(newConfigDir);
10103 newConfigDir.append(newGroupPlusName);
10104 /* consistency: use \ if appropriate on the platform */
10105 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10106 /* new dir and old dir cannot be equal here because of 'if'
10107 * above and because name != newName */
10108 Assert(configDir != newConfigDir);
10109 if (!fSettingsFileIsNew)
10110 {
10111 /* perform real rename only if the machine is not new */
10112 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10113 if ( vrc == VERR_FILE_NOT_FOUND
10114 || vrc == VERR_PATH_NOT_FOUND)
10115 {
10116 /* create the parent directory, then retry renaming */
10117 Utf8Str parent(newConfigDir);
10118 parent.stripFilename();
10119 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10120 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10121 }
10122 if (RT_FAILURE(vrc))
10123 {
10124 rc = setError(E_FAIL,
10125 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10126 configDir.c_str(),
10127 newConfigDir.c_str(),
10128 vrc);
10129 break;
10130 }
10131 /* delete subdirectories which are no longer needed */
10132 Utf8Str dir(configDir);
10133 dir.stripFilename();
10134 while (dir != newConfigBaseDir && dir != ".")
10135 {
10136 vrc = RTDirRemove(dir.c_str());
10137 if (RT_FAILURE(vrc))
10138 break;
10139 dir.stripFilename();
10140 }
10141 dirRenamed = true;
10142 }
10143 }
10144
10145 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10146 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10147
10148 /* then try to rename the settings file itself */
10149 if (newConfigFile != configFile)
10150 {
10151 /* get the path to old settings file in renamed directory */
10152 configFile = Utf8StrFmt("%s%c%s",
10153 newConfigDir.c_str(),
10154 RTPATH_DELIMITER,
10155 RTPathFilename(configFile.c_str()));
10156 if (!fSettingsFileIsNew)
10157 {
10158 /* perform real rename only if the machine is not new */
10159 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10160 if (RT_FAILURE(vrc))
10161 {
10162 rc = setError(E_FAIL,
10163 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10164 configFile.c_str(),
10165 newConfigFile.c_str(),
10166 vrc);
10167 break;
10168 }
10169 fileRenamed = true;
10170 configFilePrev = configFile;
10171 configFilePrev += "-prev";
10172 newConfigFilePrev = newConfigFile;
10173 newConfigFilePrev += "-prev";
10174 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10175 }
10176 }
10177
10178 // update m_strConfigFileFull amd mConfigFile
10179 mData->m_strConfigFileFull = newConfigFile;
10180 // compute the relative path too
10181 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10182
10183 // store the old and new so that VirtualBox::saveSettings() can update
10184 // the media registry
10185 if ( mData->mRegistered
10186 && configDir != newConfigDir)
10187 {
10188 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10189
10190 if (pfNeedsGlobalSaveSettings)
10191 *pfNeedsGlobalSaveSettings = true;
10192 }
10193
10194 // in the saved state file path, replace the old directory with the new directory
10195 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10196 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10197
10198 // and do the same thing for the saved state file paths of all the online snapshots
10199 if (mData->mFirstSnapshot)
10200 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10201 newConfigDir.c_str());
10202 }
10203 while (0);
10204
10205 if (FAILED(rc))
10206 {
10207 /* silently try to rename everything back */
10208 if (fileRenamed)
10209 {
10210 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10211 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10212 }
10213 if (dirRenamed)
10214 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10215 }
10216
10217 if (FAILED(rc)) return rc;
10218 }
10219
10220 if (fSettingsFileIsNew)
10221 {
10222 /* create a virgin config file */
10223 int vrc = VINF_SUCCESS;
10224
10225 /* ensure the settings directory exists */
10226 Utf8Str path(mData->m_strConfigFileFull);
10227 path.stripFilename();
10228 if (!RTDirExists(path.c_str()))
10229 {
10230 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10231 if (RT_FAILURE(vrc))
10232 {
10233 return setError(E_FAIL,
10234 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10235 path.c_str(),
10236 vrc);
10237 }
10238 }
10239
10240 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10241 path = Utf8Str(mData->m_strConfigFileFull);
10242 RTFILE f = NIL_RTFILE;
10243 vrc = RTFileOpen(&f, path.c_str(),
10244 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10245 if (RT_FAILURE(vrc))
10246 return setError(E_FAIL,
10247 tr("Could not create the settings file '%s' (%Rrc)"),
10248 path.c_str(),
10249 vrc);
10250 RTFileClose(f);
10251 }
10252
10253 return rc;
10254}
10255
10256/**
10257 * Saves and commits machine data, user data and hardware data.
10258 *
10259 * Note that on failure, the data remains uncommitted.
10260 *
10261 * @a aFlags may combine the following flags:
10262 *
10263 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10264 * Used when saving settings after an operation that makes them 100%
10265 * correspond to the settings from the current snapshot.
10266 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10267 * #isReallyModified() returns false. This is necessary for cases when we
10268 * change machine data directly, not through the backup()/commit() mechanism.
10269 * - SaveS_Force: settings will be saved without doing a deep compare of the
10270 * settings structures. This is used when this is called because snapshots
10271 * have changed to avoid the overhead of the deep compare.
10272 *
10273 * @note Must be called from under this object's write lock. Locks children for
10274 * writing.
10275 *
10276 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10277 * initialized to false and that will be set to true by this function if
10278 * the caller must invoke VirtualBox::saveSettings() because the global
10279 * settings have changed. This will happen if a machine rename has been
10280 * saved and the global machine and media registries will therefore need
10281 * updating.
10282 */
10283HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10284 int aFlags /*= 0*/)
10285{
10286 LogFlowThisFuncEnter();
10287
10288 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10289
10290 /* make sure child objects are unable to modify the settings while we are
10291 * saving them */
10292 ensureNoStateDependencies();
10293
10294 AssertReturn(!isSnapshotMachine(),
10295 E_FAIL);
10296
10297 HRESULT rc = S_OK;
10298 bool fNeedsWrite = false;
10299
10300 /* First, prepare to save settings. It will care about renaming the
10301 * settings directory and file if the machine name was changed and about
10302 * creating a new settings file if this is a new machine. */
10303 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10304 if (FAILED(rc)) return rc;
10305
10306 // keep a pointer to the current settings structures
10307 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10308 settings::MachineConfigFile *pNewConfig = NULL;
10309
10310 try
10311 {
10312 // make a fresh one to have everyone write stuff into
10313 pNewConfig = new settings::MachineConfigFile(NULL);
10314 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10315
10316 // now go and copy all the settings data from COM to the settings structures
10317 // (this calles saveSettings() on all the COM objects in the machine)
10318 copyMachineDataToSettings(*pNewConfig);
10319
10320 if (aFlags & SaveS_ResetCurStateModified)
10321 {
10322 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10323 mData->mCurrentStateModified = FALSE;
10324 fNeedsWrite = true; // always, no need to compare
10325 }
10326 else if (aFlags & SaveS_Force)
10327 {
10328 fNeedsWrite = true; // always, no need to compare
10329 }
10330 else
10331 {
10332 if (!mData->mCurrentStateModified)
10333 {
10334 // do a deep compare of the settings that we just saved with the settings
10335 // previously stored in the config file; this invokes MachineConfigFile::operator==
10336 // which does a deep compare of all the settings, which is expensive but less expensive
10337 // than writing out XML in vain
10338 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10339
10340 // could still be modified if any settings changed
10341 mData->mCurrentStateModified = fAnySettingsChanged;
10342
10343 fNeedsWrite = fAnySettingsChanged;
10344 }
10345 else
10346 fNeedsWrite = true;
10347 }
10348
10349 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10350
10351 if (fNeedsWrite)
10352 // now spit it all out!
10353 pNewConfig->write(mData->m_strConfigFileFull);
10354
10355 mData->pMachineConfigFile = pNewConfig;
10356 delete pOldConfig;
10357 commit();
10358
10359 // after saving settings, we are no longer different from the XML on disk
10360 mData->flModifications = 0;
10361 }
10362 catch (HRESULT err)
10363 {
10364 // we assume that error info is set by the thrower
10365 rc = err;
10366
10367 // restore old config
10368 delete pNewConfig;
10369 mData->pMachineConfigFile = pOldConfig;
10370 }
10371 catch (...)
10372 {
10373 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10374 }
10375
10376 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10377 {
10378 /* Fire the data change event, even on failure (since we've already
10379 * committed all data). This is done only for SessionMachines because
10380 * mutable Machine instances are always not registered (i.e. private
10381 * to the client process that creates them) and thus don't need to
10382 * inform callbacks. */
10383 if (isSessionMachine())
10384 mParent->onMachineDataChange(mData->mUuid);
10385 }
10386
10387 LogFlowThisFunc(("rc=%08X\n", rc));
10388 LogFlowThisFuncLeave();
10389 return rc;
10390}
10391
10392/**
10393 * Implementation for saving the machine settings into the given
10394 * settings::MachineConfigFile instance. This copies machine extradata
10395 * from the previous machine config file in the instance data, if any.
10396 *
10397 * This gets called from two locations:
10398 *
10399 * -- Machine::saveSettings(), during the regular XML writing;
10400 *
10401 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10402 * exported to OVF and we write the VirtualBox proprietary XML
10403 * into a <vbox:Machine> tag.
10404 *
10405 * This routine fills all the fields in there, including snapshots, *except*
10406 * for the following:
10407 *
10408 * -- fCurrentStateModified. There is some special logic associated with that.
10409 *
10410 * The caller can then call MachineConfigFile::write() or do something else
10411 * with it.
10412 *
10413 * Caller must hold the machine lock!
10414 *
10415 * This throws XML errors and HRESULT, so the caller must have a catch block!
10416 */
10417void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10418{
10419 // deep copy extradata
10420 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10421
10422 config.uuid = mData->mUuid;
10423
10424 // copy name, description, OS type, teleport, UTC etc.
10425 config.machineUserData = mUserData->s;
10426
10427 // Encode the Icon Override data from Machine and store on config userdata.
10428 com::SafeArray<BYTE> iconByte;
10429 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10430 ssize_t cbData = iconByte.size();
10431 if (cbData > 0)
10432 {
10433 ssize_t cchOut = RTBase64EncodedLength(cbData);
10434 Utf8Str strIconData;
10435 strIconData.reserve(cchOut+1);
10436 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10437 strIconData.mutableRaw(), strIconData.capacity(),
10438 NULL);
10439 if (RT_FAILURE(vrc))
10440 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10441 strIconData.jolt();
10442 config.machineUserData.ovIcon = strIconData;
10443 }
10444 else
10445 config.machineUserData.ovIcon.setNull();
10446
10447 if ( mData->mMachineState == MachineState_Saved
10448 || mData->mMachineState == MachineState_Restoring
10449 // when deleting a snapshot we may or may not have a saved state in the current state,
10450 // so let's not assert here please
10451 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10452 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10453 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10454 && (!mSSData->strStateFilePath.isEmpty())
10455 )
10456 )
10457 {
10458 Assert(!mSSData->strStateFilePath.isEmpty());
10459 /* try to make the file name relative to the settings file dir */
10460 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10461 }
10462 else
10463 {
10464 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10465 config.strStateFile.setNull();
10466 }
10467
10468 if (mData->mCurrentSnapshot)
10469 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10470 else
10471 config.uuidCurrentSnapshot.clear();
10472
10473 config.timeLastStateChange = mData->mLastStateChange;
10474 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10475 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10476
10477 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10478 if (FAILED(rc)) throw rc;
10479
10480 rc = saveStorageControllers(config.storageMachine);
10481 if (FAILED(rc)) throw rc;
10482
10483 // save machine's media registry if this is VirtualBox 4.0 or later
10484 if (config.canHaveOwnMediaRegistry())
10485 {
10486 // determine machine folder
10487 Utf8Str strMachineFolder = getSettingsFileFull();
10488 strMachineFolder.stripFilename();
10489 mParent->saveMediaRegistry(config.mediaRegistry,
10490 getId(), // only media with registry ID == machine UUID
10491 strMachineFolder);
10492 // this throws HRESULT
10493 }
10494
10495 // save snapshots
10496 rc = saveAllSnapshots(config);
10497 if (FAILED(rc)) throw rc;
10498}
10499
10500/**
10501 * Saves all snapshots of the machine into the given machine config file. Called
10502 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10503 * @param config
10504 * @return
10505 */
10506HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10507{
10508 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10509
10510 HRESULT rc = S_OK;
10511
10512 try
10513 {
10514 config.llFirstSnapshot.clear();
10515
10516 if (mData->mFirstSnapshot)
10517 {
10518 settings::Snapshot snapNew;
10519 config.llFirstSnapshot.push_back(snapNew);
10520
10521 // get reference to the fresh copy of the snapshot on the list and
10522 // work on that copy directly to avoid excessive copying later
10523 settings::Snapshot &snap = config.llFirstSnapshot.front();
10524
10525 rc = mData->mFirstSnapshot->i_saveSnapshot(snap, false /*aAttrsOnly*/);
10526 if (FAILED(rc)) throw rc;
10527 }
10528
10529// if (mType == IsSessionMachine)
10530// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10531
10532 }
10533 catch (HRESULT err)
10534 {
10535 /* we assume that error info is set by the thrower */
10536 rc = err;
10537 }
10538 catch (...)
10539 {
10540 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10541 }
10542
10543 return rc;
10544}
10545
10546/**
10547 * Saves the VM hardware configuration. It is assumed that the
10548 * given node is empty.
10549 *
10550 * @param data Reference to the settings object for the hardware config.
10551 * @param pDbg Pointer to the settings object for the debugging config
10552 * which happens to live in mHWData.
10553 * @param pAutostart Pointer to the settings object for the autostart config
10554 * which happens to live in mHWData.
10555 */
10556HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10557 settings::Autostart *pAutostart)
10558{
10559 HRESULT rc = S_OK;
10560
10561 try
10562 {
10563 /* The hardware version attribute (optional).
10564 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10565 if ( mHWData->mHWVersion == "1"
10566 && mSSData->strStateFilePath.isEmpty()
10567 )
10568 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. */
10569
10570 data.strVersion = mHWData->mHWVersion;
10571 data.uuid = mHWData->mHardwareUUID;
10572
10573 // CPU
10574 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10575 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10576 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10577 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10578 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10579 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10580 data.fPAE = !!mHWData->mPAEEnabled;
10581 data.enmLongMode = mHWData->mLongMode;
10582 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10583 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10584
10585 /* Standard and Extended CPUID leafs. */
10586 data.llCpuIdLeafs.clear();
10587 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10588 {
10589 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10590 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10591 }
10592 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10593 {
10594 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10595 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10596 }
10597
10598 data.cCPUs = mHWData->mCPUCount;
10599 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10600 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10601
10602 data.llCpus.clear();
10603 if (data.fCpuHotPlug)
10604 {
10605 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10606 {
10607 if (mHWData->mCPUAttached[idx])
10608 {
10609 settings::Cpu cpu;
10610 cpu.ulId = idx;
10611 data.llCpus.push_back(cpu);
10612 }
10613 }
10614 }
10615
10616 // memory
10617 data.ulMemorySizeMB = mHWData->mMemorySize;
10618 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10619
10620 // firmware
10621 data.firmwareType = mHWData->mFirmwareType;
10622
10623 // HID
10624 data.pointingHIDType = mHWData->mPointingHIDType;
10625 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10626
10627 // chipset
10628 data.chipsetType = mHWData->mChipsetType;
10629
10630 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10631
10632 // HPET
10633 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10634
10635 // boot order
10636 data.mapBootOrder.clear();
10637 for (size_t i = 0;
10638 i < RT_ELEMENTS(mHWData->mBootOrder);
10639 ++i)
10640 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10641
10642 // display
10643 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10644 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10645 data.cMonitors = mHWData->mMonitorCount;
10646 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10647 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10648 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10649 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10650 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10651 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10652 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10653 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10654 {
10655 if (mHWData->maVideoCaptureScreens[i])
10656 ASMBitSet(&data.u64VideoCaptureScreens, i);
10657 else
10658 ASMBitClear(&data.u64VideoCaptureScreens, i);
10659 }
10660 /* store relative video capture file if possible */
10661 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10662
10663 /* VRDEServer settings (optional) */
10664 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10665 if (FAILED(rc)) throw rc;
10666
10667 /* BIOS (required) */
10668 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10669 if (FAILED(rc)) throw rc;
10670
10671 /* USB Controller (required) */
10672 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10673 it != mUSBControllers->end();
10674 ++it)
10675 {
10676 ComObjPtr<USBController> ctrl = *it;
10677 settings::USBController settingsCtrl;
10678
10679 settingsCtrl.strName = ctrl->i_getName();
10680 settingsCtrl.enmType = ctrl->i_getControllerType();
10681
10682 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10683 }
10684
10685 /* USB device filters (required) */
10686 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10687 if (FAILED(rc)) throw rc;
10688
10689 /* Network adapters (required) */
10690 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10691 data.llNetworkAdapters.clear();
10692 /* Write out only the nominal number of network adapters for this
10693 * chipset type. Since Machine::commit() hasn't been called there
10694 * may be extra NIC settings in the vector. */
10695 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10696 {
10697 settings::NetworkAdapter nic;
10698 nic.ulSlot = slot;
10699 /* paranoia check... must not be NULL, but must not crash either. */
10700 if (mNetworkAdapters[slot])
10701 {
10702 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10703 if (FAILED(rc)) throw rc;
10704
10705 data.llNetworkAdapters.push_back(nic);
10706 }
10707 }
10708
10709 /* Serial ports */
10710 data.llSerialPorts.clear();
10711 for (ULONG slot = 0;
10712 slot < RT_ELEMENTS(mSerialPorts);
10713 ++slot)
10714 {
10715 settings::SerialPort s;
10716 s.ulSlot = slot;
10717 rc = mSerialPorts[slot]->i_saveSettings(s);
10718 if (FAILED(rc)) return rc;
10719
10720 data.llSerialPorts.push_back(s);
10721 }
10722
10723 /* Parallel ports */
10724 data.llParallelPorts.clear();
10725 for (ULONG slot = 0;
10726 slot < RT_ELEMENTS(mParallelPorts);
10727 ++slot)
10728 {
10729 settings::ParallelPort p;
10730 p.ulSlot = slot;
10731 rc = mParallelPorts[slot]->i_saveSettings(p);
10732 if (FAILED(rc)) return rc;
10733
10734 data.llParallelPorts.push_back(p);
10735 }
10736
10737 /* Audio adapter */
10738 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10739 if (FAILED(rc)) return rc;
10740
10741 /* Shared folders */
10742 data.llSharedFolders.clear();
10743 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10744 it != mHWData->mSharedFolders.end();
10745 ++it)
10746 {
10747 SharedFolder *pSF = *it;
10748 AutoCaller sfCaller(pSF);
10749 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10750 settings::SharedFolder sf;
10751 sf.strName = pSF->getName();
10752 sf.strHostPath = pSF->getHostPath();
10753 sf.fWritable = !!pSF->isWritable();
10754 sf.fAutoMount = !!pSF->isAutoMounted();
10755
10756 data.llSharedFolders.push_back(sf);
10757 }
10758
10759 // clipboard
10760 data.clipboardMode = mHWData->mClipboardMode;
10761
10762 // drag'n'drop
10763 data.dragAndDropMode = mHWData->mDragAndDropMode;
10764
10765 /* Guest */
10766 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10767
10768 // IO settings
10769 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10770 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10771
10772 /* BandwidthControl (required) */
10773 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10774 if (FAILED(rc)) throw rc;
10775
10776 /* Host PCI devices */
10777 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10778 it != mHWData->mPCIDeviceAssignments.end();
10779 ++it)
10780 {
10781 ComObjPtr<PCIDeviceAttachment> pda = *it;
10782 settings::HostPCIDeviceAttachment hpda;
10783
10784 rc = pda->saveSettings(hpda);
10785 if (FAILED(rc)) throw rc;
10786
10787 data.pciAttachments.push_back(hpda);
10788 }
10789
10790
10791 // guest properties
10792 data.llGuestProperties.clear();
10793#ifdef VBOX_WITH_GUEST_PROPS
10794 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10795 it != mHWData->mGuestProperties.end();
10796 ++it)
10797 {
10798 HWData::GuestProperty property = it->second;
10799
10800 /* Remove transient guest properties at shutdown unless we
10801 * are saving state */
10802 if ( ( mData->mMachineState == MachineState_PoweredOff
10803 || mData->mMachineState == MachineState_Aborted
10804 || mData->mMachineState == MachineState_Teleported)
10805 && ( property.mFlags & guestProp::TRANSIENT
10806 || property.mFlags & guestProp::TRANSRESET))
10807 continue;
10808 settings::GuestProperty prop;
10809 prop.strName = it->first;
10810 prop.strValue = property.strValue;
10811 prop.timestamp = property.mTimestamp;
10812 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10813 guestProp::writeFlags(property.mFlags, szFlags);
10814 prop.strFlags = szFlags;
10815
10816 data.llGuestProperties.push_back(prop);
10817 }
10818
10819 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10820 /* I presume this doesn't require a backup(). */
10821 mData->mGuestPropertiesModified = FALSE;
10822#endif /* VBOX_WITH_GUEST_PROPS defined */
10823
10824 *pDbg = mHWData->mDebugging;
10825 *pAutostart = mHWData->mAutostart;
10826
10827 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10828 }
10829 catch(std::bad_alloc &)
10830 {
10831 return E_OUTOFMEMORY;
10832 }
10833
10834 AssertComRC(rc);
10835 return rc;
10836}
10837
10838/**
10839 * Saves the storage controller configuration.
10840 *
10841 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10842 */
10843HRESULT Machine::saveStorageControllers(settings::Storage &data)
10844{
10845 data.llStorageControllers.clear();
10846
10847 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10848 it != mStorageControllers->end();
10849 ++it)
10850 {
10851 HRESULT rc;
10852 ComObjPtr<StorageController> pCtl = *it;
10853
10854 settings::StorageController ctl;
10855 ctl.strName = pCtl->i_getName();
10856 ctl.controllerType = pCtl->i_getControllerType();
10857 ctl.storageBus = pCtl->i_getStorageBus();
10858 ctl.ulInstance = pCtl->i_getInstance();
10859 ctl.fBootable = pCtl->i_getBootable();
10860
10861 /* Save the port count. */
10862 ULONG portCount;
10863 rc = pCtl->COMGETTER(PortCount)(&portCount);
10864 ComAssertComRCRet(rc, rc);
10865 ctl.ulPortCount = portCount;
10866
10867 /* Save fUseHostIOCache */
10868 BOOL fUseHostIOCache;
10869 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10870 ComAssertComRCRet(rc, rc);
10871 ctl.fUseHostIOCache = !!fUseHostIOCache;
10872
10873 /* Save IDE emulation settings. */
10874 if (ctl.controllerType == StorageControllerType_IntelAhci)
10875 {
10876 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10877 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10878 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10879 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10880 )
10881 ComAssertComRCRet(rc, rc);
10882 }
10883
10884 /* save the devices now. */
10885 rc = saveStorageDevices(pCtl, ctl);
10886 ComAssertComRCRet(rc, rc);
10887
10888 data.llStorageControllers.push_back(ctl);
10889 }
10890
10891 return S_OK;
10892}
10893
10894/**
10895 * Saves the hard disk configuration.
10896 */
10897HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10898 settings::StorageController &data)
10899{
10900 MediaData::AttachmentList atts;
10901
10902 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->i_getName()).raw(), atts);
10903 if (FAILED(rc)) return rc;
10904
10905 data.llAttachedDevices.clear();
10906 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10907 it != atts.end();
10908 ++it)
10909 {
10910 settings::AttachedDevice dev;
10911
10912 MediumAttachment *pAttach = *it;
10913 Medium *pMedium = pAttach->i_getMedium();
10914
10915 dev.deviceType = pAttach->i_getType();
10916 dev.lPort = pAttach->i_getPort();
10917 dev.lDevice = pAttach->i_getDevice();
10918 dev.fPassThrough = pAttach->i_getPassthrough();
10919 dev.fHotPluggable = pAttach->i_getHotPluggable();
10920 if (pMedium)
10921 {
10922 if (pMedium->i_isHostDrive())
10923 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10924 else
10925 dev.uuid = pMedium->i_getId();
10926 dev.fTempEject = pAttach->i_getTempEject();
10927 dev.fNonRotational = pAttach->i_getNonRotational();
10928 dev.fDiscard = pAttach->i_getDiscard();
10929 }
10930
10931 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10932
10933 data.llAttachedDevices.push_back(dev);
10934 }
10935
10936 return S_OK;
10937}
10938
10939/**
10940 * Saves machine state settings as defined by aFlags
10941 * (SaveSTS_* values).
10942 *
10943 * @param aFlags Combination of SaveSTS_* flags.
10944 *
10945 * @note Locks objects for writing.
10946 */
10947HRESULT Machine::saveStateSettings(int aFlags)
10948{
10949 if (aFlags == 0)
10950 return S_OK;
10951
10952 AutoCaller autoCaller(this);
10953 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10954
10955 /* This object's write lock is also necessary to serialize file access
10956 * (prevent concurrent reads and writes) */
10957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10958
10959 HRESULT rc = S_OK;
10960
10961 Assert(mData->pMachineConfigFile);
10962
10963 try
10964 {
10965 if (aFlags & SaveSTS_CurStateModified)
10966 mData->pMachineConfigFile->fCurrentStateModified = true;
10967
10968 if (aFlags & SaveSTS_StateFilePath)
10969 {
10970 if (!mSSData->strStateFilePath.isEmpty())
10971 /* try to make the file name relative to the settings file dir */
10972 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10973 else
10974 mData->pMachineConfigFile->strStateFile.setNull();
10975 }
10976
10977 if (aFlags & SaveSTS_StateTimeStamp)
10978 {
10979 Assert( mData->mMachineState != MachineState_Aborted
10980 || mSSData->strStateFilePath.isEmpty());
10981
10982 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10983
10984 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10985//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10986 }
10987
10988 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10989 }
10990 catch (...)
10991 {
10992 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10993 }
10994
10995 return rc;
10996}
10997
10998/**
10999 * Ensures that the given medium is added to a media registry. If this machine
11000 * was created with 4.0 or later, then the machine registry is used. Otherwise
11001 * the global VirtualBox media registry is used.
11002 *
11003 * Caller must NOT hold machine lock, media tree or any medium locks!
11004 *
11005 * @param pMedium
11006 */
11007void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11008{
11009 /* Paranoia checks: do not hold machine or media tree locks. */
11010 AssertReturnVoid(!isWriteLockOnCurrentThread());
11011 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11012
11013 ComObjPtr<Medium> pBase;
11014 {
11015 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11016 pBase = pMedium->i_getBase();
11017 }
11018
11019 /* Paranoia checks: do not hold medium locks. */
11020 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11021 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11022
11023 // decide which medium registry to use now that the medium is attached:
11024 Guid uuid;
11025 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
11026 // machine XML is VirtualBox 4.0 or higher:
11027 uuid = getId(); // machine UUID
11028 else
11029 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
11030
11031 if (pMedium->i_addRegistry(uuid, false /* fRecurse */))
11032 mParent->markRegistryModified(uuid);
11033
11034 /* For more complex hard disk structures it can happen that the base
11035 * medium isn't yet associated with any medium registry. Do that now. */
11036 if (pMedium != pBase)
11037 {
11038 if (pBase->i_addRegistry(uuid, true /* fRecurse */))
11039 mParent->markRegistryModified(uuid);
11040 }
11041}
11042
11043/**
11044 * Creates differencing hard disks for all normal hard disks attached to this
11045 * machine and a new set of attachments to refer to created disks.
11046 *
11047 * Used when taking a snapshot or when deleting the current state. Gets called
11048 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11049 *
11050 * This method assumes that mMediaData contains the original hard disk attachments
11051 * it needs to create diffs for. On success, these attachments will be replaced
11052 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
11053 * called to delete created diffs which will also rollback mMediaData and restore
11054 * whatever was backed up before calling this method.
11055 *
11056 * Attachments with non-normal hard disks are left as is.
11057 *
11058 * If @a aOnline is @c false then the original hard disks that require implicit
11059 * diffs will be locked for reading. Otherwise it is assumed that they are
11060 * already locked for writing (when the VM was started). Note that in the latter
11061 * case it is responsibility of the caller to lock the newly created diffs for
11062 * writing if this method succeeds.
11063 *
11064 * @param aProgress Progress object to run (must contain at least as
11065 * many operations left as the number of hard disks
11066 * attached).
11067 * @param aOnline Whether the VM was online prior to this operation.
11068 *
11069 * @note The progress object is not marked as completed, neither on success nor
11070 * on failure. This is a responsibility of the caller.
11071 *
11072 * @note Locks this object and the media tree for writing.
11073 */
11074HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
11075 ULONG aWeight,
11076 bool aOnline)
11077{
11078 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11079
11080 AutoCaller autoCaller(this);
11081 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11082
11083 AutoMultiWriteLock2 alock(this->lockHandle(),
11084 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11085
11086 /* must be in a protective state because we release the lock below */
11087 AssertReturn( mData->mMachineState == MachineState_Saving
11088 || mData->mMachineState == MachineState_LiveSnapshotting
11089 || mData->mMachineState == MachineState_RestoringSnapshot
11090 || mData->mMachineState == MachineState_DeletingSnapshot
11091 , E_FAIL);
11092
11093 HRESULT rc = S_OK;
11094
11095 // use appropriate locked media map (online or offline)
11096 MediumLockListMap lockedMediaOffline;
11097 MediumLockListMap *lockedMediaMap;
11098 if (aOnline)
11099 lockedMediaMap = &mData->mSession.mLockedMedia;
11100 else
11101 lockedMediaMap = &lockedMediaOffline;
11102
11103 try
11104 {
11105 if (!aOnline)
11106 {
11107 /* lock all attached hard disks early to detect "in use"
11108 * situations before creating actual diffs */
11109 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11110 it != mMediaData->mAttachments.end();
11111 ++it)
11112 {
11113 MediumAttachment* pAtt = *it;
11114 if (pAtt->i_getType() == DeviceType_HardDisk)
11115 {
11116 Medium* pMedium = pAtt->i_getMedium();
11117 Assert(pMedium);
11118
11119 MediumLockList *pMediumLockList(new MediumLockList());
11120 alock.release();
11121 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11122 false /* fMediumLockWrite */,
11123 NULL,
11124 *pMediumLockList);
11125 alock.acquire();
11126 if (FAILED(rc))
11127 {
11128 delete pMediumLockList;
11129 throw rc;
11130 }
11131 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11132 if (FAILED(rc))
11133 {
11134 throw setError(rc,
11135 tr("Collecting locking information for all attached media failed"));
11136 }
11137 }
11138 }
11139
11140 /* Now lock all media. If this fails, nothing is locked. */
11141 alock.release();
11142 rc = lockedMediaMap->Lock();
11143 alock.acquire();
11144 if (FAILED(rc))
11145 {
11146 throw setError(rc,
11147 tr("Locking of attached media failed"));
11148 }
11149 }
11150
11151 /* remember the current list (note that we don't use backup() since
11152 * mMediaData may be already backed up) */
11153 MediaData::AttachmentList atts = mMediaData->mAttachments;
11154
11155 /* start from scratch */
11156 mMediaData->mAttachments.clear();
11157
11158 /* go through remembered attachments and create diffs for normal hard
11159 * disks and attach them */
11160 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11161 it != atts.end();
11162 ++it)
11163 {
11164 MediumAttachment* pAtt = *it;
11165
11166 DeviceType_T devType = pAtt->i_getType();
11167 Medium* pMedium = pAtt->i_getMedium();
11168
11169 if ( devType != DeviceType_HardDisk
11170 || pMedium == NULL
11171 || pMedium->i_getType() != MediumType_Normal)
11172 {
11173 /* copy the attachment as is */
11174
11175 /** @todo the progress object created in Console::TakeSnaphot
11176 * only expects operations for hard disks. Later other
11177 * device types need to show up in the progress as well. */
11178 if (devType == DeviceType_HardDisk)
11179 {
11180 if (pMedium == NULL)
11181 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11182 aWeight); // weight
11183 else
11184 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11185 pMedium->i_getBase()->i_getName().c_str()).raw(),
11186 aWeight); // weight
11187 }
11188
11189 mMediaData->mAttachments.push_back(pAtt);
11190 continue;
11191 }
11192
11193 /* need a diff */
11194 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11195 pMedium->i_getBase()->i_getName().c_str()).raw(),
11196 aWeight); // weight
11197
11198 Utf8Str strFullSnapshotFolder;
11199 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11200
11201 ComObjPtr<Medium> diff;
11202 diff.createObject();
11203 // store the diff in the same registry as the parent
11204 // (this cannot fail here because we can't create implicit diffs for
11205 // unregistered images)
11206 Guid uuidRegistryParent;
11207 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11208 Assert(fInRegistry); NOREF(fInRegistry);
11209 rc = diff->init(mParent,
11210 pMedium->i_getPreferredDiffFormat(),
11211 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11212 uuidRegistryParent);
11213 if (FAILED(rc)) throw rc;
11214
11215 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11216 * the push_back? Looks like we're going to release medium with the
11217 * wrong kind of lock (general issue with if we fail anywhere at all)
11218 * and an orphaned VDI in the snapshots folder. */
11219
11220 /* update the appropriate lock list */
11221 MediumLockList *pMediumLockList;
11222 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11223 AssertComRCThrowRC(rc);
11224 if (aOnline)
11225 {
11226 alock.release();
11227 /* The currently attached medium will be read-only, change
11228 * the lock type to read. */
11229 rc = pMediumLockList->Update(pMedium, false);
11230 alock.acquire();
11231 AssertComRCThrowRC(rc);
11232 }
11233
11234 /* release the locks before the potentially lengthy operation */
11235 alock.release();
11236 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
11237 pMediumLockList,
11238 NULL /* aProgress */,
11239 true /* aWait */);
11240 alock.acquire();
11241 if (FAILED(rc)) throw rc;
11242
11243 /* actual lock list update is done in Medium::commitMedia */
11244
11245 rc = diff->i_addBackReference(mData->mUuid);
11246 AssertComRCThrowRC(rc);
11247
11248 /* add a new attachment */
11249 ComObjPtr<MediumAttachment> attachment;
11250 attachment.createObject();
11251 rc = attachment->init(this,
11252 diff,
11253 pAtt->i_getControllerName(),
11254 pAtt->i_getPort(),
11255 pAtt->i_getDevice(),
11256 DeviceType_HardDisk,
11257 true /* aImplicit */,
11258 false /* aPassthrough */,
11259 false /* aTempEject */,
11260 pAtt->i_getNonRotational(),
11261 pAtt->i_getDiscard(),
11262 pAtt->i_getHotPluggable(),
11263 pAtt->i_getBandwidthGroup());
11264 if (FAILED(rc)) throw rc;
11265
11266 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11267 AssertComRCThrowRC(rc);
11268 mMediaData->mAttachments.push_back(attachment);
11269 }
11270 }
11271 catch (HRESULT aRC) { rc = aRC; }
11272
11273 /* unlock all hard disks we locked when there is no VM */
11274 if (!aOnline)
11275 {
11276 ErrorInfoKeeper eik;
11277
11278 HRESULT rc1 = lockedMediaMap->Clear();
11279 AssertComRC(rc1);
11280 }
11281
11282 return rc;
11283}
11284
11285/**
11286 * Deletes implicit differencing hard disks created either by
11287 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11288 *
11289 * Note that to delete hard disks created by #AttachDevice() this method is
11290 * called from #fixupMedia() when the changes are rolled back.
11291 *
11292 * @note Locks this object and the media tree for writing.
11293 */
11294HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11295{
11296 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11297
11298 AutoCaller autoCaller(this);
11299 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11300
11301 AutoMultiWriteLock2 alock(this->lockHandle(),
11302 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11303
11304 /* We absolutely must have backed up state. */
11305 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11306
11307 /* Check if there are any implicitly created diff images. */
11308 bool fImplicitDiffs = false;
11309 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11310 it != mMediaData->mAttachments.end();
11311 ++it)
11312 {
11313 const ComObjPtr<MediumAttachment> &pAtt = *it;
11314 if (pAtt->i_isImplicit())
11315 {
11316 fImplicitDiffs = true;
11317 break;
11318 }
11319 }
11320 /* If there is nothing to do, leave early. This saves lots of image locking
11321 * effort. It also avoids a MachineStateChanged event without real reason.
11322 * This is important e.g. when loading a VM config, because there should be
11323 * no events. Otherwise API clients can become thoroughly confused for
11324 * inaccessible VMs (the code for loading VM configs uses this method for
11325 * cleanup if the config makes no sense), as they take such events as an
11326 * indication that the VM is alive, and they would force the VM config to
11327 * be reread, leading to an endless loop. */
11328 if (!fImplicitDiffs)
11329 return S_OK;
11330
11331 HRESULT rc = S_OK;
11332 MachineState_T oldState = mData->mMachineState;
11333
11334 /* will release the lock before the potentially lengthy operation,
11335 * so protect with the special state (unless already protected) */
11336 if ( oldState != MachineState_Saving
11337 && oldState != MachineState_LiveSnapshotting
11338 && oldState != MachineState_RestoringSnapshot
11339 && oldState != MachineState_DeletingSnapshot
11340 && oldState != MachineState_DeletingSnapshotOnline
11341 && oldState != MachineState_DeletingSnapshotPaused
11342 )
11343 setMachineState(MachineState_SettingUp);
11344
11345 // use appropriate locked media map (online or offline)
11346 MediumLockListMap lockedMediaOffline;
11347 MediumLockListMap *lockedMediaMap;
11348 if (aOnline)
11349 lockedMediaMap = &mData->mSession.mLockedMedia;
11350 else
11351 lockedMediaMap = &lockedMediaOffline;
11352
11353 try
11354 {
11355 if (!aOnline)
11356 {
11357 /* lock all attached hard disks early to detect "in use"
11358 * situations before deleting actual diffs */
11359 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11360 it != mMediaData->mAttachments.end();
11361 ++it)
11362 {
11363 MediumAttachment* pAtt = *it;
11364 if (pAtt->i_getType() == DeviceType_HardDisk)
11365 {
11366 Medium* pMedium = pAtt->i_getMedium();
11367 Assert(pMedium);
11368
11369 MediumLockList *pMediumLockList(new MediumLockList());
11370 alock.release();
11371 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11372 false /* fMediumLockWrite */,
11373 NULL,
11374 *pMediumLockList);
11375 alock.acquire();
11376
11377 if (FAILED(rc))
11378 {
11379 delete pMediumLockList;
11380 throw rc;
11381 }
11382
11383 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11384 if (FAILED(rc))
11385 throw rc;
11386 }
11387 }
11388
11389 if (FAILED(rc))
11390 throw rc;
11391 } // end of offline
11392
11393 /* Lock lists are now up to date and include implicitly created media */
11394
11395 /* Go through remembered attachments and delete all implicitly created
11396 * diffs and fix up the attachment information */
11397 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11398 MediaData::AttachmentList implicitAtts;
11399 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11400 it != mMediaData->mAttachments.end();
11401 ++it)
11402 {
11403 ComObjPtr<MediumAttachment> pAtt = *it;
11404 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11405 if (pMedium.isNull())
11406 continue;
11407
11408 // Implicit attachments go on the list for deletion and back references are removed.
11409 if (pAtt->i_isImplicit())
11410 {
11411 /* Deassociate and mark for deletion */
11412 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11413 rc = pMedium->i_removeBackReference(mData->mUuid);
11414 if (FAILED(rc))
11415 throw rc;
11416 implicitAtts.push_back(pAtt);
11417 continue;
11418 }
11419
11420 /* Was this medium attached before? */
11421 if (!findAttachment(oldAtts, pMedium))
11422 {
11423 /* no: de-associate */
11424 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11425 rc = pMedium->i_removeBackReference(mData->mUuid);
11426 if (FAILED(rc))
11427 throw rc;
11428 continue;
11429 }
11430 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11431 }
11432
11433 /* If there are implicit attachments to delete, throw away the lock
11434 * map contents (which will unlock all media) since the medium
11435 * attachments will be rolled back. Below we need to completely
11436 * recreate the lock map anyway since it is infinitely complex to
11437 * do this incrementally (would need reconstructing each attachment
11438 * change, which would be extremely hairy). */
11439 if (implicitAtts.size() != 0)
11440 {
11441 ErrorInfoKeeper eik;
11442
11443 HRESULT rc1 = lockedMediaMap->Clear();
11444 AssertComRC(rc1);
11445 }
11446
11447 /* rollback hard disk changes */
11448 mMediaData.rollback();
11449
11450 MultiResult mrc(S_OK);
11451
11452 // Delete unused implicit diffs.
11453 if (implicitAtts.size() != 0)
11454 {
11455 alock.release();
11456
11457 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11458 it != implicitAtts.end();
11459 ++it)
11460 {
11461 // Remove medium associated with this attachment.
11462 ComObjPtr<MediumAttachment> pAtt = *it;
11463 Assert(pAtt);
11464 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11465 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11466 Assert(pMedium);
11467
11468 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11469 // continue on delete failure, just collect error messages
11470 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(), pMedium->i_getLocationFull().c_str() ));
11471 mrc = rc;
11472 }
11473
11474 alock.acquire();
11475
11476 /* if there is a VM recreate media lock map as mentioned above,
11477 * otherwise it is a waste of time and we leave things unlocked */
11478 if (aOnline)
11479 {
11480 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11481 /* must never be NULL, but better safe than sorry */
11482 if (!pMachine.isNull())
11483 {
11484 alock.release();
11485 rc = mData->mSession.mMachine->lockMedia();
11486 alock.acquire();
11487 if (FAILED(rc))
11488 throw rc;
11489 }
11490 }
11491 }
11492 }
11493 catch (HRESULT aRC) {rc = aRC;}
11494
11495 if (mData->mMachineState == MachineState_SettingUp)
11496 setMachineState(oldState);
11497
11498 /* unlock all hard disks we locked when there is no VM */
11499 if (!aOnline)
11500 {
11501 ErrorInfoKeeper eik;
11502
11503 HRESULT rc1 = lockedMediaMap->Clear();
11504 AssertComRC(rc1);
11505 }
11506
11507 return rc;
11508}
11509
11510
11511/**
11512 * Looks through the given list of media attachments for one with the given parameters
11513 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11514 * can be searched as well if needed.
11515 *
11516 * @param list
11517 * @param aControllerName
11518 * @param aControllerPort
11519 * @param aDevice
11520 * @return
11521 */
11522MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11523 IN_BSTR aControllerName,
11524 LONG aControllerPort,
11525 LONG aDevice)
11526{
11527 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11528 it != ll.end();
11529 ++it)
11530 {
11531 MediumAttachment *pAttach = *it;
11532 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11533 return pAttach;
11534 }
11535
11536 return NULL;
11537}
11538
11539/**
11540 * Looks through the given list of media attachments for one with the given parameters
11541 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11542 * can be searched as well if needed.
11543 *
11544 * @param list
11545 * @param aControllerName
11546 * @param aControllerPort
11547 * @param aDevice
11548 * @return
11549 */
11550MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11551 ComObjPtr<Medium> pMedium)
11552{
11553 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11554 it != ll.end();
11555 ++it)
11556 {
11557 MediumAttachment *pAttach = *it;
11558 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11559 if (pMediumThis == pMedium)
11560 return pAttach;
11561 }
11562
11563 return NULL;
11564}
11565
11566/**
11567 * Looks through the given list of media attachments for one with the given parameters
11568 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11569 * can be searched as well if needed.
11570 *
11571 * @param list
11572 * @param aControllerName
11573 * @param aControllerPort
11574 * @param aDevice
11575 * @return
11576 */
11577MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11578 Guid &id)
11579{
11580 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11581 it != ll.end();
11582 ++it)
11583 {
11584 MediumAttachment *pAttach = *it;
11585 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11586 if (pMediumThis->i_getId() == id)
11587 return pAttach;
11588 }
11589
11590 return NULL;
11591}
11592
11593/**
11594 * Main implementation for Machine::DetachDevice. This also gets called
11595 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11596 *
11597 * @param pAttach Medium attachment to detach.
11598 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11599 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11600 * @return
11601 */
11602HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11603 AutoWriteLock &writeLock,
11604 Snapshot *pSnapshot)
11605{
11606 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11607 DeviceType_T mediumType = pAttach->i_getType();
11608
11609 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11610
11611 if (pAttach->i_isImplicit())
11612 {
11613 /* attempt to implicitly delete the implicitly created diff */
11614
11615 /// @todo move the implicit flag from MediumAttachment to Medium
11616 /// and forbid any hard disk operation when it is implicit. Or maybe
11617 /// a special media state for it to make it even more simple.
11618
11619 Assert(mMediaData.isBackedUp());
11620
11621 /* will release the lock before the potentially lengthy operation, so
11622 * protect with the special state */
11623 MachineState_T oldState = mData->mMachineState;
11624 setMachineState(MachineState_SettingUp);
11625
11626 writeLock.release();
11627
11628 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11629 true /*aWait*/);
11630
11631 writeLock.acquire();
11632
11633 setMachineState(oldState);
11634
11635 if (FAILED(rc)) return rc;
11636 }
11637
11638 setModified(IsModified_Storage);
11639 mMediaData.backup();
11640 mMediaData->mAttachments.remove(pAttach);
11641
11642 if (!oldmedium.isNull())
11643 {
11644 // if this is from a snapshot, do not defer detachment to commitMedia()
11645 if (pSnapshot)
11646 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11647 // else if non-hard disk media, do not defer detachment to commitMedia() either
11648 else if (mediumType != DeviceType_HardDisk)
11649 oldmedium->i_removeBackReference(mData->mUuid);
11650 }
11651
11652 return S_OK;
11653}
11654
11655/**
11656 * Goes thru all media of the given list and
11657 *
11658 * 1) calls detachDevice() on each of them for this machine and
11659 * 2) adds all Medium objects found in the process to the given list,
11660 * depending on cleanupMode.
11661 *
11662 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11663 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11664 * media to the list.
11665 *
11666 * This gets called from Machine::Unregister, both for the actual Machine and
11667 * the SnapshotMachine objects that might be found in the snapshots.
11668 *
11669 * Requires caller and locking. The machine lock must be passed in because it
11670 * will be passed on to detachDevice which needs it for temporary unlocking.
11671 *
11672 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11673 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11674 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11675 * otherwise no media get added.
11676 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11677 * @return
11678 */
11679HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11680 Snapshot *pSnapshot,
11681 CleanupMode_T cleanupMode,
11682 MediaList &llMedia)
11683{
11684 Assert(isWriteLockOnCurrentThread());
11685
11686 HRESULT rc;
11687
11688 // make a temporary list because detachDevice invalidates iterators into
11689 // mMediaData->mAttachments
11690 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11691
11692 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11693 it != llAttachments2.end();
11694 ++it)
11695 {
11696 ComObjPtr<MediumAttachment> &pAttach = *it;
11697 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11698
11699 if (!pMedium.isNull())
11700 {
11701 AutoCaller mac(pMedium);
11702 if (FAILED(mac.rc())) return mac.rc();
11703 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11704 DeviceType_T devType = pMedium->i_getDeviceType();
11705 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11706 && devType == DeviceType_HardDisk)
11707 || (cleanupMode == CleanupMode_Full)
11708 )
11709 {
11710 llMedia.push_back(pMedium);
11711 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11712 /*
11713 * Search for medias which are not attached to any machine, but
11714 * in the chain to an attached disk. Mediums are only consided
11715 * if they are:
11716 * - have only one child
11717 * - no references to any machines
11718 * - are of normal medium type
11719 */
11720 while (!pParent.isNull())
11721 {
11722 AutoCaller mac1(pParent);
11723 if (FAILED(mac1.rc())) return mac1.rc();
11724 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11725 if (pParent->i_getChildren().size() == 1)
11726 {
11727 if ( pParent->i_getMachineBackRefCount() == 0
11728 && pParent->i_getType() == MediumType_Normal
11729 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11730 llMedia.push_back(pParent);
11731 }
11732 else
11733 break;
11734 pParent = pParent->i_getParent();
11735 }
11736 }
11737 }
11738
11739 // real machine: then we need to use the proper method
11740 rc = detachDevice(pAttach, writeLock, pSnapshot);
11741
11742 if (FAILED(rc))
11743 return rc;
11744 }
11745
11746 return S_OK;
11747}
11748
11749/**
11750 * Perform deferred hard disk detachments.
11751 *
11752 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11753 * backed up).
11754 *
11755 * If @a aOnline is @c true then this method will also unlock the old hard disks
11756 * for which the new implicit diffs were created and will lock these new diffs for
11757 * writing.
11758 *
11759 * @param aOnline Whether the VM was online prior to this operation.
11760 *
11761 * @note Locks this object for writing!
11762 */
11763void Machine::commitMedia(bool aOnline /*= false*/)
11764{
11765 AutoCaller autoCaller(this);
11766 AssertComRCReturnVoid(autoCaller.rc());
11767
11768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11769
11770 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11771
11772 HRESULT rc = S_OK;
11773
11774 /* no attach/detach operations -- nothing to do */
11775 if (!mMediaData.isBackedUp())
11776 return;
11777
11778 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11779 bool fMediaNeedsLocking = false;
11780
11781 /* enumerate new attachments */
11782 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11783 it != mMediaData->mAttachments.end();
11784 ++it)
11785 {
11786 MediumAttachment *pAttach = *it;
11787
11788 pAttach->i_commit();
11789
11790 Medium* pMedium = pAttach->i_getMedium();
11791 bool fImplicit = pAttach->i_isImplicit();
11792
11793 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11794 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11795 fImplicit));
11796
11797 /** @todo convert all this Machine-based voodoo to MediumAttachment
11798 * based commit logic. */
11799 if (fImplicit)
11800 {
11801 /* convert implicit attachment to normal */
11802 pAttach->i_setImplicit(false);
11803
11804 if ( aOnline
11805 && pMedium
11806 && pAttach->i_getType() == DeviceType_HardDisk
11807 )
11808 {
11809 ComObjPtr<Medium> parent = pMedium->i_getParent();
11810 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11811
11812 /* update the appropriate lock list */
11813 MediumLockList *pMediumLockList;
11814 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11815 AssertComRC(rc);
11816 if (pMediumLockList)
11817 {
11818 /* unlock if there's a need to change the locking */
11819 if (!fMediaNeedsLocking)
11820 {
11821 rc = mData->mSession.mLockedMedia.Unlock();
11822 AssertComRC(rc);
11823 fMediaNeedsLocking = true;
11824 }
11825 rc = pMediumLockList->Update(parent, false);
11826 AssertComRC(rc);
11827 rc = pMediumLockList->Append(pMedium, true);
11828 AssertComRC(rc);
11829 }
11830 }
11831
11832 continue;
11833 }
11834
11835 if (pMedium)
11836 {
11837 /* was this medium attached before? */
11838 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11839 oldIt != oldAtts.end();
11840 ++oldIt)
11841 {
11842 MediumAttachment *pOldAttach = *oldIt;
11843 if (pOldAttach->i_getMedium() == pMedium)
11844 {
11845 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11846
11847 /* yes: remove from old to avoid de-association */
11848 oldAtts.erase(oldIt);
11849 break;
11850 }
11851 }
11852 }
11853 }
11854
11855 /* enumerate remaining old attachments and de-associate from the
11856 * current machine state */
11857 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11858 it != oldAtts.end();
11859 ++it)
11860 {
11861 MediumAttachment *pAttach = *it;
11862 Medium* pMedium = pAttach->i_getMedium();
11863
11864 /* Detach only hard disks, since DVD/floppy media is detached
11865 * instantly in MountMedium. */
11866 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11867 {
11868 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11869
11870 /* now de-associate from the current machine state */
11871 rc = pMedium->i_removeBackReference(mData->mUuid);
11872 AssertComRC(rc);
11873
11874 if (aOnline)
11875 {
11876 /* unlock since medium is not used anymore */
11877 MediumLockList *pMediumLockList;
11878 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11879 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11880 {
11881 /* this happens for online snapshots, there the attachment
11882 * is changing, but only to a diff image created under
11883 * the old one, so there is no separate lock list */
11884 Assert(!pMediumLockList);
11885 }
11886 else
11887 {
11888 AssertComRC(rc);
11889 if (pMediumLockList)
11890 {
11891 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11892 AssertComRC(rc);
11893 }
11894 }
11895 }
11896 }
11897 }
11898
11899 /* take media locks again so that the locking state is consistent */
11900 if (fMediaNeedsLocking)
11901 {
11902 Assert(aOnline);
11903 rc = mData->mSession.mLockedMedia.Lock();
11904 AssertComRC(rc);
11905 }
11906
11907 /* commit the hard disk changes */
11908 mMediaData.commit();
11909
11910 if (isSessionMachine())
11911 {
11912 /*
11913 * Update the parent machine to point to the new owner.
11914 * This is necessary because the stored parent will point to the
11915 * session machine otherwise and cause crashes or errors later
11916 * when the session machine gets invalid.
11917 */
11918 /** @todo Change the MediumAttachment class to behave like any other
11919 * class in this regard by creating peer MediumAttachment
11920 * objects for session machines and share the data with the peer
11921 * machine.
11922 */
11923 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11924 it != mMediaData->mAttachments.end();
11925 ++it)
11926 {
11927 (*it)->i_updateParentMachine(mPeer);
11928 }
11929
11930 /* attach new data to the primary machine and reshare it */
11931 mPeer->mMediaData.attach(mMediaData);
11932 }
11933
11934 return;
11935}
11936
11937/**
11938 * Perform deferred deletion of implicitly created diffs.
11939 *
11940 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11941 * backed up).
11942 *
11943 * @note Locks this object for writing!
11944 */
11945void Machine::rollbackMedia()
11946{
11947 AutoCaller autoCaller(this);
11948 AssertComRCReturnVoid(autoCaller.rc());
11949
11950 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11951 LogFlowThisFunc(("Entering rollbackMedia\n"));
11952
11953 HRESULT rc = S_OK;
11954
11955 /* no attach/detach operations -- nothing to do */
11956 if (!mMediaData.isBackedUp())
11957 return;
11958
11959 /* enumerate new attachments */
11960 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11961 it != mMediaData->mAttachments.end();
11962 ++it)
11963 {
11964 MediumAttachment *pAttach = *it;
11965 /* Fix up the backrefs for DVD/floppy media. */
11966 if (pAttach->i_getType() != DeviceType_HardDisk)
11967 {
11968 Medium* pMedium = pAttach->i_getMedium();
11969 if (pMedium)
11970 {
11971 rc = pMedium->i_removeBackReference(mData->mUuid);
11972 AssertComRC(rc);
11973 }
11974 }
11975
11976 (*it)->i_rollback();
11977
11978 pAttach = *it;
11979 /* Fix up the backrefs for DVD/floppy media. */
11980 if (pAttach->i_getType() != DeviceType_HardDisk)
11981 {
11982 Medium* pMedium = pAttach->i_getMedium();
11983 if (pMedium)
11984 {
11985 rc = pMedium->i_addBackReference(mData->mUuid);
11986 AssertComRC(rc);
11987 }
11988 }
11989 }
11990
11991 /** @todo convert all this Machine-based voodoo to MediumAttachment
11992 * based rollback logic. */
11993 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11994
11995 return;
11996}
11997
11998/**
11999 * Returns true if the settings file is located in the directory named exactly
12000 * as the machine; this means, among other things, that the machine directory
12001 * should be auto-renamed.
12002 *
12003 * @param aSettingsDir if not NULL, the full machine settings file directory
12004 * name will be assigned there.
12005 *
12006 * @note Doesn't lock anything.
12007 * @note Not thread safe (must be called from this object's lock).
12008 */
12009bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12010{
12011 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12012 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12013 if (aSettingsDir)
12014 *aSettingsDir = strMachineDirName;
12015 strMachineDirName.stripPath(); // vmname
12016 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12017 strConfigFileOnly.stripPath() // vmname.vbox
12018 .stripSuffix(); // vmname
12019 /** @todo hack, make somehow use of ComposeMachineFilename */
12020 if (mUserData->s.fDirectoryIncludesUUID)
12021 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
12022
12023 AssertReturn(!strMachineDirName.isEmpty(), false);
12024 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12025
12026 return strMachineDirName == strConfigFileOnly;
12027}
12028
12029/**
12030 * Discards all changes to machine settings.
12031 *
12032 * @param aNotify Whether to notify the direct session about changes or not.
12033 *
12034 * @note Locks objects for writing!
12035 */
12036void Machine::rollback(bool aNotify)
12037{
12038 AutoCaller autoCaller(this);
12039 AssertComRCReturn(autoCaller.rc(), (void)0);
12040
12041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12042
12043 if (!mStorageControllers.isNull())
12044 {
12045 if (mStorageControllers.isBackedUp())
12046 {
12047 /* unitialize all new devices (absent in the backed up list). */
12048 StorageControllerList::const_iterator it = mStorageControllers->begin();
12049 StorageControllerList *backedList = mStorageControllers.backedUpData();
12050 while (it != mStorageControllers->end())
12051 {
12052 if ( std::find(backedList->begin(), backedList->end(), *it)
12053 == backedList->end()
12054 )
12055 {
12056 (*it)->uninit();
12057 }
12058 ++it;
12059 }
12060
12061 /* restore the list */
12062 mStorageControllers.rollback();
12063 }
12064
12065 /* rollback any changes to devices after restoring the list */
12066 if (mData->flModifications & IsModified_Storage)
12067 {
12068 StorageControllerList::const_iterator it = mStorageControllers->begin();
12069 while (it != mStorageControllers->end())
12070 {
12071 (*it)->i_rollback();
12072 ++it;
12073 }
12074 }
12075 }
12076
12077 if (!mUSBControllers.isNull())
12078 {
12079 if (mUSBControllers.isBackedUp())
12080 {
12081 /* unitialize all new devices (absent in the backed up list). */
12082 USBControllerList::const_iterator it = mUSBControllers->begin();
12083 USBControllerList *backedList = mUSBControllers.backedUpData();
12084 while (it != mUSBControllers->end())
12085 {
12086 if ( std::find(backedList->begin(), backedList->end(), *it)
12087 == backedList->end()
12088 )
12089 {
12090 (*it)->uninit();
12091 }
12092 ++it;
12093 }
12094
12095 /* restore the list */
12096 mUSBControllers.rollback();
12097 }
12098
12099 /* rollback any changes to devices after restoring the list */
12100 if (mData->flModifications & IsModified_USB)
12101 {
12102 USBControllerList::const_iterator it = mUSBControllers->begin();
12103 while (it != mUSBControllers->end())
12104 {
12105 (*it)->i_rollback();
12106 ++it;
12107 }
12108 }
12109 }
12110
12111 mUserData.rollback();
12112
12113 mHWData.rollback();
12114
12115 if (mData->flModifications & IsModified_Storage)
12116 rollbackMedia();
12117
12118 if (mBIOSSettings)
12119 mBIOSSettings->i_rollback();
12120
12121 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12122 mVRDEServer->i_rollback();
12123
12124 if (mAudioAdapter)
12125 mAudioAdapter->i_rollback();
12126
12127 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12128 mUSBDeviceFilters->i_rollback();
12129
12130 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12131 mBandwidthControl->i_rollback();
12132
12133 if (!mHWData.isNull())
12134 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12135 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12136 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12137 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12138
12139 if (mData->flModifications & IsModified_NetworkAdapters)
12140 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12141 if ( mNetworkAdapters[slot]
12142 && mNetworkAdapters[slot]->i_isModified())
12143 {
12144 mNetworkAdapters[slot]->i_rollback();
12145 networkAdapters[slot] = mNetworkAdapters[slot];
12146 }
12147
12148 if (mData->flModifications & IsModified_SerialPorts)
12149 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12150 if ( mSerialPorts[slot]
12151 && mSerialPorts[slot]->i_isModified())
12152 {
12153 mSerialPorts[slot]->i_rollback();
12154 serialPorts[slot] = mSerialPorts[slot];
12155 }
12156
12157 if (mData->flModifications & IsModified_ParallelPorts)
12158 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12159 if ( mParallelPorts[slot]
12160 && mParallelPorts[slot]->i_isModified())
12161 {
12162 mParallelPorts[slot]->i_rollback();
12163 parallelPorts[slot] = mParallelPorts[slot];
12164 }
12165
12166 if (aNotify)
12167 {
12168 /* inform the direct session about changes */
12169
12170 ComObjPtr<Machine> that = this;
12171 uint32_t flModifications = mData->flModifications;
12172 alock.release();
12173
12174 if (flModifications & IsModified_SharedFolders)
12175 that->onSharedFolderChange();
12176
12177 if (flModifications & IsModified_VRDEServer)
12178 that->onVRDEServerChange(/* aRestart */ TRUE);
12179 if (flModifications & IsModified_USB)
12180 that->onUSBControllerChange();
12181
12182 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12183 if (networkAdapters[slot])
12184 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12185 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12186 if (serialPorts[slot])
12187 that->onSerialPortChange(serialPorts[slot]);
12188 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12189 if (parallelPorts[slot])
12190 that->onParallelPortChange(parallelPorts[slot]);
12191
12192 if (flModifications & IsModified_Storage)
12193 that->onStorageControllerChange();
12194
12195#if 0
12196 if (flModifications & IsModified_BandwidthControl)
12197 that->onBandwidthControlChange();
12198#endif
12199 }
12200}
12201
12202/**
12203 * Commits all the changes to machine settings.
12204 *
12205 * Note that this operation is supposed to never fail.
12206 *
12207 * @note Locks this object and children for writing.
12208 */
12209void Machine::commit()
12210{
12211 AutoCaller autoCaller(this);
12212 AssertComRCReturnVoid(autoCaller.rc());
12213
12214 AutoCaller peerCaller(mPeer);
12215 AssertComRCReturnVoid(peerCaller.rc());
12216
12217 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12218
12219 /*
12220 * use safe commit to ensure Snapshot machines (that share mUserData)
12221 * will still refer to a valid memory location
12222 */
12223 mUserData.commitCopy();
12224
12225 mHWData.commit();
12226
12227 if (mMediaData.isBackedUp())
12228 commitMedia(Global::IsOnline(mData->mMachineState));
12229
12230 mBIOSSettings->i_commit();
12231 mVRDEServer->i_commit();
12232 mAudioAdapter->i_commit();
12233 mUSBDeviceFilters->i_commit();
12234 mBandwidthControl->i_commit();
12235
12236 /* Since mNetworkAdapters is a list which might have been changed (resized)
12237 * without using the Backupable<> template we need to handle the copying
12238 * of the list entries manually, including the creation of peers for the
12239 * new objects. */
12240 bool commitNetworkAdapters = false;
12241 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12242 if (mPeer)
12243 {
12244 /* commit everything, even the ones which will go away */
12245 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12246 mNetworkAdapters[slot]->i_commit();
12247 /* copy over the new entries, creating a peer and uninit the original */
12248 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12249 for (size_t slot = 0; slot < newSize; slot++)
12250 {
12251 /* look if this adapter has a peer device */
12252 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12253 if (!peer)
12254 {
12255 /* no peer means the adapter is a newly created one;
12256 * create a peer owning data this data share it with */
12257 peer.createObject();
12258 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12259 }
12260 mPeer->mNetworkAdapters[slot] = peer;
12261 }
12262 /* uninit any no longer needed network adapters */
12263 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12264 mNetworkAdapters[slot]->uninit();
12265 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12266 {
12267 if (mPeer->mNetworkAdapters[slot])
12268 mPeer->mNetworkAdapters[slot]->uninit();
12269 }
12270 /* Keep the original network adapter count until this point, so that
12271 * discarding a chipset type change will not lose settings. */
12272 mNetworkAdapters.resize(newSize);
12273 mPeer->mNetworkAdapters.resize(newSize);
12274 }
12275 else
12276 {
12277 /* we have no peer (our parent is the newly created machine);
12278 * just commit changes to the network adapters */
12279 commitNetworkAdapters = true;
12280 }
12281 if (commitNetworkAdapters)
12282 {
12283 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12284 mNetworkAdapters[slot]->i_commit();
12285 }
12286
12287 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12288 mSerialPorts[slot]->i_commit();
12289 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12290 mParallelPorts[slot]->i_commit();
12291
12292 bool commitStorageControllers = false;
12293
12294 if (mStorageControllers.isBackedUp())
12295 {
12296 mStorageControllers.commit();
12297
12298 if (mPeer)
12299 {
12300 /* Commit all changes to new controllers (this will reshare data with
12301 * peers for those who have peers) */
12302 StorageControllerList *newList = new StorageControllerList();
12303 StorageControllerList::const_iterator it = mStorageControllers->begin();
12304 while (it != mStorageControllers->end())
12305 {
12306 (*it)->i_commit();
12307
12308 /* look if this controller has a peer device */
12309 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12310 if (!peer)
12311 {
12312 /* no peer means the device is a newly created one;
12313 * create a peer owning data this device share it with */
12314 peer.createObject();
12315 peer->init(mPeer, *it, true /* aReshare */);
12316 }
12317 else
12318 {
12319 /* remove peer from the old list */
12320 mPeer->mStorageControllers->remove(peer);
12321 }
12322 /* and add it to the new list */
12323 newList->push_back(peer);
12324
12325 ++it;
12326 }
12327
12328 /* uninit old peer's controllers that are left */
12329 it = mPeer->mStorageControllers->begin();
12330 while (it != mPeer->mStorageControllers->end())
12331 {
12332 (*it)->uninit();
12333 ++it;
12334 }
12335
12336 /* attach new list of controllers to our peer */
12337 mPeer->mStorageControllers.attach(newList);
12338 }
12339 else
12340 {
12341 /* we have no peer (our parent is the newly created machine);
12342 * just commit changes to devices */
12343 commitStorageControllers = true;
12344 }
12345 }
12346 else
12347 {
12348 /* the list of controllers itself is not changed,
12349 * just commit changes to controllers themselves */
12350 commitStorageControllers = true;
12351 }
12352
12353 if (commitStorageControllers)
12354 {
12355 StorageControllerList::const_iterator it = mStorageControllers->begin();
12356 while (it != mStorageControllers->end())
12357 {
12358 (*it)->i_commit();
12359 ++it;
12360 }
12361 }
12362
12363 bool commitUSBControllers = false;
12364
12365 if (mUSBControllers.isBackedUp())
12366 {
12367 mUSBControllers.commit();
12368
12369 if (mPeer)
12370 {
12371 /* Commit all changes to new controllers (this will reshare data with
12372 * peers for those who have peers) */
12373 USBControllerList *newList = new USBControllerList();
12374 USBControllerList::const_iterator it = mUSBControllers->begin();
12375 while (it != mUSBControllers->end())
12376 {
12377 (*it)->i_commit();
12378
12379 /* look if this controller has a peer device */
12380 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12381 if (!peer)
12382 {
12383 /* no peer means the device is a newly created one;
12384 * create a peer owning data this device share it with */
12385 peer.createObject();
12386 peer->init(mPeer, *it, true /* aReshare */);
12387 }
12388 else
12389 {
12390 /* remove peer from the old list */
12391 mPeer->mUSBControllers->remove(peer);
12392 }
12393 /* and add it to the new list */
12394 newList->push_back(peer);
12395
12396 ++it;
12397 }
12398
12399 /* uninit old peer's controllers that are left */
12400 it = mPeer->mUSBControllers->begin();
12401 while (it != mPeer->mUSBControllers->end())
12402 {
12403 (*it)->uninit();
12404 ++it;
12405 }
12406
12407 /* attach new list of controllers to our peer */
12408 mPeer->mUSBControllers.attach(newList);
12409 }
12410 else
12411 {
12412 /* we have no peer (our parent is the newly created machine);
12413 * just commit changes to devices */
12414 commitUSBControllers = true;
12415 }
12416 }
12417 else
12418 {
12419 /* the list of controllers itself is not changed,
12420 * just commit changes to controllers themselves */
12421 commitUSBControllers = true;
12422 }
12423
12424 if (commitUSBControllers)
12425 {
12426 USBControllerList::const_iterator it = mUSBControllers->begin();
12427 while (it != mUSBControllers->end())
12428 {
12429 (*it)->i_commit();
12430 ++it;
12431 }
12432 }
12433
12434 if (isSessionMachine())
12435 {
12436 /* attach new data to the primary machine and reshare it */
12437 mPeer->mUserData.attach(mUserData);
12438 mPeer->mHWData.attach(mHWData);
12439 /* mMediaData is reshared by fixupMedia */
12440 // mPeer->mMediaData.attach(mMediaData);
12441 Assert(mPeer->mMediaData.data() == mMediaData.data());
12442 }
12443}
12444
12445/**
12446 * Copies all the hardware data from the given machine.
12447 *
12448 * Currently, only called when the VM is being restored from a snapshot. In
12449 * particular, this implies that the VM is not running during this method's
12450 * call.
12451 *
12452 * @note This method must be called from under this object's lock.
12453 *
12454 * @note This method doesn't call #commit(), so all data remains backed up and
12455 * unsaved.
12456 */
12457void Machine::copyFrom(Machine *aThat)
12458{
12459 AssertReturnVoid(!isSnapshotMachine());
12460 AssertReturnVoid(aThat->isSnapshotMachine());
12461
12462 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12463
12464 mHWData.assignCopy(aThat->mHWData);
12465
12466 // create copies of all shared folders (mHWData after attaching a copy
12467 // contains just references to original objects)
12468 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12469 it != mHWData->mSharedFolders.end();
12470 ++it)
12471 {
12472 ComObjPtr<SharedFolder> folder;
12473 folder.createObject();
12474 HRESULT rc = folder->initCopy(getMachine(), *it);
12475 AssertComRC(rc);
12476 *it = folder;
12477 }
12478
12479 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12480 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12481 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12482 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12483 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12484
12485 /* create private copies of all controllers */
12486 mStorageControllers.backup();
12487 mStorageControllers->clear();
12488 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12489 it != aThat->mStorageControllers->end();
12490 ++it)
12491 {
12492 ComObjPtr<StorageController> ctrl;
12493 ctrl.createObject();
12494 ctrl->initCopy(this, *it);
12495 mStorageControllers->push_back(ctrl);
12496 }
12497
12498 /* create private copies of all USB controllers */
12499 mUSBControllers.backup();
12500 mUSBControllers->clear();
12501 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12502 it != aThat->mUSBControllers->end();
12503 ++it)
12504 {
12505 ComObjPtr<USBController> ctrl;
12506 ctrl.createObject();
12507 ctrl->initCopy(this, *it);
12508 mUSBControllers->push_back(ctrl);
12509 }
12510
12511 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12512 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12513 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12514 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12515 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12516 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12517 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12518}
12519
12520/**
12521 * Returns whether the given storage controller is hotplug capable.
12522 *
12523 * @returns true if the controller supports hotplugging
12524 * false otherwise.
12525 * @param enmCtrlType The controller type to check for.
12526 */
12527bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12528{
12529 ComPtr<ISystemProperties> systemProperties;
12530 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12531 if (FAILED(rc))
12532 return false;
12533
12534 BOOL aHotplugCapable = FALSE;
12535 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12536
12537 return RT_BOOL(aHotplugCapable);
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 lock is needed
12988 // by several calls to it, 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->i_getStateFilePath();
13045 mConsoleTaskData.mSnapshot->uninit();
13046 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
13047 }
13048
13049 mData->mSession.mPID = NIL_RTPROCESS;
13050
13051 if (aReason == Uninit::Unexpected)
13052 {
13053 /* Uninitialization didn't come from #checkForDeath(), so tell the
13054 * client watcher thread to update the set of machines that have open
13055 * sessions. */
13056 mParent->updateClientWatcher();
13057 }
13058
13059 /* uninitialize all remote controls */
13060 if (mData->mSession.mRemoteControls.size())
13061 {
13062 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13063 mData->mSession.mRemoteControls.size()));
13064
13065 Data::Session::RemoteControlList::iterator it =
13066 mData->mSession.mRemoteControls.begin();
13067 while (it != mData->mSession.mRemoteControls.end())
13068 {
13069 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13070 HRESULT rc = (*it)->Uninitialize();
13071 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13072 if (FAILED(rc))
13073 LogWarningThisFunc(("Forgot to close the remote session?\n"));
13074 ++it;
13075 }
13076 mData->mSession.mRemoteControls.clear();
13077 }
13078
13079 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
13080 {
13081 NetworkAttachmentType_T type;
13082 HRESULT hrc;
13083
13084 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13085 if ( SUCCEEDED(hrc)
13086 && type == NetworkAttachmentType_NATNetwork)
13087 {
13088 Bstr name;
13089 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13090 if (SUCCEEDED(hrc))
13091 {
13092 multilock.release();
13093 LogRel(("VM '%s' stops using NAT network '%ls'\n",
13094 mUserData->s.strName.c_str(), name.raw()));
13095 mParent->natNetworkRefDec(name.raw());
13096 multilock.acquire();
13097 }
13098 }
13099 }
13100
13101 /*
13102 * An expected uninitialization can come only from #checkForDeath().
13103 * Otherwise it means that something's gone really wrong (for example,
13104 * the Session implementation has released the VirtualBox reference
13105 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13106 * etc). However, it's also possible, that the client releases the IPC
13107 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13108 * but the VirtualBox release event comes first to the server process.
13109 * This case is practically possible, so we should not assert on an
13110 * unexpected uninit, just log a warning.
13111 */
13112
13113 if ((aReason == Uninit::Unexpected))
13114 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13115
13116 if (aReason != Uninit::Normal)
13117 {
13118 mData->mSession.mDirectControl.setNull();
13119 }
13120 else
13121 {
13122 /* this must be null here (see #OnSessionEnd()) */
13123 Assert(mData->mSession.mDirectControl.isNull());
13124 Assert(mData->mSession.mState == SessionState_Unlocking);
13125 Assert(!mData->mSession.mProgress.isNull());
13126 }
13127 if (mData->mSession.mProgress)
13128 {
13129 if (aReason == Uninit::Normal)
13130 mData->mSession.mProgress->notifyComplete(S_OK);
13131 else
13132 mData->mSession.mProgress->notifyComplete(E_FAIL,
13133 COM_IIDOF(ISession),
13134 getComponentName(),
13135 tr("The VM session was aborted"));
13136 mData->mSession.mProgress.setNull();
13137 }
13138
13139 /* remove the association between the peer machine and this session machine */
13140 Assert( (SessionMachine*)mData->mSession.mMachine == this
13141 || aReason == Uninit::Unexpected);
13142
13143 /* reset the rest of session data */
13144 mData->mSession.mMachine.setNull();
13145 mData->mSession.mState = SessionState_Unlocked;
13146 mData->mSession.mType.setNull();
13147
13148 /* destroy the machine client token before leaving the exclusive lock */
13149 if (mClientToken)
13150 {
13151 delete mClientToken;
13152 mClientToken = NULL;
13153 }
13154
13155 /* fire an event */
13156 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13157
13158 uninitDataAndChildObjects();
13159
13160 /* free the essential data structure last */
13161 mData.free();
13162
13163 /* release the exclusive lock before setting the below two to NULL */
13164 multilock.release();
13165
13166 unconst(mParent) = NULL;
13167 unconst(mPeer) = NULL;
13168
13169 LogFlowThisFuncLeave();
13170}
13171
13172// util::Lockable interface
13173////////////////////////////////////////////////////////////////////////////////
13174
13175/**
13176 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13177 * with the primary Machine instance (mPeer).
13178 */
13179RWLockHandle *SessionMachine::lockHandle() const
13180{
13181 AssertReturn(mPeer != NULL, NULL);
13182 return mPeer->lockHandle();
13183}
13184
13185// IInternalMachineControl methods
13186////////////////////////////////////////////////////////////////////////////////
13187
13188/**
13189 * Passes collected guest statistics to performance collector object
13190 */
13191STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13192 ULONG aCpuKernel, ULONG aCpuIdle,
13193 ULONG aMemTotal, ULONG aMemFree,
13194 ULONG aMemBalloon, ULONG aMemShared,
13195 ULONG aMemCache, ULONG aPageTotal,
13196 ULONG aAllocVMM, ULONG aFreeVMM,
13197 ULONG aBalloonedVMM, ULONG aSharedVMM,
13198 ULONG aVmNetRx, ULONG aVmNetTx)
13199{
13200#ifdef VBOX_WITH_RESOURCE_USAGE_API
13201 if (mCollectorGuest)
13202 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13203 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13204 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13205 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13206
13207 return S_OK;
13208#else
13209 NOREF(aValidStats);
13210 NOREF(aCpuUser);
13211 NOREF(aCpuKernel);
13212 NOREF(aCpuIdle);
13213 NOREF(aMemTotal);
13214 NOREF(aMemFree);
13215 NOREF(aMemBalloon);
13216 NOREF(aMemShared);
13217 NOREF(aMemCache);
13218 NOREF(aPageTotal);
13219 NOREF(aAllocVMM);
13220 NOREF(aFreeVMM);
13221 NOREF(aBalloonedVMM);
13222 NOREF(aSharedVMM);
13223 NOREF(aVmNetRx);
13224 NOREF(aVmNetTx);
13225 return E_NOTIMPL;
13226#endif
13227}
13228
13229/**
13230 * @note Locks this object for writing.
13231 */
13232STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13233{
13234 AutoCaller autoCaller(this);
13235 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13236
13237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13238
13239 mRemoveSavedState = aRemove;
13240
13241 return S_OK;
13242}
13243
13244/**
13245 * @note Locks the same as #setMachineState() does.
13246 */
13247STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13248{
13249 return setMachineState(aMachineState);
13250}
13251
13252/**
13253 * @note Locks this object for writing.
13254 */
13255STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13256{
13257 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13258 AutoCaller autoCaller(this);
13259 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13260
13261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13262
13263 if (mData->mSession.mState != SessionState_Locked)
13264 return VBOX_E_INVALID_OBJECT_STATE;
13265
13266 if (!mData->mSession.mProgress.isNull())
13267 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13268
13269 LogFlowThisFunc(("returns S_OK.\n"));
13270 return S_OK;
13271}
13272
13273/**
13274 * @note Locks this object for writing.
13275 */
13276STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13277{
13278 AutoCaller autoCaller(this);
13279 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13280
13281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13282
13283 if (mData->mSession.mState != SessionState_Locked)
13284 return VBOX_E_INVALID_OBJECT_STATE;
13285
13286 /* Finalize the LaunchVMProcess progress object. */
13287 if (mData->mSession.mProgress)
13288 {
13289 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13290 mData->mSession.mProgress.setNull();
13291 }
13292
13293 if (SUCCEEDED((HRESULT)iResult))
13294 {
13295#ifdef VBOX_WITH_RESOURCE_USAGE_API
13296 /* The VM has been powered up successfully, so it makes sense
13297 * now to offer the performance metrics for a running machine
13298 * object. Doing it earlier wouldn't be safe. */
13299 registerMetrics(mParent->performanceCollector(), mPeer,
13300 mData->mSession.mPID);
13301#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13302 }
13303
13304 return S_OK;
13305}
13306
13307/**
13308 * @note Locks this object for writing.
13309 */
13310STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13311{
13312 LogFlowThisFuncEnter();
13313
13314 CheckComArgOutPointerValid(aProgress);
13315
13316 AutoCaller autoCaller(this);
13317 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13318
13319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13320
13321 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13322 E_FAIL);
13323
13324 /* create a progress object to track operation completion */
13325 ComObjPtr<Progress> pProgress;
13326 pProgress.createObject();
13327 pProgress->init(getVirtualBox(),
13328 static_cast<IMachine *>(this) /* aInitiator */,
13329 Bstr(tr("Stopping the virtual machine")).raw(),
13330 FALSE /* aCancelable */);
13331
13332 /* fill in the console task data */
13333 mConsoleTaskData.mLastState = mData->mMachineState;
13334 mConsoleTaskData.mProgress = pProgress;
13335
13336 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13337 setMachineState(MachineState_Stopping);
13338
13339 pProgress.queryInterfaceTo(aProgress);
13340
13341 return S_OK;
13342}
13343
13344/**
13345 * @note Locks this object for writing.
13346 */
13347STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13348{
13349 LogFlowThisFuncEnter();
13350
13351 AutoCaller autoCaller(this);
13352 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13353
13354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13355
13356 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13357 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13358 && mConsoleTaskData.mLastState != MachineState_Null,
13359 E_FAIL);
13360
13361 /*
13362 * On failure, set the state to the state we had when BeginPoweringDown()
13363 * was called (this is expected by Console::PowerDown() and the associated
13364 * task). On success the VM process already changed the state to
13365 * MachineState_PoweredOff, so no need to do anything.
13366 */
13367 if (FAILED(iResult))
13368 setMachineState(mConsoleTaskData.mLastState);
13369
13370 /* notify the progress object about operation completion */
13371 Assert(mConsoleTaskData.mProgress);
13372 if (SUCCEEDED(iResult))
13373 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13374 else
13375 {
13376 Utf8Str strErrMsg(aErrMsg);
13377 if (strErrMsg.length())
13378 mConsoleTaskData.mProgress->notifyComplete(iResult,
13379 COM_IIDOF(ISession),
13380 getComponentName(),
13381 strErrMsg.c_str());
13382 else
13383 mConsoleTaskData.mProgress->notifyComplete(iResult);
13384 }
13385
13386 /* clear out the temporary saved state data */
13387 mConsoleTaskData.mLastState = MachineState_Null;
13388 mConsoleTaskData.mProgress.setNull();
13389
13390 LogFlowThisFuncLeave();
13391 return S_OK;
13392}
13393
13394
13395/**
13396 * Goes through the USB filters of the given machine to see if the given
13397 * device matches any filter or not.
13398 *
13399 * @note Locks the same as USBController::hasMatchingFilter() does.
13400 */
13401STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13402 BOOL *aMatched,
13403 ULONG *aMaskedIfs)
13404{
13405 LogFlowThisFunc(("\n"));
13406
13407 CheckComArgNotNull(aUSBDevice);
13408 CheckComArgOutPointerValid(aMatched);
13409
13410 AutoCaller autoCaller(this);
13411 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13412
13413#ifdef VBOX_WITH_USB
13414 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aUSBDevice, aMaskedIfs);
13415#else
13416 NOREF(aUSBDevice);
13417 NOREF(aMaskedIfs);
13418 *aMatched = FALSE;
13419#endif
13420
13421 return S_OK;
13422}
13423
13424/**
13425 * @note Locks the same as Host::captureUSBDevice() does.
13426 */
13427STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13428{
13429 LogFlowThisFunc(("\n"));
13430
13431 AutoCaller autoCaller(this);
13432 AssertComRCReturnRC(autoCaller.rc());
13433
13434#ifdef VBOX_WITH_USB
13435 /* if captureDeviceForVM() fails, it must have set extended error info */
13436 clearError();
13437 MultiResult rc = mParent->host()->i_checkUSBProxyService();
13438 if (FAILED(rc)) return rc;
13439
13440 USBProxyService *service = mParent->host()->i_usbProxyService();
13441 AssertReturn(service, E_FAIL);
13442 return service->captureDeviceForVM(this, Guid(aId).ref());
13443#else
13444 NOREF(aId);
13445 return E_NOTIMPL;
13446#endif
13447}
13448
13449/**
13450 * @note Locks the same as Host::detachUSBDevice() does.
13451 */
13452STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13453{
13454 LogFlowThisFunc(("\n"));
13455
13456 AutoCaller autoCaller(this);
13457 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13458
13459#ifdef VBOX_WITH_USB
13460 USBProxyService *service = mParent->host()->i_usbProxyService();
13461 AssertReturn(service, E_FAIL);
13462 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13463#else
13464 NOREF(aId);
13465 NOREF(aDone);
13466 return E_NOTIMPL;
13467#endif
13468}
13469
13470/**
13471 * Inserts all machine filters to the USB proxy service and then calls
13472 * Host::autoCaptureUSBDevices().
13473 *
13474 * Called by Console from the VM process upon VM startup.
13475 *
13476 * @note Locks what called methods lock.
13477 */
13478STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13479{
13480 LogFlowThisFunc(("\n"));
13481
13482 AutoCaller autoCaller(this);
13483 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13484
13485#ifdef VBOX_WITH_USB
13486 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13487 AssertComRC(rc);
13488 NOREF(rc);
13489
13490 USBProxyService *service = mParent->host()->i_usbProxyService();
13491 AssertReturn(service, E_FAIL);
13492 return service->autoCaptureDevicesForVM(this);
13493#else
13494 return S_OK;
13495#endif
13496}
13497
13498/**
13499 * Removes all machine filters from the USB proxy service and then calls
13500 * Host::detachAllUSBDevices().
13501 *
13502 * Called by Console from the VM process upon normal VM termination or by
13503 * SessionMachine::uninit() upon abnormal VM termination (from under the
13504 * Machine/SessionMachine lock).
13505 *
13506 * @note Locks what called methods lock.
13507 */
13508STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13509{
13510 LogFlowThisFunc(("\n"));
13511
13512 AutoCaller autoCaller(this);
13513 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13514
13515#ifdef VBOX_WITH_USB
13516 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13517 AssertComRC(rc);
13518 NOREF(rc);
13519
13520 USBProxyService *service = mParent->host()->i_usbProxyService();
13521 AssertReturn(service, E_FAIL);
13522 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13523#else
13524 NOREF(aDone);
13525 return S_OK;
13526#endif
13527}
13528
13529/**
13530 * @note Locks this object for writing.
13531 */
13532STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13533 IProgress **aProgress)
13534{
13535 LogFlowThisFuncEnter();
13536
13537 AssertReturn(aSession, E_INVALIDARG);
13538 AssertReturn(aProgress, E_INVALIDARG);
13539
13540 AutoCaller autoCaller(this);
13541
13542 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13543 /*
13544 * We don't assert below because it might happen that a non-direct session
13545 * informs us it is closed right after we've been uninitialized -- it's ok.
13546 */
13547 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13548
13549 /* get IInternalSessionControl interface */
13550 ComPtr<IInternalSessionControl> control(aSession);
13551
13552 ComAssertRet(!control.isNull(), E_INVALIDARG);
13553
13554 /* Creating a Progress object requires the VirtualBox lock, and
13555 * thus locking it here is required by the lock order rules. */
13556 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13557
13558 if (control == mData->mSession.mDirectControl)
13559 {
13560 ComAssertRet(aProgress, E_POINTER);
13561
13562 /* The direct session is being normally closed by the client process
13563 * ----------------------------------------------------------------- */
13564
13565 /* go to the closing state (essential for all open*Session() calls and
13566 * for #checkForDeath()) */
13567 Assert(mData->mSession.mState == SessionState_Locked);
13568 mData->mSession.mState = SessionState_Unlocking;
13569
13570 /* set direct control to NULL to release the remote instance */
13571 mData->mSession.mDirectControl.setNull();
13572 LogFlowThisFunc(("Direct control is set to NULL\n"));
13573
13574 if (mData->mSession.mProgress)
13575 {
13576 /* finalize the progress, someone might wait if a frontend
13577 * closes the session before powering on the VM. */
13578 mData->mSession.mProgress->notifyComplete(E_FAIL,
13579 COM_IIDOF(ISession),
13580 getComponentName(),
13581 tr("The VM session was closed before any attempt to power it on"));
13582 mData->mSession.mProgress.setNull();
13583 }
13584
13585 /* Create the progress object the client will use to wait until
13586 * #checkForDeath() is called to uninitialize this session object after
13587 * it releases the IPC semaphore.
13588 * Note! Because we're "reusing" mProgress here, this must be a proxy
13589 * object just like for LaunchVMProcess. */
13590 Assert(mData->mSession.mProgress.isNull());
13591 ComObjPtr<ProgressProxy> progress;
13592 progress.createObject();
13593 ComPtr<IUnknown> pPeer(mPeer);
13594 progress->init(mParent, pPeer,
13595 Bstr(tr("Closing session")).raw(),
13596 FALSE /* aCancelable */);
13597 progress.queryInterfaceTo(aProgress);
13598 mData->mSession.mProgress = progress;
13599 }
13600 else
13601 {
13602 /* the remote session is being normally closed */
13603 Data::Session::RemoteControlList::iterator it =
13604 mData->mSession.mRemoteControls.begin();
13605 while (it != mData->mSession.mRemoteControls.end())
13606 {
13607 if (control == *it)
13608 break;
13609 ++it;
13610 }
13611 BOOL found = it != mData->mSession.mRemoteControls.end();
13612 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13613 E_INVALIDARG);
13614 // This MUST be erase(it), not remove(*it) as the latter triggers a
13615 // very nasty use after free due to the place where the value "lives".
13616 mData->mSession.mRemoteControls.erase(it);
13617 }
13618
13619 /* signal the client watcher thread, because the client is going away */
13620 mParent->updateClientWatcher();
13621
13622 LogFlowThisFuncLeave();
13623 return S_OK;
13624}
13625
13626/**
13627 * @note Locks this object for writing.
13628 */
13629STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13630{
13631 LogFlowThisFuncEnter();
13632
13633 CheckComArgOutPointerValid(aProgress);
13634 CheckComArgOutPointerValid(aStateFilePath);
13635
13636 AutoCaller autoCaller(this);
13637 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13638
13639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13640
13641 AssertReturn( mData->mMachineState == MachineState_Paused
13642 && mConsoleTaskData.mLastState == MachineState_Null
13643 && mConsoleTaskData.strStateFilePath.isEmpty(),
13644 E_FAIL);
13645
13646 /* create a progress object to track operation completion */
13647 ComObjPtr<Progress> pProgress;
13648 pProgress.createObject();
13649 pProgress->init(getVirtualBox(),
13650 static_cast<IMachine *>(this) /* aInitiator */,
13651 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13652 FALSE /* aCancelable */);
13653
13654 Utf8Str strStateFilePath;
13655 /* stateFilePath is null when the machine is not running */
13656 if (mData->mMachineState == MachineState_Paused)
13657 composeSavedStateFilename(strStateFilePath);
13658
13659 /* fill in the console task data */
13660 mConsoleTaskData.mLastState = mData->mMachineState;
13661 mConsoleTaskData.strStateFilePath = strStateFilePath;
13662 mConsoleTaskData.mProgress = pProgress;
13663
13664 /* set the state to Saving (this is expected by Console::SaveState()) */
13665 setMachineState(MachineState_Saving);
13666
13667 strStateFilePath.cloneTo(aStateFilePath);
13668 pProgress.queryInterfaceTo(aProgress);
13669
13670 return S_OK;
13671}
13672
13673/**
13674 * @note Locks mParent + this object for writing.
13675 */
13676STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13677{
13678 LogFlowThisFunc(("\n"));
13679
13680 AutoCaller autoCaller(this);
13681 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13682
13683 /* endSavingState() need mParent lock */
13684 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13685
13686 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13687 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13688 && mConsoleTaskData.mLastState != MachineState_Null
13689 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13690 E_FAIL);
13691
13692 /*
13693 * On failure, set the state to the state we had when BeginSavingState()
13694 * was called (this is expected by Console::SaveState() and the associated
13695 * task). On success the VM process already changed the state to
13696 * MachineState_Saved, so no need to do anything.
13697 */
13698 if (FAILED(iResult))
13699 setMachineState(mConsoleTaskData.mLastState);
13700
13701 return endSavingState(iResult, aErrMsg);
13702}
13703
13704/**
13705 * @note Locks this object for writing.
13706 */
13707STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13708{
13709 LogFlowThisFunc(("\n"));
13710
13711 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13712
13713 AutoCaller autoCaller(this);
13714 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13715
13716 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13717
13718 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13719 || mData->mMachineState == MachineState_Teleported
13720 || mData->mMachineState == MachineState_Aborted
13721 , E_FAIL); /** @todo setError. */
13722
13723 Utf8Str stateFilePathFull = aSavedStateFile;
13724 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13725 if (RT_FAILURE(vrc))
13726 return setError(VBOX_E_FILE_ERROR,
13727 tr("Invalid saved state file path '%ls' (%Rrc)"),
13728 aSavedStateFile,
13729 vrc);
13730
13731 mSSData->strStateFilePath = stateFilePathFull;
13732
13733 /* The below setMachineState() will detect the state transition and will
13734 * update the settings file */
13735
13736 return setMachineState(MachineState_Saved);
13737}
13738
13739STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13740 ComSafeArrayOut(BSTR, aValues),
13741 ComSafeArrayOut(LONG64, aTimestamps),
13742 ComSafeArrayOut(BSTR, aFlags))
13743{
13744 LogFlowThisFunc(("\n"));
13745
13746#ifdef VBOX_WITH_GUEST_PROPS
13747 using namespace guestProp;
13748
13749 AutoCaller autoCaller(this);
13750 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13751
13752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13753
13754 CheckComArgOutSafeArrayPointerValid(aNames);
13755 CheckComArgOutSafeArrayPointerValid(aValues);
13756 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13757 CheckComArgOutSafeArrayPointerValid(aFlags);
13758
13759 size_t cEntries = mHWData->mGuestProperties.size();
13760 com::SafeArray<BSTR> names(cEntries);
13761 com::SafeArray<BSTR> values(cEntries);
13762 com::SafeArray<LONG64> timestamps(cEntries);
13763 com::SafeArray<BSTR> flags(cEntries);
13764 unsigned i = 0;
13765 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13766 it != mHWData->mGuestProperties.end();
13767 ++it)
13768 {
13769 char szFlags[MAX_FLAGS_LEN + 1];
13770 it->first.cloneTo(&names[i]);
13771 it->second.strValue.cloneTo(&values[i]);
13772 timestamps[i] = it->second.mTimestamp;
13773 /* If it is NULL, keep it NULL. */
13774 if (it->second.mFlags)
13775 {
13776 writeFlags(it->second.mFlags, szFlags);
13777 Bstr(szFlags).cloneTo(&flags[i]);
13778 }
13779 else
13780 flags[i] = NULL;
13781 ++i;
13782 }
13783 names.detachTo(ComSafeArrayOutArg(aNames));
13784 values.detachTo(ComSafeArrayOutArg(aValues));
13785 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13786 flags.detachTo(ComSafeArrayOutArg(aFlags));
13787 return S_OK;
13788#else
13789 ReturnComNotImplemented();
13790#endif
13791}
13792
13793STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13794 IN_BSTR aValue,
13795 LONG64 aTimestamp,
13796 IN_BSTR aFlags)
13797{
13798 LogFlowThisFunc(("\n"));
13799
13800#ifdef VBOX_WITH_GUEST_PROPS
13801 using namespace guestProp;
13802
13803 CheckComArgStrNotEmptyOrNull(aName);
13804 CheckComArgNotNull(aValue);
13805 CheckComArgNotNull(aFlags);
13806
13807 try
13808 {
13809 /*
13810 * Convert input up front.
13811 */
13812 Utf8Str utf8Name(aName);
13813 uint32_t fFlags = NILFLAG;
13814 if (aFlags)
13815 {
13816 Utf8Str utf8Flags(aFlags);
13817 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13818 AssertRCReturn(vrc, E_INVALIDARG);
13819 }
13820
13821 /*
13822 * Now grab the object lock, validate the state and do the update.
13823 */
13824 AutoCaller autoCaller(this);
13825 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13826
13827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13828
13829 switch (mData->mMachineState)
13830 {
13831 case MachineState_Paused:
13832 case MachineState_Running:
13833 case MachineState_Teleporting:
13834 case MachineState_TeleportingPausedVM:
13835 case MachineState_LiveSnapshotting:
13836 case MachineState_DeletingSnapshotOnline:
13837 case MachineState_DeletingSnapshotPaused:
13838 case MachineState_Saving:
13839 case MachineState_Stopping:
13840 break;
13841
13842 default:
13843 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13844 VBOX_E_INVALID_VM_STATE);
13845 }
13846
13847 setModified(IsModified_MachineData);
13848 mHWData.backup();
13849
13850 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13851 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13852 if (it != mHWData->mGuestProperties.end())
13853 {
13854 if (!fDelete)
13855 {
13856 it->second.strValue = aValue;
13857 it->second.mTimestamp = aTimestamp;
13858 it->second.mFlags = fFlags;
13859 }
13860 else
13861 mHWData->mGuestProperties.erase(it);
13862
13863 mData->mGuestPropertiesModified = TRUE;
13864 }
13865 else if (!fDelete)
13866 {
13867 HWData::GuestProperty prop;
13868 prop.strValue = aValue;
13869 prop.mTimestamp = aTimestamp;
13870 prop.mFlags = fFlags;
13871
13872 mHWData->mGuestProperties[utf8Name] = prop;
13873 mData->mGuestPropertiesModified = TRUE;
13874 }
13875
13876 /*
13877 * Send a callback notification if appropriate
13878 */
13879 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13880 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13881 RTSTR_MAX,
13882 utf8Name.c_str(),
13883 RTSTR_MAX, NULL)
13884 )
13885 {
13886 alock.release();
13887
13888 mParent->onGuestPropertyChange(mData->mUuid,
13889 aName,
13890 aValue,
13891 aFlags);
13892 }
13893 }
13894 catch (...)
13895 {
13896 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13897 }
13898 return S_OK;
13899#else
13900 ReturnComNotImplemented();
13901#endif
13902}
13903
13904STDMETHODIMP SessionMachine::LockMedia()
13905{
13906 AutoCaller autoCaller(this);
13907 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13908
13909 AutoMultiWriteLock2 alock(this->lockHandle(),
13910 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13911
13912 AssertReturn( mData->mMachineState == MachineState_Starting
13913 || mData->mMachineState == MachineState_Restoring
13914 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13915
13916 clearError();
13917 alock.release();
13918 return lockMedia();
13919}
13920
13921STDMETHODIMP SessionMachine::UnlockMedia()
13922{
13923 unlockMedia();
13924 return S_OK;
13925}
13926
13927STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13928 IMediumAttachment **aNewAttachment)
13929{
13930 CheckComArgNotNull(aAttachment);
13931 CheckComArgOutPointerValid(aNewAttachment);
13932
13933 AutoCaller autoCaller(this);
13934 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13935
13936 // request the host lock first, since might be calling Host methods for getting host drives;
13937 // next, protect the media tree all the while we're in here, as well as our member variables
13938 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13939 this->lockHandle(),
13940 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13941
13942 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13943
13944 Bstr ctrlName;
13945 LONG lPort;
13946 LONG lDevice;
13947 bool fTempEject;
13948 {
13949 AutoCaller autoAttachCaller(this);
13950 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13951
13952 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13953
13954 /* Need to query the details first, as the IMediumAttachment reference
13955 * might be to the original settings, which we are going to change. */
13956 ctrlName = pAttach->i_getControllerName();
13957 lPort = pAttach->i_getPort();
13958 lDevice = pAttach->i_getDevice();
13959 fTempEject = pAttach->i_getTempEject();
13960 }
13961
13962 if (!fTempEject)
13963 {
13964 /* Remember previously mounted medium. The medium before taking the
13965 * backup is not necessarily the same thing. */
13966 ComObjPtr<Medium> oldmedium;
13967 oldmedium = pAttach->i_getMedium();
13968
13969 setModified(IsModified_Storage);
13970 mMediaData.backup();
13971
13972 // The backup operation makes the pAttach reference point to the
13973 // old settings. Re-get the correct reference.
13974 pAttach = findAttachment(mMediaData->mAttachments,
13975 ctrlName.raw(),
13976 lPort,
13977 lDevice);
13978
13979 {
13980 AutoCaller autoAttachCaller(this);
13981 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13982
13983 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13984 if (!oldmedium.isNull())
13985 oldmedium->i_removeBackReference(mData->mUuid);
13986
13987 pAttach->i_updateMedium(NULL);
13988 pAttach->i_updateEjected();
13989 }
13990
13991 setModified(IsModified_Storage);
13992 }
13993 else
13994 {
13995 {
13996 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13997 pAttach->i_updateEjected();
13998 }
13999 }
14000
14001 pAttach.queryInterfaceTo(aNewAttachment);
14002
14003 return S_OK;
14004}
14005
14006// public methods only for internal purposes
14007/////////////////////////////////////////////////////////////////////////////
14008
14009#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14010/**
14011 * Called from the client watcher thread to check for expected or unexpected
14012 * death of the client process that has a direct session to this machine.
14013 *
14014 * On Win32 and on OS/2, this method is called only when we've got the
14015 * mutex (i.e. the client has either died or terminated normally) so it always
14016 * returns @c true (the client is terminated, the session machine is
14017 * uninitialized).
14018 *
14019 * On other platforms, the method returns @c true if the client process has
14020 * terminated normally or abnormally and the session machine was uninitialized,
14021 * and @c false if the client process is still alive.
14022 *
14023 * @note Locks this object for writing.
14024 */
14025bool SessionMachine::checkForDeath()
14026{
14027 Uninit::Reason reason;
14028 bool terminated = false;
14029
14030 /* Enclose autoCaller with a block because calling uninit() from under it
14031 * will deadlock. */
14032 {
14033 AutoCaller autoCaller(this);
14034 if (!autoCaller.isOk())
14035 {
14036 /* return true if not ready, to cause the client watcher to exclude
14037 * the corresponding session from watching */
14038 LogFlowThisFunc(("Already uninitialized!\n"));
14039 return true;
14040 }
14041
14042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14043
14044 /* Determine the reason of death: if the session state is Closing here,
14045 * everything is fine. Otherwise it means that the client did not call
14046 * OnSessionEnd() before it released the IPC semaphore. This may happen
14047 * either because the client process has abnormally terminated, or
14048 * because it simply forgot to call ISession::Close() before exiting. We
14049 * threat the latter also as an abnormal termination (see
14050 * Session::uninit() for details). */
14051 reason = mData->mSession.mState == SessionState_Unlocking ?
14052 Uninit::Normal :
14053 Uninit::Abnormal;
14054
14055 if (mClientToken)
14056 terminated = mClientToken->release();
14057 } /* AutoCaller block */
14058
14059 if (terminated)
14060 uninit(reason);
14061
14062 return terminated;
14063}
14064
14065void SessionMachine::getTokenId(Utf8Str &strTokenId)
14066{
14067 LogFlowThisFunc(("\n"));
14068
14069 strTokenId.setNull();
14070
14071 AutoCaller autoCaller(this);
14072 AssertComRCReturnVoid(autoCaller.rc());
14073
14074 Assert(mClientToken);
14075 if (mClientToken)
14076 mClientToken->getId(strTokenId);
14077}
14078#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14079IToken *SessionMachine::getToken()
14080{
14081 LogFlowThisFunc(("\n"));
14082
14083 AutoCaller autoCaller(this);
14084 AssertComRCReturn(autoCaller.rc(), NULL);
14085
14086 Assert(mClientToken);
14087 if (mClientToken)
14088 return mClientToken->getToken();
14089 else
14090 return NULL;
14091}
14092#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14093
14094Machine::ClientToken *SessionMachine::getClientToken()
14095{
14096 LogFlowThisFunc(("\n"));
14097
14098 AutoCaller autoCaller(this);
14099 AssertComRCReturn(autoCaller.rc(), NULL);
14100
14101 return mClientToken;
14102}
14103
14104
14105/**
14106 * @note Locks this object for reading.
14107 */
14108HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14109{
14110 LogFlowThisFunc(("\n"));
14111
14112 AutoCaller autoCaller(this);
14113 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14114
14115 ComPtr<IInternalSessionControl> directControl;
14116 {
14117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14118 directControl = mData->mSession.mDirectControl;
14119 }
14120
14121 /* ignore notifications sent after #OnSessionEnd() is called */
14122 if (!directControl)
14123 return S_OK;
14124
14125 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14126}
14127
14128/**
14129 * @note Locks this object for reading.
14130 */
14131HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14132 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
14133{
14134 LogFlowThisFunc(("\n"));
14135
14136 AutoCaller autoCaller(this);
14137 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14138
14139 ComPtr<IInternalSessionControl> directControl;
14140 {
14141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14142 directControl = mData->mSession.mDirectControl;
14143 }
14144
14145 /* ignore notifications sent after #OnSessionEnd() is called */
14146 if (!directControl)
14147 return S_OK;
14148 /*
14149 * instead acting like callback we ask IVirtualBox deliver corresponding event
14150 */
14151
14152 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14153 return S_OK;
14154}
14155
14156/**
14157 * @note Locks this object for reading.
14158 */
14159HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14160{
14161 LogFlowThisFunc(("\n"));
14162
14163 AutoCaller autoCaller(this);
14164 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14165
14166 ComPtr<IInternalSessionControl> directControl;
14167 {
14168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14169 directControl = mData->mSession.mDirectControl;
14170 }
14171
14172 /* ignore notifications sent after #OnSessionEnd() is called */
14173 if (!directControl)
14174 return S_OK;
14175
14176 return directControl->OnSerialPortChange(serialPort);
14177}
14178
14179/**
14180 * @note Locks this object for reading.
14181 */
14182HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14183{
14184 LogFlowThisFunc(("\n"));
14185
14186 AutoCaller autoCaller(this);
14187 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14188
14189 ComPtr<IInternalSessionControl> directControl;
14190 {
14191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14192 directControl = mData->mSession.mDirectControl;
14193 }
14194
14195 /* ignore notifications sent after #OnSessionEnd() is called */
14196 if (!directControl)
14197 return S_OK;
14198
14199 return directControl->OnParallelPortChange(parallelPort);
14200}
14201
14202/**
14203 * @note Locks this object for reading.
14204 */
14205HRESULT SessionMachine::onStorageControllerChange()
14206{
14207 LogFlowThisFunc(("\n"));
14208
14209 AutoCaller autoCaller(this);
14210 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14211
14212 ComPtr<IInternalSessionControl> directControl;
14213 {
14214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14215 directControl = mData->mSession.mDirectControl;
14216 }
14217
14218 /* ignore notifications sent after #OnSessionEnd() is called */
14219 if (!directControl)
14220 return S_OK;
14221
14222 return directControl->OnStorageControllerChange();
14223}
14224
14225/**
14226 * @note Locks this object for reading.
14227 */
14228HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14229{
14230 LogFlowThisFunc(("\n"));
14231
14232 AutoCaller autoCaller(this);
14233 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14234
14235 ComPtr<IInternalSessionControl> directControl;
14236 {
14237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14238 directControl = mData->mSession.mDirectControl;
14239 }
14240
14241 /* ignore notifications sent after #OnSessionEnd() is called */
14242 if (!directControl)
14243 return S_OK;
14244
14245 return directControl->OnMediumChange(aAttachment, aForce);
14246}
14247
14248/**
14249 * @note Locks this object for reading.
14250 */
14251HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14252{
14253 LogFlowThisFunc(("\n"));
14254
14255 AutoCaller autoCaller(this);
14256 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14257
14258 ComPtr<IInternalSessionControl> directControl;
14259 {
14260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14261 directControl = mData->mSession.mDirectControl;
14262 }
14263
14264 /* ignore notifications sent after #OnSessionEnd() is called */
14265 if (!directControl)
14266 return S_OK;
14267
14268 return directControl->OnCPUChange(aCPU, aRemove);
14269}
14270
14271HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14272{
14273 LogFlowThisFunc(("\n"));
14274
14275 AutoCaller autoCaller(this);
14276 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14277
14278 ComPtr<IInternalSessionControl> directControl;
14279 {
14280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14281 directControl = mData->mSession.mDirectControl;
14282 }
14283
14284 /* ignore notifications sent after #OnSessionEnd() is called */
14285 if (!directControl)
14286 return S_OK;
14287
14288 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14289}
14290
14291/**
14292 * @note Locks this object for reading.
14293 */
14294HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14295{
14296 LogFlowThisFunc(("\n"));
14297
14298 AutoCaller autoCaller(this);
14299 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14300
14301 ComPtr<IInternalSessionControl> directControl;
14302 {
14303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14304 directControl = mData->mSession.mDirectControl;
14305 }
14306
14307 /* ignore notifications sent after #OnSessionEnd() is called */
14308 if (!directControl)
14309 return S_OK;
14310
14311 return directControl->OnVRDEServerChange(aRestart);
14312}
14313
14314/**
14315 * @note Locks this object for reading.
14316 */
14317HRESULT SessionMachine::onVideoCaptureChange()
14318{
14319 LogFlowThisFunc(("\n"));
14320
14321 AutoCaller autoCaller(this);
14322 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14323
14324 ComPtr<IInternalSessionControl> directControl;
14325 {
14326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14327 directControl = mData->mSession.mDirectControl;
14328 }
14329
14330 /* ignore notifications sent after #OnSessionEnd() is called */
14331 if (!directControl)
14332 return S_OK;
14333
14334 return directControl->OnVideoCaptureChange();
14335}
14336
14337/**
14338 * @note Locks this object for reading.
14339 */
14340HRESULT SessionMachine::onUSBControllerChange()
14341{
14342 LogFlowThisFunc(("\n"));
14343
14344 AutoCaller autoCaller(this);
14345 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14346
14347 ComPtr<IInternalSessionControl> directControl;
14348 {
14349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14350 directControl = mData->mSession.mDirectControl;
14351 }
14352
14353 /* ignore notifications sent after #OnSessionEnd() is called */
14354 if (!directControl)
14355 return S_OK;
14356
14357 return directControl->OnUSBControllerChange();
14358}
14359
14360/**
14361 * @note Locks this object for reading.
14362 */
14363HRESULT SessionMachine::onSharedFolderChange()
14364{
14365 LogFlowThisFunc(("\n"));
14366
14367 AutoCaller autoCaller(this);
14368 AssertComRCReturnRC(autoCaller.rc());
14369
14370 ComPtr<IInternalSessionControl> directControl;
14371 {
14372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14373 directControl = mData->mSession.mDirectControl;
14374 }
14375
14376 /* ignore notifications sent after #OnSessionEnd() is called */
14377 if (!directControl)
14378 return S_OK;
14379
14380 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14381}
14382
14383/**
14384 * @note Locks this object for reading.
14385 */
14386HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14387{
14388 LogFlowThisFunc(("\n"));
14389
14390 AutoCaller autoCaller(this);
14391 AssertComRCReturnRC(autoCaller.rc());
14392
14393 ComPtr<IInternalSessionControl> directControl;
14394 {
14395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14396 directControl = mData->mSession.mDirectControl;
14397 }
14398
14399 /* ignore notifications sent after #OnSessionEnd() is called */
14400 if (!directControl)
14401 return S_OK;
14402
14403 return directControl->OnClipboardModeChange(aClipboardMode);
14404}
14405
14406/**
14407 * @note Locks this object for reading.
14408 */
14409HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14410{
14411 LogFlowThisFunc(("\n"));
14412
14413 AutoCaller autoCaller(this);
14414 AssertComRCReturnRC(autoCaller.rc());
14415
14416 ComPtr<IInternalSessionControl> directControl;
14417 {
14418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14419 directControl = mData->mSession.mDirectControl;
14420 }
14421
14422 /* ignore notifications sent after #OnSessionEnd() is called */
14423 if (!directControl)
14424 return S_OK;
14425
14426 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14427}
14428
14429/**
14430 * @note Locks this object for reading.
14431 */
14432HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14433{
14434 LogFlowThisFunc(("\n"));
14435
14436 AutoCaller autoCaller(this);
14437 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14438
14439 ComPtr<IInternalSessionControl> directControl;
14440 {
14441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14442 directControl = mData->mSession.mDirectControl;
14443 }
14444
14445 /* ignore notifications sent after #OnSessionEnd() is called */
14446 if (!directControl)
14447 return S_OK;
14448
14449 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14450}
14451
14452/**
14453 * @note Locks this object for reading.
14454 */
14455HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14456{
14457 LogFlowThisFunc(("\n"));
14458
14459 AutoCaller autoCaller(this);
14460 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14461
14462 ComPtr<IInternalSessionControl> directControl;
14463 {
14464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14465 directControl = mData->mSession.mDirectControl;
14466 }
14467
14468 /* ignore notifications sent after #OnSessionEnd() is called */
14469 if (!directControl)
14470 return S_OK;
14471
14472 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14473}
14474
14475/**
14476 * Returns @c true if this machine's USB controller reports it has a matching
14477 * filter for the given USB device and @c false otherwise.
14478 *
14479 * @note locks this object for reading.
14480 */
14481bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14482{
14483 AutoCaller autoCaller(this);
14484 /* silently return if not ready -- this method may be called after the
14485 * direct machine session has been called */
14486 if (!autoCaller.isOk())
14487 return false;
14488
14489#ifdef VBOX_WITH_USB
14490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14491
14492 switch (mData->mMachineState)
14493 {
14494 case MachineState_Starting:
14495 case MachineState_Restoring:
14496 case MachineState_TeleportingIn:
14497 case MachineState_Paused:
14498 case MachineState_Running:
14499 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14500 * elsewhere... */
14501 alock.release();
14502 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14503 default: break;
14504 }
14505#else
14506 NOREF(aDevice);
14507 NOREF(aMaskedIfs);
14508#endif
14509 return false;
14510}
14511
14512/**
14513 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14514 */
14515HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14516 IVirtualBoxErrorInfo *aError,
14517 ULONG aMaskedIfs)
14518{
14519 LogFlowThisFunc(("\n"));
14520
14521 AutoCaller autoCaller(this);
14522
14523 /* This notification may happen after the machine object has been
14524 * uninitialized (the session was closed), so don't assert. */
14525 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14526
14527 ComPtr<IInternalSessionControl> directControl;
14528 {
14529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14530 directControl = mData->mSession.mDirectControl;
14531 }
14532
14533 /* fail on notifications sent after #OnSessionEnd() is called, it is
14534 * expected by the caller */
14535 if (!directControl)
14536 return E_FAIL;
14537
14538 /* No locks should be held at this point. */
14539 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14540 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14541
14542 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14543}
14544
14545/**
14546 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14547 */
14548HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14549 IVirtualBoxErrorInfo *aError)
14550{
14551 LogFlowThisFunc(("\n"));
14552
14553 AutoCaller autoCaller(this);
14554
14555 /* This notification may happen after the machine object has been
14556 * uninitialized (the session was closed), so don't assert. */
14557 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14558
14559 ComPtr<IInternalSessionControl> directControl;
14560 {
14561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14562 directControl = mData->mSession.mDirectControl;
14563 }
14564
14565 /* fail on notifications sent after #OnSessionEnd() is called, it is
14566 * expected by the caller */
14567 if (!directControl)
14568 return E_FAIL;
14569
14570 /* No locks should be held at this point. */
14571 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14572 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14573
14574 return directControl->OnUSBDeviceDetach(aId, aError);
14575}
14576
14577// protected methods
14578/////////////////////////////////////////////////////////////////////////////
14579
14580/**
14581 * Helper method to finalize saving the state.
14582 *
14583 * @note Must be called from under this object's lock.
14584 *
14585 * @param aRc S_OK if the snapshot has been taken successfully
14586 * @param aErrMsg human readable error message for failure
14587 *
14588 * @note Locks mParent + this objects for writing.
14589 */
14590HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14591{
14592 LogFlowThisFuncEnter();
14593
14594 AutoCaller autoCaller(this);
14595 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14596
14597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14598
14599 HRESULT rc = S_OK;
14600
14601 if (SUCCEEDED(aRc))
14602 {
14603 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14604
14605 /* save all VM settings */
14606 rc = saveSettings(NULL);
14607 // no need to check whether VirtualBox.xml needs saving also since
14608 // we can't have a name change pending at this point
14609 }
14610 else
14611 {
14612 // delete the saved state file (it might have been already created);
14613 // we need not check whether this is shared with a snapshot here because
14614 // we certainly created this saved state file here anew
14615 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14616 }
14617
14618 /* notify the progress object about operation completion */
14619 Assert(mConsoleTaskData.mProgress);
14620 if (SUCCEEDED(aRc))
14621 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14622 else
14623 {
14624 if (aErrMsg.length())
14625 mConsoleTaskData.mProgress->notifyComplete(aRc,
14626 COM_IIDOF(ISession),
14627 getComponentName(),
14628 aErrMsg.c_str());
14629 else
14630 mConsoleTaskData.mProgress->notifyComplete(aRc);
14631 }
14632
14633 /* clear out the temporary saved state data */
14634 mConsoleTaskData.mLastState = MachineState_Null;
14635 mConsoleTaskData.strStateFilePath.setNull();
14636 mConsoleTaskData.mProgress.setNull();
14637
14638 LogFlowThisFuncLeave();
14639 return rc;
14640}
14641
14642/**
14643 * Deletes the given file if it is no longer in use by either the current machine state
14644 * (if the machine is "saved") or any of the machine's snapshots.
14645 *
14646 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14647 * but is different for each SnapshotMachine. When calling this, the order of calling this
14648 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14649 * is therefore critical. I know, it's all rather messy.
14650 *
14651 * @param strStateFile
14652 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14653 */
14654void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14655 Snapshot *pSnapshotToIgnore)
14656{
14657 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14658 if ( (strStateFile.isNotEmpty())
14659 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14660 )
14661 // ... and it must also not be shared with other snapshots
14662 if ( !mData->mFirstSnapshot
14663 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14664 // this checks the SnapshotMachine's state file paths
14665 )
14666 RTFileDelete(strStateFile.c_str());
14667}
14668
14669/**
14670 * Locks the attached media.
14671 *
14672 * All attached hard disks are locked for writing and DVD/floppy are locked for
14673 * reading. Parents of attached hard disks (if any) are locked for reading.
14674 *
14675 * This method also performs accessibility check of all media it locks: if some
14676 * media is inaccessible, the method will return a failure and a bunch of
14677 * extended error info objects per each inaccessible medium.
14678 *
14679 * Note that this method is atomic: if it returns a success, all media are
14680 * locked as described above; on failure no media is locked at all (all
14681 * succeeded individual locks will be undone).
14682 *
14683 * The caller is responsible for doing the necessary state sanity checks.
14684 *
14685 * The locks made by this method must be undone by calling #unlockMedia() when
14686 * no more needed.
14687 */
14688HRESULT SessionMachine::lockMedia()
14689{
14690 AutoCaller autoCaller(this);
14691 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14692
14693 AutoMultiWriteLock2 alock(this->lockHandle(),
14694 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14695
14696 /* bail out if trying to lock things with already set up locking */
14697 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14698
14699 MultiResult mrc(S_OK);
14700
14701 /* Collect locking information for all medium objects attached to the VM. */
14702 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14703 it != mMediaData->mAttachments.end();
14704 ++it)
14705 {
14706 MediumAttachment* pAtt = *it;
14707 DeviceType_T devType = pAtt->i_getType();
14708 Medium *pMedium = pAtt->i_getMedium();
14709
14710 MediumLockList *pMediumLockList(new MediumLockList());
14711 // There can be attachments without a medium (floppy/dvd), and thus
14712 // it's impossible to create a medium lock list. It still makes sense
14713 // to have the empty medium lock list in the map in case a medium is
14714 // attached later.
14715 if (pMedium != NULL)
14716 {
14717 MediumType_T mediumType = pMedium->i_getType();
14718 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14719 || mediumType == MediumType_Shareable;
14720 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14721
14722 alock.release();
14723 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14724 !fIsReadOnlyLock /* fMediumLockWrite */,
14725 NULL,
14726 *pMediumLockList);
14727 alock.acquire();
14728 if (FAILED(mrc))
14729 {
14730 delete pMediumLockList;
14731 mData->mSession.mLockedMedia.Clear();
14732 break;
14733 }
14734 }
14735
14736 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14737 if (FAILED(rc))
14738 {
14739 mData->mSession.mLockedMedia.Clear();
14740 mrc = setError(rc,
14741 tr("Collecting locking information for all attached media failed"));
14742 break;
14743 }
14744 }
14745
14746 if (SUCCEEDED(mrc))
14747 {
14748 /* Now lock all media. If this fails, nothing is locked. */
14749 alock.release();
14750 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14751 alock.acquire();
14752 if (FAILED(rc))
14753 {
14754 mrc = setError(rc,
14755 tr("Locking of attached media failed"));
14756 }
14757 }
14758
14759 return mrc;
14760}
14761
14762/**
14763 * Undoes the locks made by by #lockMedia().
14764 */
14765void SessionMachine::unlockMedia()
14766{
14767 AutoCaller autoCaller(this);
14768 AssertComRCReturnVoid(autoCaller.rc());
14769
14770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14771
14772 /* we may be holding important error info on the current thread;
14773 * preserve it */
14774 ErrorInfoKeeper eik;
14775
14776 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14777 AssertComRC(rc);
14778}
14779
14780/**
14781 * Helper to change the machine state (reimplementation).
14782 *
14783 * @note Locks this object for writing.
14784 * @note This method must not call saveSettings or SaveSettings, otherwise
14785 * it can cause crashes in random places due to unexpectedly committing
14786 * the current settings. The caller is responsible for that. The call
14787 * to saveStateSettings is fine, because this method does not commit.
14788 */
14789HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14790{
14791 LogFlowThisFuncEnter();
14792 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14793
14794 AutoCaller autoCaller(this);
14795 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14796
14797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14798
14799 MachineState_T oldMachineState = mData->mMachineState;
14800
14801 AssertMsgReturn(oldMachineState != aMachineState,
14802 ("oldMachineState=%s, aMachineState=%s\n",
14803 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14804 E_FAIL);
14805
14806 HRESULT rc = S_OK;
14807
14808 int stsFlags = 0;
14809 bool deleteSavedState = false;
14810
14811 /* detect some state transitions */
14812
14813 if ( ( oldMachineState == MachineState_Saved
14814 && aMachineState == MachineState_Restoring)
14815 || ( ( oldMachineState == MachineState_PoweredOff
14816 || oldMachineState == MachineState_Teleported
14817 || oldMachineState == MachineState_Aborted
14818 )
14819 && ( aMachineState == MachineState_TeleportingIn
14820 || aMachineState == MachineState_Starting
14821 )
14822 )
14823 )
14824 {
14825 /* The EMT thread is about to start */
14826
14827 /* Nothing to do here for now... */
14828
14829 /// @todo NEWMEDIA don't let mDVDDrive and other children
14830 /// change anything when in the Starting/Restoring state
14831 }
14832 else if ( ( oldMachineState == MachineState_Running
14833 || oldMachineState == MachineState_Paused
14834 || oldMachineState == MachineState_Teleporting
14835 || oldMachineState == MachineState_LiveSnapshotting
14836 || oldMachineState == MachineState_Stuck
14837 || oldMachineState == MachineState_Starting
14838 || oldMachineState == MachineState_Stopping
14839 || oldMachineState == MachineState_Saving
14840 || oldMachineState == MachineState_Restoring
14841 || oldMachineState == MachineState_TeleportingPausedVM
14842 || oldMachineState == MachineState_TeleportingIn
14843 )
14844 && ( aMachineState == MachineState_PoweredOff
14845 || aMachineState == MachineState_Saved
14846 || aMachineState == MachineState_Teleported
14847 || aMachineState == MachineState_Aborted
14848 )
14849 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14850 * snapshot */
14851 && ( mConsoleTaskData.mSnapshot.isNull()
14852 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14853 )
14854 )
14855 {
14856 /* The EMT thread has just stopped, unlock attached media. Note that as
14857 * opposed to locking that is done from Console, we do unlocking here
14858 * because the VM process may have aborted before having a chance to
14859 * properly unlock all media it locked. */
14860
14861 unlockMedia();
14862 }
14863
14864 if (oldMachineState == MachineState_Restoring)
14865 {
14866 if (aMachineState != MachineState_Saved)
14867 {
14868 /*
14869 * delete the saved state file once the machine has finished
14870 * restoring from it (note that Console sets the state from
14871 * Restoring to Saved if the VM couldn't restore successfully,
14872 * to give the user an ability to fix an error and retry --
14873 * we keep the saved state file in this case)
14874 */
14875 deleteSavedState = true;
14876 }
14877 }
14878 else if ( oldMachineState == MachineState_Saved
14879 && ( aMachineState == MachineState_PoweredOff
14880 || aMachineState == MachineState_Aborted
14881 || aMachineState == MachineState_Teleported
14882 )
14883 )
14884 {
14885 /*
14886 * delete the saved state after Console::ForgetSavedState() is called
14887 * or if the VM process (owning a direct VM session) crashed while the
14888 * VM was Saved
14889 */
14890
14891 /// @todo (dmik)
14892 // Not sure that deleting the saved state file just because of the
14893 // client death before it attempted to restore the VM is a good
14894 // thing. But when it crashes we need to go to the Aborted state
14895 // which cannot have the saved state file associated... The only
14896 // way to fix this is to make the Aborted condition not a VM state
14897 // but a bool flag: i.e., when a crash occurs, set it to true and
14898 // change the state to PoweredOff or Saved depending on the
14899 // saved state presence.
14900
14901 deleteSavedState = true;
14902 mData->mCurrentStateModified = TRUE;
14903 stsFlags |= SaveSTS_CurStateModified;
14904 }
14905
14906 if ( aMachineState == MachineState_Starting
14907 || aMachineState == MachineState_Restoring
14908 || aMachineState == MachineState_TeleportingIn
14909 )
14910 {
14911 /* set the current state modified flag to indicate that the current
14912 * state is no more identical to the state in the
14913 * current snapshot */
14914 if (!mData->mCurrentSnapshot.isNull())
14915 {
14916 mData->mCurrentStateModified = TRUE;
14917 stsFlags |= SaveSTS_CurStateModified;
14918 }
14919 }
14920
14921 if (deleteSavedState)
14922 {
14923 if (mRemoveSavedState)
14924 {
14925 Assert(!mSSData->strStateFilePath.isEmpty());
14926
14927 // it is safe to delete the saved state file if ...
14928 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14929 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14930 // ... none of the snapshots share the saved state file
14931 )
14932 RTFileDelete(mSSData->strStateFilePath.c_str());
14933 }
14934
14935 mSSData->strStateFilePath.setNull();
14936 stsFlags |= SaveSTS_StateFilePath;
14937 }
14938
14939 /* redirect to the underlying peer machine */
14940 mPeer->setMachineState(aMachineState);
14941
14942 if ( aMachineState == MachineState_PoweredOff
14943 || aMachineState == MachineState_Teleported
14944 || aMachineState == MachineState_Aborted
14945 || aMachineState == MachineState_Saved)
14946 {
14947 /* the machine has stopped execution
14948 * (or the saved state file was adopted) */
14949 stsFlags |= SaveSTS_StateTimeStamp;
14950 }
14951
14952 if ( ( oldMachineState == MachineState_PoweredOff
14953 || oldMachineState == MachineState_Aborted
14954 || oldMachineState == MachineState_Teleported
14955 )
14956 && aMachineState == MachineState_Saved)
14957 {
14958 /* the saved state file was adopted */
14959 Assert(!mSSData->strStateFilePath.isEmpty());
14960 stsFlags |= SaveSTS_StateFilePath;
14961 }
14962
14963#ifdef VBOX_WITH_GUEST_PROPS
14964 if ( aMachineState == MachineState_PoweredOff
14965 || aMachineState == MachineState_Aborted
14966 || aMachineState == MachineState_Teleported)
14967 {
14968 /* Make sure any transient guest properties get removed from the
14969 * property store on shutdown. */
14970
14971 HWData::GuestPropertyMap::const_iterator it;
14972 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14973 if (!fNeedsSaving)
14974 for (it = mHWData->mGuestProperties.begin();
14975 it != mHWData->mGuestProperties.end(); ++it)
14976 if ( (it->second.mFlags & guestProp::TRANSIENT)
14977 || (it->second.mFlags & guestProp::TRANSRESET))
14978 {
14979 fNeedsSaving = true;
14980 break;
14981 }
14982 if (fNeedsSaving)
14983 {
14984 mData->mCurrentStateModified = TRUE;
14985 stsFlags |= SaveSTS_CurStateModified;
14986 }
14987 }
14988#endif
14989
14990 rc = saveStateSettings(stsFlags);
14991
14992 if ( ( oldMachineState != MachineState_PoweredOff
14993 && oldMachineState != MachineState_Aborted
14994 && oldMachineState != MachineState_Teleported
14995 )
14996 && ( aMachineState == MachineState_PoweredOff
14997 || aMachineState == MachineState_Aborted
14998 || aMachineState == MachineState_Teleported
14999 )
15000 )
15001 {
15002 /* we've been shut down for any reason */
15003 /* no special action so far */
15004 }
15005
15006 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15007 LogFlowThisFuncLeave();
15008 return rc;
15009}
15010
15011/**
15012 * Sends the current machine state value to the VM process.
15013 *
15014 * @note Locks this object for reading, then calls a client process.
15015 */
15016HRESULT SessionMachine::updateMachineStateOnClient()
15017{
15018 AutoCaller autoCaller(this);
15019 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15020
15021 ComPtr<IInternalSessionControl> directControl;
15022 {
15023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15024 AssertReturn(!!mData, E_FAIL);
15025 directControl = mData->mSession.mDirectControl;
15026
15027 /* directControl may be already set to NULL here in #OnSessionEnd()
15028 * called too early by the direct session process while there is still
15029 * some operation (like deleting the snapshot) in progress. The client
15030 * process in this case is waiting inside Session::close() for the
15031 * "end session" process object to complete, while #uninit() called by
15032 * #checkForDeath() on the Watcher thread is waiting for the pending
15033 * operation to complete. For now, we accept this inconsistent behavior
15034 * and simply do nothing here. */
15035
15036 if (mData->mSession.mState == SessionState_Unlocking)
15037 return S_OK;
15038
15039 AssertReturn(!directControl.isNull(), E_FAIL);
15040 }
15041
15042 return directControl->UpdateMachineState(mData->mMachineState);
15043}
Note: See TracBrowser for help on using the repository browser.

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