VirtualBox

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

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

Main/Machine+ClientWatcher: fix for the generic watcher, it missed crashes of spawning sessions (e.g. launching a GUI VM if the DISPLAY variable isn't set), 4.3 regression

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