VirtualBox

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

Last change on this file since 48505 was 48505, checked in by vboxsync, 12 years ago

Main/Machine: preparations for moving machines to a different place on disk

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 501.2 KB
Line 
1/* $Id: MachineImpl.cpp 48505 2013-09-17 15:48:55Z 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 mHPETEnabled = false;
191
192 /* default boot order: floppy - DVD - HDD */
193 mBootOrder[0] = DeviceType_Floppy;
194 mBootOrder[1] = DeviceType_DVD;
195 mBootOrder[2] = DeviceType_HardDisk;
196 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
197 mBootOrder[i] = DeviceType_Null;
198
199 mClipboardMode = ClipboardMode_Disabled;
200 mDragAndDropMode = DragAndDropMode_Disabled;
201 mGuestPropertyNotificationPatterns = "";
202
203 mFirmwareType = FirmwareType_BIOS;
204 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
205 mPointingHIDType = PointingHIDType_PS2Mouse;
206 mChipsetType = ChipsetType_PIIX3;
207 mEmulatedUSBCardReaderEnabled = FALSE;
208
209 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
210 mCPUAttached[i] = false;
211
212 mIOCacheEnabled = true;
213 mIOCacheSize = 5; /* 5MB */
214
215 /* Maximum CPU execution cap by default. */
216 mCpuExecutionCap = 100;
217}
218
219Machine::HWData::~HWData()
220{
221}
222
223/////////////////////////////////////////////////////////////////////////////
224// Machine::HDData structure
225/////////////////////////////////////////////////////////////////////////////
226
227Machine::MediaData::MediaData()
228{
229}
230
231Machine::MediaData::~MediaData()
232{
233}
234
235/////////////////////////////////////////////////////////////////////////////
236// Machine class
237/////////////////////////////////////////////////////////////////////////////
238
239// constructor / destructor
240/////////////////////////////////////////////////////////////////////////////
241
242Machine::Machine() :
243#ifdef VBOX_WITH_RESOURCE_USAGE_API
244 mCollectorGuest(NULL),
245#endif
246 mPeer(NULL),
247 mParent(NULL),
248 mSerialPorts(),
249 mParallelPorts(),
250 uRegistryNeedsSaving(0)
251{}
252
253Machine::~Machine()
254{}
255
256HRESULT Machine::FinalConstruct()
257{
258 LogFlowThisFunc(("\n"));
259 return BaseFinalConstruct();
260}
261
262void Machine::FinalRelease()
263{
264 LogFlowThisFunc(("\n"));
265 uninit();
266 BaseFinalRelease();
267}
268
269/**
270 * Initializes a new machine instance; this init() variant creates a new, empty machine.
271 * This gets called from VirtualBox::CreateMachine().
272 *
273 * @param aParent Associated parent object
274 * @param strConfigFile Local file system path to the VM settings file (can
275 * be relative to the VirtualBox config directory).
276 * @param strName name for the machine
277 * @param llGroups list of groups for the machine
278 * @param aOsType OS Type of this machine or NULL.
279 * @param aId UUID for the new machine.
280 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
281 *
282 * @return Success indicator. if not S_OK, the machine object is invalid
283 */
284HRESULT Machine::init(VirtualBox *aParent,
285 const Utf8Str &strConfigFile,
286 const Utf8Str &strName,
287 const StringsList &llGroups,
288 GuestOSType *aOsType,
289 const Guid &aId,
290 bool fForceOverwrite,
291 bool fDirectoryIncludesUUID)
292{
293 LogFlowThisFuncEnter();
294 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
295
296 /* Enclose the state transition NotReady->InInit->Ready */
297 AutoInitSpan autoInitSpan(this);
298 AssertReturn(autoInitSpan.isOk(), E_FAIL);
299
300 HRESULT rc = initImpl(aParent, strConfigFile);
301 if (FAILED(rc)) return rc;
302
303 rc = tryCreateMachineConfigFile(fForceOverwrite);
304 if (FAILED(rc)) return rc;
305
306 if (SUCCEEDED(rc))
307 {
308 // create an empty machine config
309 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
310
311 rc = initDataAndChildObjects();
312 }
313
314 if (SUCCEEDED(rc))
315 {
316 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
317 mData->mAccessible = TRUE;
318
319 unconst(mData->mUuid) = aId;
320
321 mUserData->s.strName = strName;
322
323 mUserData->s.llGroups = llGroups;
324
325 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
326 // the "name sync" flag determines whether the machine directory gets renamed along
327 // with the machine file; say so if the settings file name is the same as the
328 // settings file parent directory (machine directory)
329 mUserData->s.fNameSync = isInOwnDir();
330
331 // initialize the default snapshots folder
332 rc = COMSETTER(SnapshotFolder)(NULL);
333 AssertComRC(rc);
334
335 if (aOsType)
336 {
337 /* Store OS type */
338 mUserData->s.strOsType = aOsType->id();
339
340 /* Apply BIOS defaults */
341 mBIOSSettings->applyDefaults(aOsType);
342
343 /* Apply network adapters defaults */
344 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
345 mNetworkAdapters[slot]->applyDefaults(aOsType);
346
347 /* Apply serial port defaults */
348 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
349 mSerialPorts[slot]->applyDefaults(aOsType);
350
351 /* Let the OS type select 64-bit ness. */
352 mHWData->mLongMode = aOsType->is64Bit()
353 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
354 }
355
356 /* At this point the changing of the current state modification
357 * flag is allowed. */
358 allowStateModification();
359
360 /* commit all changes made during the initialization */
361 commit();
362 }
363
364 /* Confirm a successful initialization when it's the case */
365 if (SUCCEEDED(rc))
366 {
367 if (mData->mAccessible)
368 autoInitSpan.setSucceeded();
369 else
370 autoInitSpan.setLimited();
371 }
372
373 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
374 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
375 mData->mRegistered,
376 mData->mAccessible,
377 rc));
378
379 LogFlowThisFuncLeave();
380
381 return rc;
382}
383
384/**
385 * Initializes a new instance with data from machine XML (formerly Init_Registered).
386 * Gets called in two modes:
387 *
388 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
389 * UUID is specified and we mark the machine as "registered";
390 *
391 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
392 * and the machine remains unregistered until RegisterMachine() is called.
393 *
394 * @param aParent Associated parent object
395 * @param aConfigFile Local file system path to the VM settings file (can
396 * be relative to the VirtualBox config directory).
397 * @param aId UUID of the machine or NULL (see above).
398 *
399 * @return Success indicator. if not S_OK, the machine object is invalid
400 */
401HRESULT Machine::initFromSettings(VirtualBox *aParent,
402 const Utf8Str &strConfigFile,
403 const Guid *aId)
404{
405 LogFlowThisFuncEnter();
406 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
407
408 /* Enclose the state transition NotReady->InInit->Ready */
409 AutoInitSpan autoInitSpan(this);
410 AssertReturn(autoInitSpan.isOk(), E_FAIL);
411
412 HRESULT rc = initImpl(aParent, strConfigFile);
413 if (FAILED(rc)) return rc;
414
415 if (aId)
416 {
417 // loading a registered VM:
418 unconst(mData->mUuid) = *aId;
419 mData->mRegistered = TRUE;
420 // now load the settings from XML:
421 rc = registeredInit();
422 // this calls initDataAndChildObjects() and loadSettings()
423 }
424 else
425 {
426 // opening an unregistered VM (VirtualBox::OpenMachine()):
427 rc = initDataAndChildObjects();
428
429 if (SUCCEEDED(rc))
430 {
431 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
432 mData->mAccessible = TRUE;
433
434 try
435 {
436 // load and parse machine XML; this will throw on XML or logic errors
437 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
438
439 // reject VM UUID duplicates, they can happen if someone
440 // tries to register an already known VM config again
441 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
442 true /* fPermitInaccessible */,
443 false /* aDoSetError */,
444 NULL) != VBOX_E_OBJECT_NOT_FOUND)
445 {
446 throw setError(E_FAIL,
447 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
448 mData->m_strConfigFile.c_str());
449 }
450
451 // use UUID from machine config
452 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
453
454 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
455 NULL /* puuidRegistry */);
456 if (FAILED(rc)) throw rc;
457
458 /* At this point the changing of the current state modification
459 * flag is allowed. */
460 allowStateModification();
461
462 commit();
463 }
464 catch (HRESULT err)
465 {
466 /* we assume that error info is set by the thrower */
467 rc = err;
468 }
469 catch (...)
470 {
471 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
472 }
473 }
474 }
475
476 /* Confirm a successful initialization when it's the case */
477 if (SUCCEEDED(rc))
478 {
479 if (mData->mAccessible)
480 autoInitSpan.setSucceeded();
481 else
482 {
483 autoInitSpan.setLimited();
484
485 // uninit media from this machine's media registry, or else
486 // reloading the settings will fail
487 mParent->unregisterMachineMedia(getId());
488 }
489 }
490
491 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
492 "rc=%08X\n",
493 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
494 mData->mRegistered, mData->mAccessible, rc));
495
496 LogFlowThisFuncLeave();
497
498 return rc;
499}
500
501/**
502 * Initializes a new instance from a machine config that is already in memory
503 * (import OVF case). Since we are importing, the UUID in the machine
504 * config is ignored and we always generate a fresh one.
505 *
506 * @param strName Name for the new machine; this overrides what is specified in config and is used
507 * for the settings file as well.
508 * @param config Machine configuration loaded and parsed from XML.
509 *
510 * @return Success indicator. if not S_OK, the machine object is invalid
511 */
512HRESULT Machine::init(VirtualBox *aParent,
513 const Utf8Str &strName,
514 const settings::MachineConfigFile &config)
515{
516 LogFlowThisFuncEnter();
517
518 /* Enclose the state transition NotReady->InInit->Ready */
519 AutoInitSpan autoInitSpan(this);
520 AssertReturn(autoInitSpan.isOk(), E_FAIL);
521
522 Utf8Str strConfigFile;
523 aParent->getDefaultMachineFolder(strConfigFile);
524 strConfigFile.append(RTPATH_DELIMITER);
525 strConfigFile.append(strName);
526 strConfigFile.append(RTPATH_DELIMITER);
527 strConfigFile.append(strName);
528 strConfigFile.append(".vbox");
529
530 HRESULT rc = initImpl(aParent, strConfigFile);
531 if (FAILED(rc)) return rc;
532
533 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
534 if (FAILED(rc)) return rc;
535
536 rc = initDataAndChildObjects();
537
538 if (SUCCEEDED(rc))
539 {
540 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
541 mData->mAccessible = TRUE;
542
543 // create empty machine config for instance data
544 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
545
546 // generate fresh UUID, ignore machine config
547 unconst(mData->mUuid).create();
548
549 rc = loadMachineDataFromSettings(config,
550 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
551
552 // override VM name as well, it may be different
553 mUserData->s.strName = strName;
554
555 if (SUCCEEDED(rc))
556 {
557 /* At this point the changing of the current state modification
558 * flag is allowed. */
559 allowStateModification();
560
561 /* commit all changes made during the initialization */
562 commit();
563 }
564 }
565
566 /* Confirm a successful initialization when it's the case */
567 if (SUCCEEDED(rc))
568 {
569 if (mData->mAccessible)
570 autoInitSpan.setSucceeded();
571 else
572 {
573 autoInitSpan.setLimited();
574
575 // uninit media from this machine's media registry, or else
576 // reloading the settings will fail
577 mParent->unregisterMachineMedia(getId());
578 }
579 }
580
581 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
582 "rc=%08X\n",
583 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
584 mData->mRegistered, mData->mAccessible, rc));
585
586 LogFlowThisFuncLeave();
587
588 return rc;
589}
590
591/**
592 * Shared code between the various init() implementations.
593 * @param aParent
594 * @return
595 */
596HRESULT Machine::initImpl(VirtualBox *aParent,
597 const Utf8Str &strConfigFile)
598{
599 LogFlowThisFuncEnter();
600
601 AssertReturn(aParent, E_INVALIDARG);
602 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
603
604 HRESULT rc = S_OK;
605
606 /* share the parent weakly */
607 unconst(mParent) = aParent;
608
609 /* allocate the essential machine data structure (the rest will be
610 * allocated later by initDataAndChildObjects() */
611 mData.allocate();
612
613 /* memorize the config file name (as provided) */
614 mData->m_strConfigFile = strConfigFile;
615
616 /* get the full file name */
617 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
618 if (RT_FAILURE(vrc1))
619 return setError(VBOX_E_FILE_ERROR,
620 tr("Invalid machine settings file name '%s' (%Rrc)"),
621 strConfigFile.c_str(),
622 vrc1);
623
624 LogFlowThisFuncLeave();
625
626 return rc;
627}
628
629/**
630 * Tries to create a machine settings file in the path stored in the machine
631 * instance data. Used when a new machine is created to fail gracefully if
632 * the settings file could not be written (e.g. because machine dir is read-only).
633 * @return
634 */
635HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
636{
637 HRESULT rc = S_OK;
638
639 // when we create a new machine, we must be able to create the settings file
640 RTFILE f = NIL_RTFILE;
641 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
642 if ( RT_SUCCESS(vrc)
643 || vrc == VERR_SHARING_VIOLATION
644 )
645 {
646 if (RT_SUCCESS(vrc))
647 RTFileClose(f);
648 if (!fForceOverwrite)
649 rc = setError(VBOX_E_FILE_ERROR,
650 tr("Machine settings file '%s' already exists"),
651 mData->m_strConfigFileFull.c_str());
652 else
653 {
654 /* try to delete the config file, as otherwise the creation
655 * of a new settings file will fail. */
656 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
657 if (RT_FAILURE(vrc2))
658 rc = setError(VBOX_E_FILE_ERROR,
659 tr("Could not delete the existing settings file '%s' (%Rrc)"),
660 mData->m_strConfigFileFull.c_str(), vrc2);
661 }
662 }
663 else if ( vrc != VERR_FILE_NOT_FOUND
664 && vrc != VERR_PATH_NOT_FOUND
665 )
666 rc = setError(VBOX_E_FILE_ERROR,
667 tr("Invalid machine settings file name '%s' (%Rrc)"),
668 mData->m_strConfigFileFull.c_str(),
669 vrc);
670 return rc;
671}
672
673/**
674 * Initializes the registered machine by loading the settings file.
675 * This method is separated from #init() in order to make it possible to
676 * retry the operation after VirtualBox startup instead of refusing to
677 * startup the whole VirtualBox server in case if the settings file of some
678 * registered VM is invalid or inaccessible.
679 *
680 * @note Must be always called from this object's write lock
681 * (unless called from #init() that doesn't need any locking).
682 * @note Locks the mUSBController method for writing.
683 * @note Subclasses must not call this method.
684 */
685HRESULT Machine::registeredInit()
686{
687 AssertReturn(!isSessionMachine(), E_FAIL);
688 AssertReturn(!isSnapshotMachine(), E_FAIL);
689 AssertReturn(mData->mUuid.isValid(), E_FAIL);
690 AssertReturn(!mData->mAccessible, E_FAIL);
691
692 HRESULT rc = initDataAndChildObjects();
693
694 if (SUCCEEDED(rc))
695 {
696 /* Temporarily reset the registered flag in order to let setters
697 * potentially called from loadSettings() succeed (isMutable() used in
698 * all setters will return FALSE for a Machine instance if mRegistered
699 * is TRUE). */
700 mData->mRegistered = FALSE;
701
702 try
703 {
704 // load and parse machine XML; this will throw on XML or logic errors
705 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
706
707 if (mData->mUuid != mData->pMachineConfigFile->uuid)
708 throw setError(E_FAIL,
709 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
710 mData->pMachineConfigFile->uuid.raw(),
711 mData->m_strConfigFileFull.c_str(),
712 mData->mUuid.toString().c_str(),
713 mParent->settingsFilePath().c_str());
714
715 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
716 NULL /* const Guid *puuidRegistry */);
717 if (FAILED(rc)) throw rc;
718 }
719 catch (HRESULT err)
720 {
721 /* we assume that error info is set by the thrower */
722 rc = err;
723 }
724 catch (...)
725 {
726 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
727 }
728
729 /* Restore the registered flag (even on failure) */
730 mData->mRegistered = TRUE;
731 }
732
733 if (SUCCEEDED(rc))
734 {
735 /* Set mAccessible to TRUE only if we successfully locked and loaded
736 * the settings file */
737 mData->mAccessible = TRUE;
738
739 /* commit all changes made during loading the settings file */
740 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
741 /// @todo r=klaus for some reason the settings loading logic backs up
742 // the settings, and therefore a commit is needed. Should probably be changed.
743 }
744 else
745 {
746 /* If the machine is registered, then, instead of returning a
747 * failure, we mark it as inaccessible and set the result to
748 * success to give it a try later */
749
750 /* fetch the current error info */
751 mData->mAccessError = com::ErrorInfo();
752 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
753 mData->mUuid.raw(),
754 mData->mAccessError.getText().raw()));
755
756 /* rollback all changes */
757 rollback(false /* aNotify */);
758
759 // uninit media from this machine's media registry, or else
760 // reloading the settings will fail
761 mParent->unregisterMachineMedia(getId());
762
763 /* uninitialize the common part to make sure all data is reset to
764 * default (null) values */
765 uninitDataAndChildObjects();
766
767 rc = S_OK;
768 }
769
770 return rc;
771}
772
773/**
774 * Uninitializes the instance.
775 * Called either from FinalRelease() or by the parent when it gets destroyed.
776 *
777 * @note The caller of this method must make sure that this object
778 * a) doesn't have active callers on the current thread and b) is not locked
779 * by the current thread; otherwise uninit() will hang either a) due to
780 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
781 * a dead-lock caused by this thread waiting for all callers on the other
782 * threads are done but preventing them from doing so by holding a lock.
783 */
784void Machine::uninit()
785{
786 LogFlowThisFuncEnter();
787
788 Assert(!isWriteLockOnCurrentThread());
789
790 Assert(!uRegistryNeedsSaving);
791 if (uRegistryNeedsSaving)
792 {
793 AutoCaller autoCaller(this);
794 if (SUCCEEDED(autoCaller.rc()))
795 {
796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
797 saveSettings(NULL, Machine::SaveS_Force);
798 }
799 }
800
801 /* Enclose the state transition Ready->InUninit->NotReady */
802 AutoUninitSpan autoUninitSpan(this);
803 if (autoUninitSpan.uninitDone())
804 return;
805
806 Assert(!isSnapshotMachine());
807 Assert(!isSessionMachine());
808 Assert(!!mData);
809
810 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
811 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
812
813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
814
815 if (!mData->mSession.mMachine.isNull())
816 {
817 /* Theoretically, this can only happen if the VirtualBox server has been
818 * terminated while there were clients running that owned open direct
819 * sessions. Since in this case we are definitely called by
820 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
821 * won't happen on the client watcher thread (because it does
822 * VirtualBox::addCaller() for the duration of the
823 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
824 * cannot happen until the VirtualBox caller is released). This is
825 * important, because SessionMachine::uninit() cannot correctly operate
826 * after we return from this method (it expects the Machine instance is
827 * still valid). We'll call it ourselves below.
828 */
829 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
830 (SessionMachine*)mData->mSession.mMachine));
831
832 if (Global::IsOnlineOrTransient(mData->mMachineState))
833 {
834 LogWarningThisFunc(("Setting state to Aborted!\n"));
835 /* set machine state using SessionMachine reimplementation */
836 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
837 }
838
839 /*
840 * Uninitialize SessionMachine using public uninit() to indicate
841 * an unexpected uninitialization.
842 */
843 mData->mSession.mMachine->uninit();
844 /* SessionMachine::uninit() must set mSession.mMachine to null */
845 Assert(mData->mSession.mMachine.isNull());
846 }
847
848 // uninit media from this machine's media registry, if they're still there
849 Guid uuidMachine(getId());
850
851 /* the lock is no more necessary (SessionMachine is uninitialized) */
852 alock.release();
853
854 /* XXX This will fail with
855 * "cannot be closed because it is still attached to 1 virtual machines"
856 * because at this point we did not call uninitDataAndChildObjects() yet
857 * and therefore also removeBackReference() for all these mediums was not called! */
858
859 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
860 mParent->unregisterMachineMedia(uuidMachine);
861
862 // has machine been modified?
863 if (mData->flModifications)
864 {
865 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
866 rollback(false /* aNotify */);
867 }
868
869 if (mData->mAccessible)
870 uninitDataAndChildObjects();
871
872 /* free the essential data structure last */
873 mData.free();
874
875 LogFlowThisFuncLeave();
876}
877
878// IMachine properties
879/////////////////////////////////////////////////////////////////////////////
880
881STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
882{
883 CheckComArgOutPointerValid(aParent);
884
885 AutoLimitedCaller autoCaller(this);
886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
887
888 /* mParent is constant during life time, no need to lock */
889 ComObjPtr<VirtualBox> pVirtualBox(mParent);
890 pVirtualBox.queryInterfaceTo(aParent);
891
892 return S_OK;
893}
894
895STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
896{
897 CheckComArgOutPointerValid(aAccessible);
898
899 AutoLimitedCaller autoCaller(this);
900 if (FAILED(autoCaller.rc())) return autoCaller.rc();
901
902 LogFlowThisFunc(("ENTER\n"));
903
904 /* In some cases (medium registry related), it is necessary to be able to
905 * go through the list of all machines. Happens when an inaccessible VM
906 * has a sensible medium registry. */
907 AutoReadLock mllock(mParent->getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
909
910 HRESULT rc = S_OK;
911
912 if (!mData->mAccessible)
913 {
914 /* try to initialize the VM once more if not accessible */
915
916 AutoReinitSpan autoReinitSpan(this);
917 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
918
919#ifdef DEBUG
920 LogFlowThisFunc(("Dumping media backreferences\n"));
921 mParent->dumpAllBackRefs();
922#endif
923
924 if (mData->pMachineConfigFile)
925 {
926 // reset the XML file to force loadSettings() (called from registeredInit())
927 // to parse it again; the file might have changed
928 delete mData->pMachineConfigFile;
929 mData->pMachineConfigFile = NULL;
930 }
931
932 rc = registeredInit();
933
934 if (SUCCEEDED(rc) && mData->mAccessible)
935 {
936 autoReinitSpan.setSucceeded();
937
938 /* make sure interesting parties will notice the accessibility
939 * state change */
940 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
941 mParent->onMachineDataChange(mData->mUuid);
942 }
943 }
944
945 if (SUCCEEDED(rc))
946 *aAccessible = mData->mAccessible;
947
948 LogFlowThisFuncLeave();
949
950 return rc;
951}
952
953STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
954{
955 CheckComArgOutPointerValid(aAccessError);
956
957 AutoLimitedCaller autoCaller(this);
958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
959
960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
961
962 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
963 {
964 /* return shortly */
965 aAccessError = NULL;
966 return S_OK;
967 }
968
969 HRESULT rc = S_OK;
970
971 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
972 rc = errorInfo.createObject();
973 if (SUCCEEDED(rc))
974 {
975 errorInfo->init(mData->mAccessError.getResultCode(),
976 mData->mAccessError.getInterfaceID().ref(),
977 Utf8Str(mData->mAccessError.getComponent()).c_str(),
978 Utf8Str(mData->mAccessError.getText()));
979 rc = errorInfo.queryInterfaceTo(aAccessError);
980 }
981
982 return rc;
983}
984
985STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
986{
987 CheckComArgOutPointerValid(aName);
988
989 AutoCaller autoCaller(this);
990 if (FAILED(autoCaller.rc())) return autoCaller.rc();
991
992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
993
994 mUserData->s.strName.cloneTo(aName);
995
996 return S_OK;
997}
998
999STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1000{
1001 CheckComArgStrNotEmptyOrNull(aName);
1002
1003 AutoCaller autoCaller(this);
1004 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1005
1006 // prohibit setting a UUID only as the machine name, or else it can
1007 // never be found by findMachine()
1008 Guid test(aName);
1009
1010 if (test.isValid())
1011 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1012
1013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1014
1015 HRESULT rc = checkStateDependency(MutableStateDep);
1016 if (FAILED(rc)) return rc;
1017
1018 setModified(IsModified_MachineData);
1019 mUserData.backup();
1020 mUserData->s.strName = aName;
1021
1022 return S_OK;
1023}
1024
1025STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1026{
1027 CheckComArgOutPointerValid(aDescription);
1028
1029 AutoCaller autoCaller(this);
1030 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1031
1032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1033
1034 mUserData->s.strDescription.cloneTo(aDescription);
1035
1036 return S_OK;
1037}
1038
1039STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1040{
1041 AutoCaller autoCaller(this);
1042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1043
1044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1045
1046 // this can be done in principle in any state as it doesn't affect the VM
1047 // significantly, but play safe by not messing around while complex
1048 // activities are going on
1049 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1050 if (FAILED(rc)) return rc;
1051
1052 setModified(IsModified_MachineData);
1053 mUserData.backup();
1054 mUserData->s.strDescription = aDescription;
1055
1056 return S_OK;
1057}
1058
1059STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1060{
1061 CheckComArgOutPointerValid(aId);
1062
1063 AutoLimitedCaller autoCaller(this);
1064 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1065
1066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1067
1068 mData->mUuid.toUtf16().cloneTo(aId);
1069
1070 return S_OK;
1071}
1072
1073STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1074{
1075 CheckComArgOutSafeArrayPointerValid(aGroups);
1076
1077 AutoCaller autoCaller(this);
1078 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1079
1080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1081 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1082 size_t i = 0;
1083 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1084 it != mUserData->s.llGroups.end();
1085 ++it, i++)
1086 {
1087 Bstr tmp = *it;
1088 tmp.cloneTo(&groups[i]);
1089 }
1090 groups.detachTo(ComSafeArrayOutArg(aGroups));
1091
1092 return S_OK;
1093}
1094
1095STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1096{
1097 AutoCaller autoCaller(this);
1098 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1099
1100 StringsList llGroups;
1101 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1102 if (FAILED(rc))
1103 return rc;
1104
1105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1106
1107 // changing machine groups is possible while the VM is offline
1108 rc = checkStateDependency(OfflineStateDep);
1109 if (FAILED(rc)) return rc;
1110
1111 setModified(IsModified_MachineData);
1112 mUserData.backup();
1113 mUserData->s.llGroups = llGroups;
1114
1115 return S_OK;
1116}
1117
1118STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1119{
1120 CheckComArgOutPointerValid(aOSTypeId);
1121
1122 AutoCaller autoCaller(this);
1123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1124
1125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1126
1127 mUserData->s.strOsType.cloneTo(aOSTypeId);
1128
1129 return S_OK;
1130}
1131
1132STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1133{
1134 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1135
1136 AutoCaller autoCaller(this);
1137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1138
1139 /* look up the object by Id to check it is valid */
1140 ComPtr<IGuestOSType> guestOSType;
1141 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1142 if (FAILED(rc)) return rc;
1143
1144 /* when setting, always use the "etalon" value for consistency -- lookup
1145 * by ID is case-insensitive and the input value may have different case */
1146 Bstr osTypeId;
1147 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1148 if (FAILED(rc)) return rc;
1149
1150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1151
1152 rc = checkStateDependency(MutableStateDep);
1153 if (FAILED(rc)) return rc;
1154
1155 setModified(IsModified_MachineData);
1156 mUserData.backup();
1157 mUserData->s.strOsType = osTypeId;
1158
1159 return S_OK;
1160}
1161
1162
1163STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1164{
1165 CheckComArgOutPointerValid(aFirmwareType);
1166
1167 AutoCaller autoCaller(this);
1168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1169
1170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1171
1172 *aFirmwareType = mHWData->mFirmwareType;
1173
1174 return S_OK;
1175}
1176
1177STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1178{
1179 AutoCaller autoCaller(this);
1180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1181 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1182
1183 HRESULT rc = checkStateDependency(MutableStateDep);
1184 if (FAILED(rc)) return rc;
1185
1186 setModified(IsModified_MachineData);
1187 mHWData.backup();
1188 mHWData->mFirmwareType = aFirmwareType;
1189
1190 return S_OK;
1191}
1192
1193STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1194{
1195 CheckComArgOutPointerValid(aKeyboardHIDType);
1196
1197 AutoCaller autoCaller(this);
1198 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1199
1200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1201
1202 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1203
1204 return S_OK;
1205}
1206
1207STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1208{
1209 AutoCaller autoCaller(this);
1210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1212
1213 HRESULT rc = checkStateDependency(MutableStateDep);
1214 if (FAILED(rc)) return rc;
1215
1216 setModified(IsModified_MachineData);
1217 mHWData.backup();
1218 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1219
1220 return S_OK;
1221}
1222
1223STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1224{
1225 CheckComArgOutPointerValid(aPointingHIDType);
1226
1227 AutoCaller autoCaller(this);
1228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1229
1230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1231
1232 *aPointingHIDType = mHWData->mPointingHIDType;
1233
1234 return S_OK;
1235}
1236
1237STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1238{
1239 AutoCaller autoCaller(this);
1240 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 HRESULT rc = checkStateDependency(MutableStateDep);
1244 if (FAILED(rc)) return rc;
1245
1246 setModified(IsModified_MachineData);
1247 mHWData.backup();
1248 mHWData->mPointingHIDType = aPointingHIDType;
1249
1250 return S_OK;
1251}
1252
1253STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1254{
1255 CheckComArgOutPointerValid(aChipsetType);
1256
1257 AutoCaller autoCaller(this);
1258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1259
1260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1261
1262 *aChipsetType = mHWData->mChipsetType;
1263
1264 return S_OK;
1265}
1266
1267STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1268{
1269 AutoCaller autoCaller(this);
1270 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1272
1273 HRESULT rc = checkStateDependency(MutableStateDep);
1274 if (FAILED(rc)) return rc;
1275
1276 if (aChipsetType != mHWData->mChipsetType)
1277 {
1278 setModified(IsModified_MachineData);
1279 mHWData.backup();
1280 mHWData->mChipsetType = aChipsetType;
1281
1282 // Resize network adapter array, to be finalized on commit/rollback.
1283 // We must not throw away entries yet, otherwise settings are lost
1284 // without a way to roll back.
1285 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1286 size_t oldCount = mNetworkAdapters.size();
1287 if (newCount > oldCount)
1288 {
1289 mNetworkAdapters.resize(newCount);
1290 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1291 {
1292 unconst(mNetworkAdapters[slot]).createObject();
1293 mNetworkAdapters[slot]->init(this, slot);
1294 }
1295 }
1296 }
1297
1298 return S_OK;
1299}
1300
1301STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1302{
1303 CheckComArgOutPointerValid(aHWVersion);
1304
1305 AutoCaller autoCaller(this);
1306 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1307
1308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1309
1310 mHWData->mHWVersion.cloneTo(aHWVersion);
1311
1312 return S_OK;
1313}
1314
1315STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1316{
1317 /* check known version */
1318 Utf8Str hwVersion = aHWVersion;
1319 if ( hwVersion.compare("1") != 0
1320 && hwVersion.compare("2") != 0)
1321 return setError(E_INVALIDARG,
1322 tr("Invalid hardware version: %ls\n"), aHWVersion);
1323
1324 AutoCaller autoCaller(this);
1325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1326
1327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1328
1329 HRESULT rc = checkStateDependency(MutableStateDep);
1330 if (FAILED(rc)) return rc;
1331
1332 setModified(IsModified_MachineData);
1333 mHWData.backup();
1334 mHWData->mHWVersion = hwVersion;
1335
1336 return S_OK;
1337}
1338
1339STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1340{
1341 CheckComArgOutPointerValid(aUUID);
1342
1343 AutoCaller autoCaller(this);
1344 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1345
1346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1347
1348 if (mHWData->mHardwareUUID.isValid())
1349 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1350 else
1351 mData->mUuid.toUtf16().cloneTo(aUUID);
1352
1353 return S_OK;
1354}
1355
1356STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1357{
1358 Guid hardwareUUID(aUUID);
1359 if (!hardwareUUID.isValid())
1360 return E_INVALIDARG;
1361
1362 AutoCaller autoCaller(this);
1363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1364
1365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1366
1367 HRESULT rc = checkStateDependency(MutableStateDep);
1368 if (FAILED(rc)) return rc;
1369
1370 setModified(IsModified_MachineData);
1371 mHWData.backup();
1372 if (hardwareUUID == mData->mUuid)
1373 mHWData->mHardwareUUID.clear();
1374 else
1375 mHWData->mHardwareUUID = hardwareUUID;
1376
1377 return S_OK;
1378}
1379
1380STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1381{
1382 CheckComArgOutPointerValid(memorySize);
1383
1384 AutoCaller autoCaller(this);
1385 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1386
1387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1388
1389 *memorySize = mHWData->mMemorySize;
1390
1391 return S_OK;
1392}
1393
1394STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1395{
1396 /* check RAM limits */
1397 if ( memorySize < MM_RAM_MIN_IN_MB
1398 || memorySize > MM_RAM_MAX_IN_MB
1399 )
1400 return setError(E_INVALIDARG,
1401 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1402 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1403
1404 AutoCaller autoCaller(this);
1405 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1406
1407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1408
1409 HRESULT rc = checkStateDependency(MutableStateDep);
1410 if (FAILED(rc)) return rc;
1411
1412 setModified(IsModified_MachineData);
1413 mHWData.backup();
1414 mHWData->mMemorySize = memorySize;
1415
1416 return S_OK;
1417}
1418
1419STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1420{
1421 CheckComArgOutPointerValid(CPUCount);
1422
1423 AutoCaller autoCaller(this);
1424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1425
1426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1427
1428 *CPUCount = mHWData->mCPUCount;
1429
1430 return S_OK;
1431}
1432
1433STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1434{
1435 /* check CPU limits */
1436 if ( CPUCount < SchemaDefs::MinCPUCount
1437 || CPUCount > SchemaDefs::MaxCPUCount
1438 )
1439 return setError(E_INVALIDARG,
1440 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1441 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1442
1443 AutoCaller autoCaller(this);
1444 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1445
1446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1447
1448 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1449 if (mHWData->mCPUHotPlugEnabled)
1450 {
1451 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1452 {
1453 if (mHWData->mCPUAttached[idx])
1454 return setError(E_INVALIDARG,
1455 tr("There is still a CPU attached to socket %lu."
1456 "Detach the CPU before removing the socket"),
1457 CPUCount, idx+1);
1458 }
1459 }
1460
1461 HRESULT rc = checkStateDependency(MutableStateDep);
1462 if (FAILED(rc)) return rc;
1463
1464 setModified(IsModified_MachineData);
1465 mHWData.backup();
1466 mHWData->mCPUCount = CPUCount;
1467
1468 return S_OK;
1469}
1470
1471STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1472{
1473 CheckComArgOutPointerValid(aExecutionCap);
1474
1475 AutoCaller autoCaller(this);
1476 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1477
1478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1479
1480 *aExecutionCap = mHWData->mCpuExecutionCap;
1481
1482 return S_OK;
1483}
1484
1485STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1486{
1487 HRESULT rc = S_OK;
1488
1489 /* check throttle limits */
1490 if ( aExecutionCap < 1
1491 || aExecutionCap > 100
1492 )
1493 return setError(E_INVALIDARG,
1494 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1495 aExecutionCap, 1, 100);
1496
1497 AutoCaller autoCaller(this);
1498 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1499
1500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1501
1502 alock.release();
1503 rc = onCPUExecutionCapChange(aExecutionCap);
1504 alock.acquire();
1505 if (FAILED(rc)) return rc;
1506
1507 setModified(IsModified_MachineData);
1508 mHWData.backup();
1509 mHWData->mCpuExecutionCap = aExecutionCap;
1510
1511 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1512 if (Global::IsOnline(mData->mMachineState))
1513 saveSettings(NULL);
1514
1515 return S_OK;
1516}
1517
1518
1519STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1520{
1521 CheckComArgOutPointerValid(aEnabled);
1522
1523 AutoCaller autoCaller(this);
1524 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1525
1526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1527
1528 *aEnabled = mHWData->mCPUHotPlugEnabled;
1529
1530 return S_OK;
1531}
1532
1533STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1534{
1535 HRESULT rc = S_OK;
1536
1537 AutoCaller autoCaller(this);
1538 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1539
1540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1541
1542 rc = checkStateDependency(MutableStateDep);
1543 if (FAILED(rc)) return rc;
1544
1545 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1546 {
1547 if (aEnabled)
1548 {
1549 setModified(IsModified_MachineData);
1550 mHWData.backup();
1551
1552 /* Add the amount of CPUs currently attached */
1553 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1554 {
1555 mHWData->mCPUAttached[i] = true;
1556 }
1557 }
1558 else
1559 {
1560 /*
1561 * We can disable hotplug only if the amount of maximum CPUs is equal
1562 * to the amount of attached CPUs
1563 */
1564 unsigned cCpusAttached = 0;
1565 unsigned iHighestId = 0;
1566
1567 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1568 {
1569 if (mHWData->mCPUAttached[i])
1570 {
1571 cCpusAttached++;
1572 iHighestId = i;
1573 }
1574 }
1575
1576 if ( (cCpusAttached != mHWData->mCPUCount)
1577 || (iHighestId >= mHWData->mCPUCount))
1578 return setError(E_INVALIDARG,
1579 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1580
1581 setModified(IsModified_MachineData);
1582 mHWData.backup();
1583 }
1584 }
1585
1586 mHWData->mCPUHotPlugEnabled = aEnabled;
1587
1588 return rc;
1589}
1590
1591STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1592{
1593#ifdef VBOX_WITH_USB_CARDREADER
1594 CheckComArgOutPointerValid(aEnabled);
1595
1596 AutoCaller autoCaller(this);
1597 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1598
1599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1600
1601 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1602
1603 return S_OK;
1604#else
1605 NOREF(aEnabled);
1606 return E_NOTIMPL;
1607#endif
1608}
1609
1610STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1611{
1612#ifdef VBOX_WITH_USB_CARDREADER
1613 AutoCaller autoCaller(this);
1614 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1616
1617 HRESULT rc = checkStateDependency(MutableStateDep);
1618 if (FAILED(rc)) return rc;
1619
1620 setModified(IsModified_MachineData);
1621 mHWData.backup();
1622 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1623
1624 return S_OK;
1625#else
1626 NOREF(aEnabled);
1627 return E_NOTIMPL;
1628#endif
1629}
1630
1631STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1632{
1633 CheckComArgOutPointerValid(aEnabled);
1634
1635 AutoCaller autoCaller(this);
1636 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1638
1639 *aEnabled = mHWData->mHPETEnabled;
1640
1641 return S_OK;
1642}
1643
1644STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1645{
1646 HRESULT rc = S_OK;
1647
1648 AutoCaller autoCaller(this);
1649 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1651
1652 rc = checkStateDependency(MutableStateDep);
1653 if (FAILED(rc)) return rc;
1654
1655 setModified(IsModified_MachineData);
1656 mHWData.backup();
1657
1658 mHWData->mHPETEnabled = aEnabled;
1659
1660 return rc;
1661}
1662
1663STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1664{
1665 AutoCaller autoCaller(this);
1666 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1667
1668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1669
1670 *fEnabled = mHWData->mVideoCaptureEnabled;
1671 return S_OK;
1672}
1673
1674STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1675{
1676 HRESULT rc = S_OK;
1677
1678 AutoCaller autoCaller(this);
1679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1681
1682 setModified(IsModified_MachineData);
1683 mHWData.backup();
1684 mHWData->mVideoCaptureEnabled = fEnabled;
1685
1686 alock.release();
1687 rc = onVideoCaptureChange();
1688 alock.acquire();
1689 if (FAILED(rc))
1690 {
1691 /*
1692 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1693 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1694 * determine if it should start or stop capturing. Therefore we need to manually
1695 * undo change.
1696 */
1697 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1698 return rc;
1699 }
1700
1701 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1702 if (Global::IsOnline(mData->mMachineState))
1703 saveSettings(NULL);
1704
1705 return rc;
1706}
1707
1708STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1709{
1710 CheckComArgOutSafeArrayPointerValid(aScreens);
1711
1712 AutoCaller autoCaller(this);
1713 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1714
1715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1718 for (unsigned i = 0; i < screens.size(); i++)
1719 screens[i] = mHWData->maVideoCaptureScreens[i];
1720 screens.detachTo(ComSafeArrayOutArg(aScreens));
1721 return S_OK;
1722}
1723
1724STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1725{
1726 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1727 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1728 bool fChanged = false;
1729
1730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1731
1732 for (unsigned i = 0; i < screens.size(); i++)
1733 {
1734 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1735 {
1736 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1737 fChanged = true;
1738 }
1739 }
1740 if (fChanged)
1741 {
1742 alock.release();
1743 HRESULT rc = onVideoCaptureChange();
1744 alock.acquire();
1745 if (FAILED(rc)) return rc;
1746 setModified(IsModified_MachineData);
1747
1748 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1749 if (Global::IsOnline(mData->mMachineState))
1750 saveSettings(NULL);
1751 }
1752
1753 return S_OK;
1754}
1755
1756STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1757{
1758 AutoCaller autoCaller(this);
1759 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1760
1761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1762 if (mHWData->mVideoCaptureFile.isEmpty())
1763 {
1764 Utf8Str defaultFile;
1765 getDefaultVideoCaptureFile(defaultFile);
1766 defaultFile.cloneTo(apFile);
1767 }
1768 else
1769 mHWData->mVideoCaptureFile.cloneTo(apFile);
1770 return S_OK;
1771}
1772
1773STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1774{
1775 Utf8Str strFile(aFile);
1776 AutoCaller autoCaller(this);
1777 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1778
1779 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1780
1781 if ( Global::IsOnline(mData->mMachineState)
1782 && mHWData->mVideoCaptureEnabled)
1783 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1784
1785 if (!RTPathStartsWithRoot(strFile.c_str()))
1786 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1787
1788 if (!strFile.isEmpty())
1789 {
1790 Utf8Str defaultFile;
1791 getDefaultVideoCaptureFile(defaultFile);
1792 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1793 strFile.setNull();
1794 }
1795
1796 setModified(IsModified_MachineData);
1797 mHWData.backup();
1798 mHWData->mVideoCaptureFile = strFile;
1799
1800 return S_OK;
1801}
1802
1803STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1804{
1805 AutoCaller autoCaller(this);
1806 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1807
1808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1809 *aHorzRes = mHWData->mVideoCaptureWidth;
1810 return S_OK;
1811}
1812
1813STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1814{
1815 AutoCaller autoCaller(this);
1816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1817
1818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1819
1820 if ( Global::IsOnline(mData->mMachineState)
1821 && mHWData->mVideoCaptureEnabled)
1822 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1823
1824 setModified(IsModified_MachineData);
1825 mHWData.backup();
1826 mHWData->mVideoCaptureWidth = aHorzRes;
1827
1828 return S_OK;
1829}
1830
1831STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1832{
1833 AutoCaller autoCaller(this);
1834 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1835
1836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1837 *aVertRes = mHWData->mVideoCaptureHeight;
1838 return S_OK;
1839}
1840
1841STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1842{
1843 AutoCaller autoCaller(this);
1844 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1845
1846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 if ( Global::IsOnline(mData->mMachineState)
1849 && mHWData->mVideoCaptureEnabled)
1850 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1851
1852 setModified(IsModified_MachineData);
1853 mHWData.backup();
1854 mHWData->mVideoCaptureHeight = aVertRes;
1855
1856 return S_OK;
1857}
1858
1859STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1860{
1861 AutoCaller autoCaller(this);
1862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1863
1864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1865 *aRate = mHWData->mVideoCaptureRate;
1866 return S_OK;
1867}
1868
1869STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1870{
1871 AutoCaller autoCaller(this);
1872 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1873
1874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1875
1876 if ( Global::IsOnline(mData->mMachineState)
1877 && mHWData->mVideoCaptureEnabled)
1878 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1879
1880 setModified(IsModified_MachineData);
1881 mHWData.backup();
1882 mHWData->mVideoCaptureRate = aRate;
1883
1884 return S_OK;
1885}
1886
1887STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1888{
1889 AutoCaller autoCaller(this);
1890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1891
1892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1893 *aFPS = mHWData->mVideoCaptureFPS;
1894 return S_OK;
1895}
1896
1897STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1898{
1899 AutoCaller autoCaller(this);
1900 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1901
1902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1903
1904 if ( Global::IsOnline(mData->mMachineState)
1905 && mHWData->mVideoCaptureEnabled)
1906 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1907
1908 setModified(IsModified_MachineData);
1909 mHWData.backup();
1910 mHWData->mVideoCaptureFPS = aFPS;
1911
1912 return S_OK;
1913}
1914
1915STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1916{
1917 CheckComArgOutPointerValid(aGraphicsControllerType);
1918
1919 AutoCaller autoCaller(this);
1920 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1921
1922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1923
1924 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1925
1926 return S_OK;
1927}
1928
1929STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1930{
1931 switch (aGraphicsControllerType)
1932 {
1933 case GraphicsControllerType_Null:
1934 case GraphicsControllerType_VBoxVGA:
1935 break;
1936 default:
1937 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1938 }
1939
1940 AutoCaller autoCaller(this);
1941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1942
1943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1944
1945 HRESULT rc = checkStateDependency(MutableStateDep);
1946 if (FAILED(rc)) return rc;
1947
1948 setModified(IsModified_MachineData);
1949 mHWData.backup();
1950 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1951
1952 return S_OK;
1953}
1954
1955STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1956{
1957 CheckComArgOutPointerValid(memorySize);
1958
1959 AutoCaller autoCaller(this);
1960 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1961
1962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1963
1964 *memorySize = mHWData->mVRAMSize;
1965
1966 return S_OK;
1967}
1968
1969STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1970{
1971 /* check VRAM limits */
1972 if (memorySize < SchemaDefs::MinGuestVRAM ||
1973 memorySize > SchemaDefs::MaxGuestVRAM)
1974 return setError(E_INVALIDARG,
1975 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1976 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1977
1978 AutoCaller autoCaller(this);
1979 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1980
1981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1982
1983 HRESULT rc = checkStateDependency(MutableStateDep);
1984 if (FAILED(rc)) return rc;
1985
1986 setModified(IsModified_MachineData);
1987 mHWData.backup();
1988 mHWData->mVRAMSize = memorySize;
1989
1990 return S_OK;
1991}
1992
1993/** @todo this method should not be public */
1994STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1995{
1996 CheckComArgOutPointerValid(memoryBalloonSize);
1997
1998 AutoCaller autoCaller(this);
1999 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2000
2001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2002
2003 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2004
2005 return S_OK;
2006}
2007
2008/**
2009 * Set the memory balloon size.
2010 *
2011 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2012 * we have to make sure that we never call IGuest from here.
2013 */
2014STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2015{
2016 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2017#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2018 /* check limits */
2019 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2020 return setError(E_INVALIDARG,
2021 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2022 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2023
2024 AutoCaller autoCaller(this);
2025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2026
2027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2028
2029 setModified(IsModified_MachineData);
2030 mHWData.backup();
2031 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2032
2033 return S_OK;
2034#else
2035 NOREF(memoryBalloonSize);
2036 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2037#endif
2038}
2039
2040STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2041{
2042 CheckComArgOutPointerValid(aEnabled);
2043
2044 AutoCaller autoCaller(this);
2045 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2046
2047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2048
2049 *aEnabled = mHWData->mPageFusionEnabled;
2050 return S_OK;
2051}
2052
2053STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2054{
2055#ifdef VBOX_WITH_PAGE_SHARING
2056 AutoCaller autoCaller(this);
2057 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2058
2059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2060
2061 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2062 setModified(IsModified_MachineData);
2063 mHWData.backup();
2064 mHWData->mPageFusionEnabled = aEnabled;
2065 return S_OK;
2066#else
2067 NOREF(aEnabled);
2068 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2069#endif
2070}
2071
2072STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2073{
2074 CheckComArgOutPointerValid(aEnabled);
2075
2076 AutoCaller autoCaller(this);
2077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2078
2079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2080
2081 *aEnabled = mHWData->mAccelerate3DEnabled;
2082
2083 return S_OK;
2084}
2085
2086STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2087{
2088 AutoCaller autoCaller(this);
2089 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2090
2091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2092
2093 HRESULT rc = checkStateDependency(MutableStateDep);
2094 if (FAILED(rc)) return rc;
2095
2096 /** @todo check validity! */
2097
2098 setModified(IsModified_MachineData);
2099 mHWData.backup();
2100 mHWData->mAccelerate3DEnabled = enable;
2101
2102 return S_OK;
2103}
2104
2105
2106STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2107{
2108 CheckComArgOutPointerValid(aEnabled);
2109
2110 AutoCaller autoCaller(this);
2111 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2112
2113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2114
2115 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2116
2117 return S_OK;
2118}
2119
2120STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2121{
2122 AutoCaller autoCaller(this);
2123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2124
2125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2126
2127 HRESULT rc = checkStateDependency(MutableStateDep);
2128 if (FAILED(rc)) return rc;
2129
2130 /** @todo check validity! */
2131
2132 setModified(IsModified_MachineData);
2133 mHWData.backup();
2134 mHWData->mAccelerate2DVideoEnabled = enable;
2135
2136 return S_OK;
2137}
2138
2139STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2140{
2141 CheckComArgOutPointerValid(monitorCount);
2142
2143 AutoCaller autoCaller(this);
2144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2145
2146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2147
2148 *monitorCount = mHWData->mMonitorCount;
2149
2150 return S_OK;
2151}
2152
2153STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2154{
2155 /* make sure monitor count is a sensible number */
2156 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2157 return setError(E_INVALIDARG,
2158 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2159 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2160
2161 AutoCaller autoCaller(this);
2162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2163
2164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2165
2166 HRESULT rc = checkStateDependency(MutableStateDep);
2167 if (FAILED(rc)) return rc;
2168
2169 setModified(IsModified_MachineData);
2170 mHWData.backup();
2171 mHWData->mMonitorCount = monitorCount;
2172
2173 return S_OK;
2174}
2175
2176STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2177{
2178 CheckComArgOutPointerValid(biosSettings);
2179
2180 AutoCaller autoCaller(this);
2181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2182
2183 /* mBIOSSettings is constant during life time, no need to lock */
2184 mBIOSSettings.queryInterfaceTo(biosSettings);
2185
2186 return S_OK;
2187}
2188
2189STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2190{
2191 CheckComArgOutPointerValid(aVal);
2192
2193 AutoCaller autoCaller(this);
2194 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2195
2196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2197
2198 switch (property)
2199 {
2200 case CPUPropertyType_PAE:
2201 *aVal = mHWData->mPAEEnabled;
2202 break;
2203
2204 case CPUPropertyType_Synthetic:
2205 *aVal = mHWData->mSyntheticCpu;
2206 break;
2207
2208 case CPUPropertyType_LongMode:
2209 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2210 *aVal = TRUE;
2211 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2212 *aVal = FALSE;
2213#if HC_ARCH_BITS == 64
2214 else
2215 *aVal = TRUE;
2216#else
2217 else
2218 {
2219 *aVal = FALSE;
2220
2221 ComPtr<IGuestOSType> ptrGuestOSType;
2222 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2223 if (SUCCEEDED(hrc2))
2224 {
2225 BOOL fIs64Bit = FALSE;
2226 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2227 if (SUCCEEDED(hrc2) && fIs64Bit)
2228 {
2229 ComObjPtr<Host> ptrHost = mParent->host();
2230 alock.release();
2231
2232 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2233 if (FAILED(hrc2))
2234 *aVal = FALSE;
2235 }
2236 }
2237 }
2238#endif
2239 break;
2240
2241 default:
2242 return E_INVALIDARG;
2243 }
2244 return S_OK;
2245}
2246
2247STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2248{
2249 AutoCaller autoCaller(this);
2250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2251
2252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2253
2254 HRESULT rc = checkStateDependency(MutableStateDep);
2255 if (FAILED(rc)) return rc;
2256
2257 switch (property)
2258 {
2259 case CPUPropertyType_PAE:
2260 setModified(IsModified_MachineData);
2261 mHWData.backup();
2262 mHWData->mPAEEnabled = !!aVal;
2263 break;
2264
2265 case CPUPropertyType_Synthetic:
2266 setModified(IsModified_MachineData);
2267 mHWData.backup();
2268 mHWData->mSyntheticCpu = !!aVal;
2269 break;
2270
2271 case CPUPropertyType_LongMode:
2272 setModified(IsModified_MachineData);
2273 mHWData.backup();
2274 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2275 break;
2276
2277 default:
2278 return E_INVALIDARG;
2279 }
2280 return S_OK;
2281}
2282
2283STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2284{
2285 CheckComArgOutPointerValid(aValEax);
2286 CheckComArgOutPointerValid(aValEbx);
2287 CheckComArgOutPointerValid(aValEcx);
2288 CheckComArgOutPointerValid(aValEdx);
2289
2290 AutoCaller autoCaller(this);
2291 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2292
2293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2294
2295 switch(aId)
2296 {
2297 case 0x0:
2298 case 0x1:
2299 case 0x2:
2300 case 0x3:
2301 case 0x4:
2302 case 0x5:
2303 case 0x6:
2304 case 0x7:
2305 case 0x8:
2306 case 0x9:
2307 case 0xA:
2308 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2309 return E_INVALIDARG;
2310
2311 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2312 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2313 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2314 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2315 break;
2316
2317 case 0x80000000:
2318 case 0x80000001:
2319 case 0x80000002:
2320 case 0x80000003:
2321 case 0x80000004:
2322 case 0x80000005:
2323 case 0x80000006:
2324 case 0x80000007:
2325 case 0x80000008:
2326 case 0x80000009:
2327 case 0x8000000A:
2328 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2329 return E_INVALIDARG;
2330
2331 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2332 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2333 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2334 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2335 break;
2336
2337 default:
2338 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2339 }
2340 return S_OK;
2341}
2342
2343STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2344{
2345 AutoCaller autoCaller(this);
2346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2347
2348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2349
2350 HRESULT rc = checkStateDependency(MutableStateDep);
2351 if (FAILED(rc)) return rc;
2352
2353 switch(aId)
2354 {
2355 case 0x0:
2356 case 0x1:
2357 case 0x2:
2358 case 0x3:
2359 case 0x4:
2360 case 0x5:
2361 case 0x6:
2362 case 0x7:
2363 case 0x8:
2364 case 0x9:
2365 case 0xA:
2366 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2367 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2368 setModified(IsModified_MachineData);
2369 mHWData.backup();
2370 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2371 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2372 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2373 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2374 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2375 break;
2376
2377 case 0x80000000:
2378 case 0x80000001:
2379 case 0x80000002:
2380 case 0x80000003:
2381 case 0x80000004:
2382 case 0x80000005:
2383 case 0x80000006:
2384 case 0x80000007:
2385 case 0x80000008:
2386 case 0x80000009:
2387 case 0x8000000A:
2388 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2389 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2390 setModified(IsModified_MachineData);
2391 mHWData.backup();
2392 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2393 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2394 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2395 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2396 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2397 break;
2398
2399 default:
2400 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2401 }
2402 return S_OK;
2403}
2404
2405STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2406{
2407 AutoCaller autoCaller(this);
2408 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2409
2410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2411
2412 HRESULT rc = checkStateDependency(MutableStateDep);
2413 if (FAILED(rc)) return rc;
2414
2415 switch(aId)
2416 {
2417 case 0x0:
2418 case 0x1:
2419 case 0x2:
2420 case 0x3:
2421 case 0x4:
2422 case 0x5:
2423 case 0x6:
2424 case 0x7:
2425 case 0x8:
2426 case 0x9:
2427 case 0xA:
2428 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2429 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2430 setModified(IsModified_MachineData);
2431 mHWData.backup();
2432 /* Invalidate leaf. */
2433 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2434 break;
2435
2436 case 0x80000000:
2437 case 0x80000001:
2438 case 0x80000002:
2439 case 0x80000003:
2440 case 0x80000004:
2441 case 0x80000005:
2442 case 0x80000006:
2443 case 0x80000007:
2444 case 0x80000008:
2445 case 0x80000009:
2446 case 0x8000000A:
2447 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2448 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2449 setModified(IsModified_MachineData);
2450 mHWData.backup();
2451 /* Invalidate leaf. */
2452 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2453 break;
2454
2455 default:
2456 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2457 }
2458 return S_OK;
2459}
2460
2461STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2462{
2463 AutoCaller autoCaller(this);
2464 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2465
2466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2467
2468 HRESULT rc = checkStateDependency(MutableStateDep);
2469 if (FAILED(rc)) return rc;
2470
2471 setModified(IsModified_MachineData);
2472 mHWData.backup();
2473
2474 /* Invalidate all standard leafs. */
2475 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2476 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2477
2478 /* Invalidate all extended leafs. */
2479 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2480 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2481
2482 return S_OK;
2483}
2484
2485STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2486{
2487 CheckComArgOutPointerValid(aVal);
2488
2489 AutoCaller autoCaller(this);
2490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2491
2492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2493
2494 switch(property)
2495 {
2496 case HWVirtExPropertyType_Enabled:
2497 *aVal = mHWData->mHWVirtExEnabled;
2498 break;
2499
2500 case HWVirtExPropertyType_VPID:
2501 *aVal = mHWData->mHWVirtExVPIDEnabled;
2502 break;
2503
2504 case HWVirtExPropertyType_NestedPaging:
2505 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2506 break;
2507
2508 case HWVirtExPropertyType_UnrestrictedExecution:
2509 *aVal = mHWData->mHWVirtExUXEnabled;
2510 break;
2511
2512 case HWVirtExPropertyType_LargePages:
2513 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2514#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2515 *aVal = FALSE;
2516#endif
2517 break;
2518
2519 case HWVirtExPropertyType_Force:
2520 *aVal = mHWData->mHWVirtExForceEnabled;
2521 break;
2522
2523 default:
2524 return E_INVALIDARG;
2525 }
2526 return S_OK;
2527}
2528
2529STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2530{
2531 AutoCaller autoCaller(this);
2532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2533
2534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 HRESULT rc = checkStateDependency(MutableStateDep);
2537 if (FAILED(rc)) return rc;
2538
2539 switch(property)
2540 {
2541 case HWVirtExPropertyType_Enabled:
2542 setModified(IsModified_MachineData);
2543 mHWData.backup();
2544 mHWData->mHWVirtExEnabled = !!aVal;
2545 break;
2546
2547 case HWVirtExPropertyType_VPID:
2548 setModified(IsModified_MachineData);
2549 mHWData.backup();
2550 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2551 break;
2552
2553 case HWVirtExPropertyType_NestedPaging:
2554 setModified(IsModified_MachineData);
2555 mHWData.backup();
2556 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2557 break;
2558
2559 case HWVirtExPropertyType_UnrestrictedExecution:
2560 setModified(IsModified_MachineData);
2561 mHWData.backup();
2562 mHWData->mHWVirtExUXEnabled = !!aVal;
2563 break;
2564
2565 case HWVirtExPropertyType_LargePages:
2566 setModified(IsModified_MachineData);
2567 mHWData.backup();
2568 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2569 break;
2570
2571 case HWVirtExPropertyType_Force:
2572 setModified(IsModified_MachineData);
2573 mHWData.backup();
2574 mHWData->mHWVirtExForceEnabled = !!aVal;
2575 break;
2576
2577 default:
2578 return E_INVALIDARG;
2579 }
2580
2581 return S_OK;
2582}
2583
2584STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2585{
2586 CheckComArgOutPointerValid(aSnapshotFolder);
2587
2588 AutoCaller autoCaller(this);
2589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2590
2591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2592
2593 Utf8Str strFullSnapshotFolder;
2594 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2595 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2596
2597 return S_OK;
2598}
2599
2600STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2601{
2602 /* @todo (r=dmik):
2603 * 1. Allow to change the name of the snapshot folder containing snapshots
2604 * 2. Rename the folder on disk instead of just changing the property
2605 * value (to be smart and not to leave garbage). Note that it cannot be
2606 * done here because the change may be rolled back. Thus, the right
2607 * place is #saveSettings().
2608 */
2609
2610 AutoCaller autoCaller(this);
2611 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2612
2613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615 HRESULT rc = checkStateDependency(MutableStateDep);
2616 if (FAILED(rc)) return rc;
2617
2618 if (!mData->mCurrentSnapshot.isNull())
2619 return setError(E_FAIL,
2620 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2621
2622 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2623
2624 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2625 if (strSnapshotFolder.isEmpty())
2626 strSnapshotFolder = "Snapshots";
2627 int vrc = calculateFullPath(strSnapshotFolder,
2628 strSnapshotFolder);
2629 if (RT_FAILURE(vrc))
2630 return setError(E_FAIL,
2631 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2632 aSnapshotFolder, vrc);
2633
2634 setModified(IsModified_MachineData);
2635 mUserData.backup();
2636
2637 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2638
2639 return S_OK;
2640}
2641
2642STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2643{
2644 CheckComArgOutSafeArrayPointerValid(aAttachments);
2645
2646 AutoCaller autoCaller(this);
2647 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2648
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2652 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2653
2654 return S_OK;
2655}
2656
2657STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2658{
2659 CheckComArgOutPointerValid(vrdeServer);
2660
2661 AutoCaller autoCaller(this);
2662 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2663
2664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2665
2666 Assert(!!mVRDEServer);
2667 mVRDEServer.queryInterfaceTo(vrdeServer);
2668
2669 return S_OK;
2670}
2671
2672STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2673{
2674 CheckComArgOutPointerValid(audioAdapter);
2675
2676 AutoCaller autoCaller(this);
2677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2678
2679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2680
2681 mAudioAdapter.queryInterfaceTo(audioAdapter);
2682 return S_OK;
2683}
2684
2685STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2686{
2687#ifdef VBOX_WITH_VUSB
2688 CheckComArgOutPointerValid(aUSBControllers);
2689
2690 AutoCaller autoCaller(this);
2691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2692
2693 clearError();
2694 MultiResult rc(S_OK);
2695
2696# ifdef VBOX_WITH_USB
2697 rc = mParent->host()->checkUSBProxyService();
2698 if (FAILED(rc)) return rc;
2699# endif
2700
2701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2702
2703 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2704 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2705 return S_OK;
2706#else
2707 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2708 * extended error info to indicate that USB is simply not available
2709 * (w/o treating it as a failure), for example, as in OSE */
2710 NOREF(aUSBControllers);
2711 ReturnComNotImplemented();
2712#endif /* VBOX_WITH_VUSB */
2713}
2714
2715STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2716{
2717#ifdef VBOX_WITH_VUSB
2718 CheckComArgOutPointerValid(aUSBDeviceFilters);
2719
2720 AutoCaller autoCaller(this);
2721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2722
2723 clearError();
2724 MultiResult rc(S_OK);
2725
2726# ifdef VBOX_WITH_USB
2727 rc = mParent->host()->checkUSBProxyService();
2728 if (FAILED(rc)) return rc;
2729# endif
2730
2731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2734#else
2735 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2736 * extended error info to indicate that USB is simply not available
2737 * (w/o treating it as a failure), for example, as in OSE */
2738 NOREF(aUSBDeviceFilters);
2739 ReturnComNotImplemented();
2740#endif /* VBOX_WITH_VUSB */
2741}
2742
2743STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2744{
2745 CheckComArgOutPointerValid(aFilePath);
2746
2747 AutoLimitedCaller autoCaller(this);
2748 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2749
2750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2751
2752 mData->m_strConfigFileFull.cloneTo(aFilePath);
2753 return S_OK;
2754}
2755
2756STDMETHODIMP Machine::COMSETTER(SettingsFilePath)(IN_BSTR aFilePath)
2757{
2758 CheckComArgStrNotEmptyOrNull(aFilePath);
2759
2760 AutoCaller autoCaller(this);
2761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2762
2763 ReturnComNotImplemented();
2764}
2765
2766STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2767{
2768 CheckComArgOutPointerValid(aModified);
2769
2770 AutoCaller autoCaller(this);
2771 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2772
2773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2774
2775 HRESULT rc = checkStateDependency(MutableStateDep);
2776 if (FAILED(rc)) return rc;
2777
2778 if (!mData->pMachineConfigFile->fileExists())
2779 // this is a new machine, and no config file exists yet:
2780 *aModified = TRUE;
2781 else
2782 *aModified = (mData->flModifications != 0);
2783
2784 return S_OK;
2785}
2786
2787STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2788{
2789 CheckComArgOutPointerValid(aSessionState);
2790
2791 AutoCaller autoCaller(this);
2792 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2793
2794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2795
2796 *aSessionState = mData->mSession.mState;
2797
2798 return S_OK;
2799}
2800
2801STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2802{
2803 CheckComArgOutPointerValid(aSessionType);
2804
2805 AutoCaller autoCaller(this);
2806 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2807
2808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2809
2810 mData->mSession.mType.cloneTo(aSessionType);
2811
2812 return S_OK;
2813}
2814
2815STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2816{
2817 CheckComArgOutPointerValid(aSessionPID);
2818
2819 AutoCaller autoCaller(this);
2820 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2821
2822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2823
2824 *aSessionPID = mData->mSession.mPID;
2825
2826 return S_OK;
2827}
2828
2829STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2830{
2831 CheckComArgOutPointerValid(machineState);
2832
2833 AutoCaller autoCaller(this);
2834 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2835
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 *machineState = mData->mMachineState;
2839
2840 return S_OK;
2841}
2842
2843STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2844{
2845 CheckComArgOutPointerValid(aLastStateChange);
2846
2847 AutoCaller autoCaller(this);
2848 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2849
2850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2851
2852 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2853
2854 return S_OK;
2855}
2856
2857STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2858{
2859 CheckComArgOutPointerValid(aStateFilePath);
2860
2861 AutoCaller autoCaller(this);
2862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2863
2864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2865
2866 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2867
2868 return S_OK;
2869}
2870
2871STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2872{
2873 CheckComArgOutPointerValid(aLogFolder);
2874
2875 AutoCaller autoCaller(this);
2876 AssertComRCReturnRC(autoCaller.rc());
2877
2878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2879
2880 Utf8Str logFolder;
2881 getLogFolder(logFolder);
2882 logFolder.cloneTo(aLogFolder);
2883
2884 return S_OK;
2885}
2886
2887STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2888{
2889 CheckComArgOutPointerValid(aCurrentSnapshot);
2890
2891 AutoCaller autoCaller(this);
2892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2893
2894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2895
2896 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2897
2898 return S_OK;
2899}
2900
2901STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2902{
2903 CheckComArgOutPointerValid(aSnapshotCount);
2904
2905 AutoCaller autoCaller(this);
2906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2907
2908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2909
2910 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2911 ? 0
2912 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2913
2914 return S_OK;
2915}
2916
2917STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2918{
2919 CheckComArgOutPointerValid(aCurrentStateModified);
2920
2921 AutoCaller autoCaller(this);
2922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2923
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 /* Note: for machines with no snapshots, we always return FALSE
2927 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2928 * reasons :) */
2929
2930 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2931 ? FALSE
2932 : mData->mCurrentStateModified;
2933
2934 return S_OK;
2935}
2936
2937STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2938{
2939 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2940
2941 AutoCaller autoCaller(this);
2942 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2943
2944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2945
2946 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2947 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2948
2949 return S_OK;
2950}
2951
2952STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2953{
2954 CheckComArgOutPointerValid(aClipboardMode);
2955
2956 AutoCaller autoCaller(this);
2957 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2958
2959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2960
2961 *aClipboardMode = mHWData->mClipboardMode;
2962
2963 return S_OK;
2964}
2965
2966STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2967{
2968 HRESULT rc = S_OK;
2969
2970 AutoCaller autoCaller(this);
2971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2972
2973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 alock.release();
2976 rc = onClipboardModeChange(aClipboardMode);
2977 alock.acquire();
2978 if (FAILED(rc)) return rc;
2979
2980 setModified(IsModified_MachineData);
2981 mHWData.backup();
2982 mHWData->mClipboardMode = aClipboardMode;
2983
2984 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2985 if (Global::IsOnline(mData->mMachineState))
2986 saveSettings(NULL);
2987
2988 return S_OK;
2989}
2990
2991STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2992{
2993 CheckComArgOutPointerValid(aDragAndDropMode);
2994
2995 AutoCaller autoCaller(this);
2996 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2997
2998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2999
3000 *aDragAndDropMode = mHWData->mDragAndDropMode;
3001
3002 return S_OK;
3003}
3004
3005STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3006{
3007 HRESULT rc = S_OK;
3008
3009 AutoCaller autoCaller(this);
3010 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3011
3012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3013
3014 alock.release();
3015 rc = onDragAndDropModeChange(aDragAndDropMode);
3016 alock.acquire();
3017 if (FAILED(rc)) return rc;
3018
3019 setModified(IsModified_MachineData);
3020 mHWData.backup();
3021 mHWData->mDragAndDropMode = aDragAndDropMode;
3022
3023 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3024 if (Global::IsOnline(mData->mMachineState))
3025 saveSettings(NULL);
3026
3027 return S_OK;
3028}
3029
3030STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3031{
3032 CheckComArgOutPointerValid(aPatterns);
3033
3034 AutoCaller autoCaller(this);
3035 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3036
3037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3038
3039 try
3040 {
3041 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3042 }
3043 catch (...)
3044 {
3045 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3046 }
3047
3048 return S_OK;
3049}
3050
3051STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3052{
3053 AutoCaller autoCaller(this);
3054 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3055
3056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 HRESULT rc = checkStateDependency(MutableStateDep);
3059 if (FAILED(rc)) return rc;
3060
3061 setModified(IsModified_MachineData);
3062 mHWData.backup();
3063 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3064 return rc;
3065}
3066
3067STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3068{
3069 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3070
3071 AutoCaller autoCaller(this);
3072 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3073
3074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3075
3076 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3077 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3078
3079 return S_OK;
3080}
3081
3082STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3083{
3084 CheckComArgOutPointerValid(aEnabled);
3085
3086 AutoCaller autoCaller(this);
3087 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3088
3089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3090
3091 *aEnabled = mUserData->s.fTeleporterEnabled;
3092
3093 return S_OK;
3094}
3095
3096STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3097{
3098 AutoCaller autoCaller(this);
3099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3100
3101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3102
3103 /* Only allow it to be set to true when PoweredOff or Aborted.
3104 (Clearing it is always permitted.) */
3105 if ( aEnabled
3106 && mData->mRegistered
3107 && ( !isSessionMachine()
3108 || ( mData->mMachineState != MachineState_PoweredOff
3109 && mData->mMachineState != MachineState_Teleported
3110 && mData->mMachineState != MachineState_Aborted
3111 )
3112 )
3113 )
3114 return setError(VBOX_E_INVALID_VM_STATE,
3115 tr("The machine is not powered off (state is %s)"),
3116 Global::stringifyMachineState(mData->mMachineState));
3117
3118 setModified(IsModified_MachineData);
3119 mUserData.backup();
3120 mUserData->s.fTeleporterEnabled = !!aEnabled;
3121
3122 return S_OK;
3123}
3124
3125STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3126{
3127 CheckComArgOutPointerValid(aPort);
3128
3129 AutoCaller autoCaller(this);
3130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3131
3132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3133
3134 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3135
3136 return S_OK;
3137}
3138
3139STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3140{
3141 if (aPort >= _64K)
3142 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3143
3144 AutoCaller autoCaller(this);
3145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3146
3147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3148
3149 HRESULT rc = checkStateDependency(MutableStateDep);
3150 if (FAILED(rc)) return rc;
3151
3152 setModified(IsModified_MachineData);
3153 mUserData.backup();
3154 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3155
3156 return S_OK;
3157}
3158
3159STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3160{
3161 CheckComArgOutPointerValid(aAddress);
3162
3163 AutoCaller autoCaller(this);
3164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3165
3166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3167
3168 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3169
3170 return S_OK;
3171}
3172
3173STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3174{
3175 AutoCaller autoCaller(this);
3176 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3177
3178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3179
3180 HRESULT rc = checkStateDependency(MutableStateDep);
3181 if (FAILED(rc)) return rc;
3182
3183 setModified(IsModified_MachineData);
3184 mUserData.backup();
3185 mUserData->s.strTeleporterAddress = aAddress;
3186
3187 return S_OK;
3188}
3189
3190STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3191{
3192 CheckComArgOutPointerValid(aPassword);
3193
3194 AutoCaller autoCaller(this);
3195 HRESULT hrc = autoCaller.rc();
3196 if (SUCCEEDED(hrc))
3197 {
3198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3199 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3200 }
3201
3202 return hrc;
3203}
3204
3205STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3206{
3207 /*
3208 * Hash the password first.
3209 */
3210 Utf8Str strPassword(aPassword);
3211 if (!strPassword.isEmpty())
3212 {
3213 if (VBoxIsPasswordHashed(&strPassword))
3214 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3215 VBoxHashPassword(&strPassword);
3216 }
3217
3218 /*
3219 * Do the update.
3220 */
3221 AutoCaller autoCaller(this);
3222 HRESULT hrc = autoCaller.rc();
3223 if (SUCCEEDED(hrc))
3224 {
3225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3226 hrc = checkStateDependency(MutableStateDep);
3227 if (SUCCEEDED(hrc))
3228 {
3229 setModified(IsModified_MachineData);
3230 mUserData.backup();
3231 mUserData->s.strTeleporterPassword = strPassword;
3232 }
3233 }
3234
3235 return hrc;
3236}
3237
3238STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3239{
3240 CheckComArgOutPointerValid(aState);
3241
3242 AutoCaller autoCaller(this);
3243 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3244
3245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3246
3247 *aState = mUserData->s.enmFaultToleranceState;
3248 return S_OK;
3249}
3250
3251STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3252{
3253 AutoCaller autoCaller(this);
3254 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3255
3256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3257
3258 /* @todo deal with running state change. */
3259 HRESULT rc = checkStateDependency(MutableStateDep);
3260 if (FAILED(rc)) return rc;
3261
3262 setModified(IsModified_MachineData);
3263 mUserData.backup();
3264 mUserData->s.enmFaultToleranceState = aState;
3265 return S_OK;
3266}
3267
3268STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3269{
3270 CheckComArgOutPointerValid(aAddress);
3271
3272 AutoCaller autoCaller(this);
3273 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3274
3275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3276
3277 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3278 return S_OK;
3279}
3280
3281STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3282{
3283 AutoCaller autoCaller(this);
3284 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3285
3286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3287
3288 /* @todo deal with running state change. */
3289 HRESULT rc = checkStateDependency(MutableStateDep);
3290 if (FAILED(rc)) return rc;
3291
3292 setModified(IsModified_MachineData);
3293 mUserData.backup();
3294 mUserData->s.strFaultToleranceAddress = aAddress;
3295 return S_OK;
3296}
3297
3298STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3299{
3300 CheckComArgOutPointerValid(aPort);
3301
3302 AutoCaller autoCaller(this);
3303 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3304
3305 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3306
3307 *aPort = mUserData->s.uFaultTolerancePort;
3308 return S_OK;
3309}
3310
3311STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3312{
3313 AutoCaller autoCaller(this);
3314 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3315
3316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3317
3318 /* @todo deal with running state change. */
3319 HRESULT rc = checkStateDependency(MutableStateDep);
3320 if (FAILED(rc)) return rc;
3321
3322 setModified(IsModified_MachineData);
3323 mUserData.backup();
3324 mUserData->s.uFaultTolerancePort = aPort;
3325 return S_OK;
3326}
3327
3328STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3329{
3330 CheckComArgOutPointerValid(aPassword);
3331
3332 AutoCaller autoCaller(this);
3333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3334
3335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3336
3337 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3338
3339 return S_OK;
3340}
3341
3342STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3343{
3344 AutoCaller autoCaller(this);
3345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3346
3347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3348
3349 /* @todo deal with running state change. */
3350 HRESULT rc = checkStateDependency(MutableStateDep);
3351 if (FAILED(rc)) return rc;
3352
3353 setModified(IsModified_MachineData);
3354 mUserData.backup();
3355 mUserData->s.strFaultTolerancePassword = aPassword;
3356
3357 return S_OK;
3358}
3359
3360STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3361{
3362 CheckComArgOutPointerValid(aInterval);
3363
3364 AutoCaller autoCaller(this);
3365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3366
3367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3368
3369 *aInterval = mUserData->s.uFaultToleranceInterval;
3370 return S_OK;
3371}
3372
3373STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3374{
3375 AutoCaller autoCaller(this);
3376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3377
3378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3379
3380 /* @todo deal with running state change. */
3381 HRESULT rc = checkStateDependency(MutableStateDep);
3382 if (FAILED(rc)) return rc;
3383
3384 setModified(IsModified_MachineData);
3385 mUserData.backup();
3386 mUserData->s.uFaultToleranceInterval = aInterval;
3387 return S_OK;
3388}
3389
3390STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3391{
3392 CheckComArgOutPointerValid(aEnabled);
3393
3394 AutoCaller autoCaller(this);
3395 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3396
3397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3398
3399 *aEnabled = mUserData->s.fRTCUseUTC;
3400
3401 return S_OK;
3402}
3403
3404STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3405{
3406 AutoCaller autoCaller(this);
3407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3408
3409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3410
3411 /* Only allow it to be set to true when PoweredOff or Aborted.
3412 (Clearing it is always permitted.) */
3413 if ( aEnabled
3414 && mData->mRegistered
3415 && ( !isSessionMachine()
3416 || ( mData->mMachineState != MachineState_PoweredOff
3417 && mData->mMachineState != MachineState_Teleported
3418 && mData->mMachineState != MachineState_Aborted
3419 )
3420 )
3421 )
3422 return setError(VBOX_E_INVALID_VM_STATE,
3423 tr("The machine is not powered off (state is %s)"),
3424 Global::stringifyMachineState(mData->mMachineState));
3425
3426 setModified(IsModified_MachineData);
3427 mUserData.backup();
3428 mUserData->s.fRTCUseUTC = !!aEnabled;
3429
3430 return S_OK;
3431}
3432
3433STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3434{
3435 CheckComArgOutPointerValid(aEnabled);
3436
3437 AutoCaller autoCaller(this);
3438 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3439
3440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3441
3442 *aEnabled = mHWData->mIOCacheEnabled;
3443
3444 return S_OK;
3445}
3446
3447STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3448{
3449 AutoCaller autoCaller(this);
3450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3451
3452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3453
3454 HRESULT rc = checkStateDependency(MutableStateDep);
3455 if (FAILED(rc)) return rc;
3456
3457 setModified(IsModified_MachineData);
3458 mHWData.backup();
3459 mHWData->mIOCacheEnabled = aEnabled;
3460
3461 return S_OK;
3462}
3463
3464STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3465{
3466 CheckComArgOutPointerValid(aIOCacheSize);
3467
3468 AutoCaller autoCaller(this);
3469 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3470
3471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3472
3473 *aIOCacheSize = mHWData->mIOCacheSize;
3474
3475 return S_OK;
3476}
3477
3478STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3479{
3480 AutoCaller autoCaller(this);
3481 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3482
3483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3484
3485 HRESULT rc = checkStateDependency(MutableStateDep);
3486 if (FAILED(rc)) return rc;
3487
3488 setModified(IsModified_MachineData);
3489 mHWData.backup();
3490 mHWData->mIOCacheSize = aIOCacheSize;
3491
3492 return S_OK;
3493}
3494
3495
3496/**
3497 * @note Locks objects!
3498 */
3499STDMETHODIMP Machine::LockMachine(ISession *aSession,
3500 LockType_T lockType)
3501{
3502 CheckComArgNotNull(aSession);
3503
3504 AutoCaller autoCaller(this);
3505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3506
3507 /* check the session state */
3508 SessionState_T state;
3509 HRESULT rc = aSession->COMGETTER(State)(&state);
3510 if (FAILED(rc)) return rc;
3511
3512 if (state != SessionState_Unlocked)
3513 return setError(VBOX_E_INVALID_OBJECT_STATE,
3514 tr("The given session is busy"));
3515
3516 // get the client's IInternalSessionControl interface
3517 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3518 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3519 E_INVALIDARG);
3520
3521 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3522
3523 if (!mData->mRegistered)
3524 return setError(E_UNEXPECTED,
3525 tr("The machine '%s' is not registered"),
3526 mUserData->s.strName.c_str());
3527
3528 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3529
3530 SessionState_T oldState = mData->mSession.mState;
3531 /* Hack: in case the session is closing and there is a progress object
3532 * which allows waiting for the session to be closed, take the opportunity
3533 * and do a limited wait (max. 1 second). This helps a lot when the system
3534 * is busy and thus session closing can take a little while. */
3535 if ( mData->mSession.mState == SessionState_Unlocking
3536 && mData->mSession.mProgress)
3537 {
3538 alock.release();
3539 mData->mSession.mProgress->WaitForCompletion(1000);
3540 alock.acquire();
3541 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3542 }
3543
3544 // try again now
3545 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3546 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3547 )
3548 {
3549 // OK, share the session... we are now dealing with three processes:
3550 // 1) VBoxSVC (where this code runs);
3551 // 2) process C: the caller's client process (who wants a shared session);
3552 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3553
3554 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3555 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3556 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3557 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3558 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3559
3560 /*
3561 * Release the lock before calling the client process. It's safe here
3562 * since the only thing to do after we get the lock again is to add
3563 * the remote control to the list (which doesn't directly influence
3564 * anything).
3565 */
3566 alock.release();
3567
3568 // get the console of the session holding the write lock (this is a remote call)
3569 ComPtr<IConsole> pConsoleW;
3570 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3571 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3572 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3573 if (FAILED(rc))
3574 // the failure may occur w/o any error info (from RPC), so provide one
3575 return setError(VBOX_E_VM_ERROR,
3576 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3577
3578 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3579
3580 // share the session machine and W's console with the caller's session
3581 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3582 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3583 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3584
3585 if (FAILED(rc))
3586 // the failure may occur w/o any error info (from RPC), so provide one
3587 return setError(VBOX_E_VM_ERROR,
3588 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3589 alock.acquire();
3590
3591 // need to revalidate the state after acquiring the lock again
3592 if (mData->mSession.mState != SessionState_Locked)
3593 {
3594 pSessionControl->Uninitialize();
3595 return setError(VBOX_E_INVALID_SESSION_STATE,
3596 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3597 mUserData->s.strName.c_str());
3598 }
3599
3600 // add the caller's session to the list
3601 mData->mSession.mRemoteControls.push_back(pSessionControl);
3602 }
3603 else if ( mData->mSession.mState == SessionState_Locked
3604 || mData->mSession.mState == SessionState_Unlocking
3605 )
3606 {
3607 // sharing not permitted, or machine still unlocking:
3608 return setError(VBOX_E_INVALID_OBJECT_STATE,
3609 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3610 mUserData->s.strName.c_str());
3611 }
3612 else
3613 {
3614 // machine is not locked: then write-lock the machine (create the session machine)
3615
3616 // must not be busy
3617 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3618
3619 // get the caller's session PID
3620 RTPROCESS pid = NIL_RTPROCESS;
3621 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3622 pSessionControl->GetPID((ULONG*)&pid);
3623 Assert(pid != NIL_RTPROCESS);
3624
3625 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3626
3627 if (fLaunchingVMProcess)
3628 {
3629 if (mData->mSession.mPID == NIL_RTPROCESS)
3630 {
3631 // two or more clients racing for a lock, the one which set the
3632 // session state to Spawning will win, the others will get an
3633 // error as we can't decide here if waiting a little would help
3634 // (only for shared locks this would avoid an error)
3635 return setError(VBOX_E_INVALID_OBJECT_STATE,
3636 tr("The machine '%s' already has a lock request pending"),
3637 mUserData->s.strName.c_str());
3638 }
3639
3640 // this machine is awaiting for a spawning session to be opened:
3641 // then the calling process must be the one that got started by
3642 // LaunchVMProcess()
3643
3644 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3645 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3646
3647 if (mData->mSession.mPID != pid)
3648 return setError(E_ACCESSDENIED,
3649 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3650 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3651 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3652 }
3653
3654 // create the mutable SessionMachine from the current machine
3655 ComObjPtr<SessionMachine> sessionMachine;
3656 sessionMachine.createObject();
3657 rc = sessionMachine->init(this);
3658 AssertComRC(rc);
3659
3660 /* NOTE: doing return from this function after this point but
3661 * before the end is forbidden since it may call SessionMachine::uninit()
3662 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3663 * lock while still holding the Machine lock in alock so that a deadlock
3664 * is possible due to the wrong lock order. */
3665
3666 if (SUCCEEDED(rc))
3667 {
3668 /*
3669 * Set the session state to Spawning to protect against subsequent
3670 * attempts to open a session and to unregister the machine after
3671 * we release the lock.
3672 */
3673 SessionState_T origState = mData->mSession.mState;
3674 mData->mSession.mState = SessionState_Spawning;
3675
3676#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3677 /* Get the client token ID to be passed to the client process */
3678 Utf8Str strTokenId;
3679 sessionMachine->getTokenId(strTokenId);
3680 Assert(!strTokenId.isEmpty());
3681#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3682 /* Get the client token to be passed to the client process */
3683 ComPtr<IToken> pToken(sessionMachine->getToken());
3684 /* The token is now "owned" by pToken, fix refcount */
3685 if (!pToken.isNull())
3686 pToken->Release();
3687#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3688
3689 /*
3690 * Release the lock before calling the client process -- it will call
3691 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3692 * because the state is Spawning, so that LaunchVMProcess() and
3693 * LockMachine() calls will fail. This method, called before we
3694 * acquire the lock again, will fail because of the wrong PID.
3695 *
3696 * Note that mData->mSession.mRemoteControls accessed outside
3697 * the lock may not be modified when state is Spawning, so it's safe.
3698 */
3699 alock.release();
3700
3701 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3702#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3703 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3704#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3705 rc = pSessionControl->AssignMachine(sessionMachine, lockType, pToken);
3706 /* Now the token is owned by the client process. */
3707 pToken.setNull();
3708#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3709 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3710
3711 /* The failure may occur w/o any error info (from RPC), so provide one */
3712 if (FAILED(rc))
3713 setError(VBOX_E_VM_ERROR,
3714 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3715
3716 if ( SUCCEEDED(rc)
3717 && fLaunchingVMProcess
3718 )
3719 {
3720 /* complete the remote session initialization */
3721
3722 /* get the console from the direct session */
3723 ComPtr<IConsole> console;
3724 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3725 ComAssertComRC(rc);
3726
3727 if (SUCCEEDED(rc) && !console)
3728 {
3729 ComAssert(!!console);
3730 rc = E_FAIL;
3731 }
3732
3733 /* assign machine & console to the remote session */
3734 if (SUCCEEDED(rc))
3735 {
3736 /*
3737 * after LaunchVMProcess(), the first and the only
3738 * entry in remoteControls is that remote session
3739 */
3740 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3741 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3742 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3743
3744 /* The failure may occur w/o any error info (from RPC), so provide one */
3745 if (FAILED(rc))
3746 setError(VBOX_E_VM_ERROR,
3747 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3748 }
3749
3750 if (FAILED(rc))
3751 pSessionControl->Uninitialize();
3752 }
3753
3754 /* acquire the lock again */
3755 alock.acquire();
3756
3757 /* Restore the session state */
3758 mData->mSession.mState = origState;
3759 }
3760
3761 // finalize spawning anyway (this is why we don't return on errors above)
3762 if (fLaunchingVMProcess)
3763 {
3764 /* Note that the progress object is finalized later */
3765 /** @todo Consider checking mData->mSession.mProgress for cancellation
3766 * around here. */
3767
3768 /* We don't reset mSession.mPID here because it is necessary for
3769 * SessionMachine::uninit() to reap the child process later. */
3770
3771 if (FAILED(rc))
3772 {
3773 /* Close the remote session, remove the remote control from the list
3774 * and reset session state to Closed (@note keep the code in sync
3775 * with the relevant part in checkForSpawnFailure()). */
3776
3777 Assert(mData->mSession.mRemoteControls.size() == 1);
3778 if (mData->mSession.mRemoteControls.size() == 1)
3779 {
3780 ErrorInfoKeeper eik;
3781 mData->mSession.mRemoteControls.front()->Uninitialize();
3782 }
3783
3784 mData->mSession.mRemoteControls.clear();
3785 mData->mSession.mState = SessionState_Unlocked;
3786 }
3787 }
3788 else
3789 {
3790 /* memorize PID of the directly opened session */
3791 if (SUCCEEDED(rc))
3792 mData->mSession.mPID = pid;
3793 }
3794
3795 if (SUCCEEDED(rc))
3796 {
3797 /* memorize the direct session control and cache IUnknown for it */
3798 mData->mSession.mDirectControl = pSessionControl;
3799 mData->mSession.mState = SessionState_Locked;
3800 /* associate the SessionMachine with this Machine */
3801 mData->mSession.mMachine = sessionMachine;
3802
3803 /* request an IUnknown pointer early from the remote party for later
3804 * identity checks (it will be internally cached within mDirectControl
3805 * at least on XPCOM) */
3806 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3807 NOREF(unk);
3808 }
3809
3810 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3811 * would break the lock order */
3812 alock.release();
3813
3814 /* uninitialize the created session machine on failure */
3815 if (FAILED(rc))
3816 sessionMachine->uninit();
3817
3818 }
3819
3820 if (SUCCEEDED(rc))
3821 {
3822 /*
3823 * tell the client watcher thread to update the set of
3824 * machines that have open sessions
3825 */
3826 mParent->updateClientWatcher();
3827
3828 if (oldState != SessionState_Locked)
3829 /* fire an event */
3830 mParent->onSessionStateChange(getId(), SessionState_Locked);
3831 }
3832
3833 return rc;
3834}
3835
3836/**
3837 * @note Locks objects!
3838 */
3839STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3840 IN_BSTR aFrontend,
3841 IN_BSTR aEnvironment,
3842 IProgress **aProgress)
3843{
3844 CheckComArgStr(aFrontend);
3845 Utf8Str strFrontend(aFrontend);
3846 Utf8Str strEnvironment(aEnvironment);
3847 /* "emergencystop" doesn't need the session, so skip the checks/interface
3848 * retrieval. This code doesn't quite fit in here, but introducing a
3849 * special API method would be even more effort, and would require explicit
3850 * support by every API client. It's better to hide the feature a bit. */
3851 if (strFrontend != "emergencystop")
3852 CheckComArgNotNull(aSession);
3853 CheckComArgOutPointerValid(aProgress);
3854
3855 AutoCaller autoCaller(this);
3856 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3857
3858 HRESULT rc = S_OK;
3859 if (strFrontend.isEmpty())
3860 {
3861 Bstr bstrFrontend;
3862 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3863 if (FAILED(rc))
3864 return rc;
3865 strFrontend = bstrFrontend;
3866 if (strFrontend.isEmpty())
3867 {
3868 ComPtr<ISystemProperties> systemProperties;
3869 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3870 if (FAILED(rc))
3871 return rc;
3872 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3873 if (FAILED(rc))
3874 return rc;
3875 strFrontend = bstrFrontend;
3876 }
3877 /* paranoia - emergencystop is not a valid default */
3878 if (strFrontend == "emergencystop")
3879 strFrontend = Utf8Str::Empty;
3880 }
3881 /* default frontend: Qt GUI */
3882 if (strFrontend.isEmpty())
3883 strFrontend = "GUI/Qt";
3884
3885 if (strFrontend != "emergencystop")
3886 {
3887 /* check the session state */
3888 SessionState_T state;
3889 rc = aSession->COMGETTER(State)(&state);
3890 if (FAILED(rc))
3891 return rc;
3892
3893 if (state != SessionState_Unlocked)
3894 return setError(VBOX_E_INVALID_OBJECT_STATE,
3895 tr("The given session is busy"));
3896
3897 /* get the IInternalSessionControl interface */
3898 ComPtr<IInternalSessionControl> control(aSession);
3899 ComAssertMsgRet(!control.isNull(),
3900 ("No IInternalSessionControl interface"),
3901 E_INVALIDARG);
3902
3903 /* get the teleporter enable state for the progress object init. */
3904 BOOL fTeleporterEnabled;
3905 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3906 if (FAILED(rc))
3907 return rc;
3908
3909 /* create a progress object */
3910 ComObjPtr<ProgressProxy> progress;
3911 progress.createObject();
3912 rc = progress->init(mParent,
3913 static_cast<IMachine*>(this),
3914 Bstr(tr("Starting VM")).raw(),
3915 TRUE /* aCancelable */,
3916 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3917 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3918 2 /* uFirstOperationWeight */,
3919 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3920
3921 if (SUCCEEDED(rc))
3922 {
3923 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3924 if (SUCCEEDED(rc))
3925 {
3926 progress.queryInterfaceTo(aProgress);
3927
3928 /* signal the client watcher thread */
3929 mParent->updateClientWatcher();
3930
3931 /* fire an event */
3932 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3933 }
3934 }
3935 }
3936 else
3937 {
3938 /* no progress object - either instant success or failure */
3939 *aProgress = NULL;
3940
3941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3942
3943 if (mData->mSession.mState != SessionState_Locked)
3944 return setError(VBOX_E_INVALID_OBJECT_STATE,
3945 tr("The machine '%s' is not locked by a session"),
3946 mUserData->s.strName.c_str());
3947
3948 /* must have a VM process associated - do not kill normal API clients
3949 * with an open session */
3950 if (!Global::IsOnline(mData->mMachineState))
3951 return setError(VBOX_E_INVALID_OBJECT_STATE,
3952 tr("The machine '%s' does not have a VM process"),
3953 mUserData->s.strName.c_str());
3954
3955 /* forcibly terminate the VM process */
3956 if (mData->mSession.mPID != NIL_RTPROCESS)
3957 RTProcTerminate(mData->mSession.mPID);
3958
3959 /* signal the client watcher thread, as most likely the client has
3960 * been terminated */
3961 mParent->updateClientWatcher();
3962 }
3963
3964 return rc;
3965}
3966
3967STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3968{
3969 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3970 return setError(E_INVALIDARG,
3971 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3972 aPosition, SchemaDefs::MaxBootPosition);
3973
3974 if (aDevice == DeviceType_USB)
3975 return setError(E_NOTIMPL,
3976 tr("Booting from USB device is currently not supported"));
3977
3978 AutoCaller autoCaller(this);
3979 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3980
3981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3982
3983 HRESULT rc = checkStateDependency(MutableStateDep);
3984 if (FAILED(rc)) return rc;
3985
3986 setModified(IsModified_MachineData);
3987 mHWData.backup();
3988 mHWData->mBootOrder[aPosition - 1] = aDevice;
3989
3990 return S_OK;
3991}
3992
3993STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3994{
3995 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3996 return setError(E_INVALIDARG,
3997 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3998 aPosition, SchemaDefs::MaxBootPosition);
3999
4000 AutoCaller autoCaller(this);
4001 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4002
4003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4004
4005 *aDevice = mHWData->mBootOrder[aPosition - 1];
4006
4007 return S_OK;
4008}
4009
4010STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4011 LONG aControllerPort,
4012 LONG aDevice,
4013 DeviceType_T aType,
4014 IMedium *aMedium)
4015{
4016 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4017 aControllerName, aControllerPort, aDevice, aType, aMedium));
4018
4019 CheckComArgStrNotEmptyOrNull(aControllerName);
4020
4021 AutoCaller autoCaller(this);
4022 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4023
4024 // request the host lock first, since might be calling Host methods for getting host drives;
4025 // next, protect the media tree all the while we're in here, as well as our member variables
4026 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4027 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4028
4029 HRESULT rc = checkStateDependency(MutableStateDep);
4030 if (FAILED(rc)) return rc;
4031
4032 /// @todo NEWMEDIA implicit machine registration
4033 if (!mData->mRegistered)
4034 return setError(VBOX_E_INVALID_OBJECT_STATE,
4035 tr("Cannot attach storage devices to an unregistered machine"));
4036
4037 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4038
4039 /* Check for an existing controller. */
4040 ComObjPtr<StorageController> ctl;
4041 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4042 if (FAILED(rc)) return rc;
4043
4044 StorageControllerType_T ctrlType;
4045 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4046 if (FAILED(rc))
4047 return setError(E_FAIL,
4048 tr("Could not get type of controller '%ls'"),
4049 aControllerName);
4050
4051 bool fSilent = false;
4052 Utf8Str strReconfig;
4053
4054 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4055 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4056 if ( mData->mMachineState == MachineState_Paused
4057 && strReconfig == "1")
4058 fSilent = true;
4059
4060 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4061 bool fHotplug = false;
4062 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4063 fHotplug = true;
4064
4065 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4066 return setError(VBOX_E_INVALID_VM_STATE,
4067 tr("Controller '%ls' does not support hotplugging"),
4068 aControllerName);
4069
4070 // check that the port and device are not out of range
4071 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4072 if (FAILED(rc)) return rc;
4073
4074 /* check if the device slot is already busy */
4075 MediumAttachment *pAttachTemp;
4076 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4077 aControllerName,
4078 aControllerPort,
4079 aDevice)))
4080 {
4081 Medium *pMedium = pAttachTemp->getMedium();
4082 if (pMedium)
4083 {
4084 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4085 return setError(VBOX_E_OBJECT_IN_USE,
4086 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4087 pMedium->getLocationFull().c_str(),
4088 aControllerPort,
4089 aDevice,
4090 aControllerName);
4091 }
4092 else
4093 return setError(VBOX_E_OBJECT_IN_USE,
4094 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4095 aControllerPort, aDevice, aControllerName);
4096 }
4097
4098 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4099 if (aMedium && medium.isNull())
4100 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4101
4102 AutoCaller mediumCaller(medium);
4103 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4104
4105 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4106
4107 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4108 && !medium.isNull()
4109 )
4110 return setError(VBOX_E_OBJECT_IN_USE,
4111 tr("Medium '%s' is already attached to this virtual machine"),
4112 medium->getLocationFull().c_str());
4113
4114 if (!medium.isNull())
4115 {
4116 MediumType_T mtype = medium->getType();
4117 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4118 // For DVDs it's not written to the config file, so needs no global config
4119 // version bump. For floppies it's a new attribute "type", which is ignored
4120 // by older VirtualBox version, so needs no global config version bump either.
4121 // For hard disks this type is not accepted.
4122 if (mtype == MediumType_MultiAttach)
4123 {
4124 // This type is new with VirtualBox 4.0 and therefore requires settings
4125 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4126 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4127 // two reasons: The medium type is a property of the media registry tree, which
4128 // can reside in the global config file (for pre-4.0 media); we would therefore
4129 // possibly need to bump the global config version. We don't want to do that though
4130 // because that might make downgrading to pre-4.0 impossible.
4131 // As a result, we can only use these two new types if the medium is NOT in the
4132 // global registry:
4133 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4134 if ( medium->isInRegistry(uuidGlobalRegistry)
4135 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4136 )
4137 return setError(VBOX_E_INVALID_OBJECT_STATE,
4138 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4139 "to machines that were created with VirtualBox 4.0 or later"),
4140 medium->getLocationFull().c_str());
4141 }
4142 }
4143
4144 bool fIndirect = false;
4145 if (!medium.isNull())
4146 fIndirect = medium->isReadOnly();
4147 bool associate = true;
4148
4149 do
4150 {
4151 if ( aType == DeviceType_HardDisk
4152 && mMediaData.isBackedUp())
4153 {
4154 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4155
4156 /* check if the medium was attached to the VM before we started
4157 * changing attachments in which case the attachment just needs to
4158 * be restored */
4159 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4160 {
4161 AssertReturn(!fIndirect, E_FAIL);
4162
4163 /* see if it's the same bus/channel/device */
4164 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4165 {
4166 /* the simplest case: restore the whole attachment
4167 * and return, nothing else to do */
4168 mMediaData->mAttachments.push_back(pAttachTemp);
4169
4170 /* Reattach the medium to the VM. */
4171 if (fHotplug || fSilent)
4172 {
4173 mediumLock.release();
4174 treeLock.release();
4175 alock.release();
4176
4177 MediumLockList *pMediumLockList(new MediumLockList());
4178
4179 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4180 true /* fMediumLockWrite */,
4181 NULL,
4182 *pMediumLockList);
4183 alock.acquire();
4184 if (FAILED(rc))
4185 delete pMediumLockList;
4186 else
4187 {
4188 mData->mSession.mLockedMedia.Unlock();
4189 alock.release();
4190 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4191 mData->mSession.mLockedMedia.Lock();
4192 alock.acquire();
4193 }
4194 alock.release();
4195
4196 if (SUCCEEDED(rc))
4197 {
4198 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4199 /* Remove lock list in case of error. */
4200 if (FAILED(rc))
4201 {
4202 mData->mSession.mLockedMedia.Unlock();
4203 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4204 mData->mSession.mLockedMedia.Lock();
4205 }
4206 }
4207 }
4208
4209 return S_OK;
4210 }
4211
4212 /* bus/channel/device differ; we need a new attachment object,
4213 * but don't try to associate it again */
4214 associate = false;
4215 break;
4216 }
4217 }
4218
4219 /* go further only if the attachment is to be indirect */
4220 if (!fIndirect)
4221 break;
4222
4223 /* perform the so called smart attachment logic for indirect
4224 * attachments. Note that smart attachment is only applicable to base
4225 * hard disks. */
4226
4227 if (medium->getParent().isNull())
4228 {
4229 /* first, investigate the backup copy of the current hard disk
4230 * attachments to make it possible to re-attach existing diffs to
4231 * another device slot w/o losing their contents */
4232 if (mMediaData.isBackedUp())
4233 {
4234 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4235
4236 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4237 uint32_t foundLevel = 0;
4238
4239 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4240 it != oldAtts.end();
4241 ++it)
4242 {
4243 uint32_t level = 0;
4244 MediumAttachment *pAttach = *it;
4245 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4246 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4247 if (pMedium.isNull())
4248 continue;
4249
4250 if (pMedium->getBase(&level) == medium)
4251 {
4252 /* skip the hard disk if its currently attached (we
4253 * cannot attach the same hard disk twice) */
4254 if (findAttachment(mMediaData->mAttachments,
4255 pMedium))
4256 continue;
4257
4258 /* matched device, channel and bus (i.e. attached to the
4259 * same place) will win and immediately stop the search;
4260 * otherwise the attachment that has the youngest
4261 * descendant of medium will be used
4262 */
4263 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4264 {
4265 /* the simplest case: restore the whole attachment
4266 * and return, nothing else to do */
4267 mMediaData->mAttachments.push_back(*it);
4268
4269 /* Reattach the medium to the VM. */
4270 if (fHotplug || fSilent)
4271 {
4272 mediumLock.release();
4273 treeLock.release();
4274 alock.release();
4275
4276 MediumLockList *pMediumLockList(new MediumLockList());
4277
4278 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4279 true /* fMediumLockWrite */,
4280 NULL,
4281 *pMediumLockList);
4282 alock.acquire();
4283 if (FAILED(rc))
4284 delete pMediumLockList;
4285 else
4286 {
4287 mData->mSession.mLockedMedia.Unlock();
4288 alock.release();
4289 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4290 mData->mSession.mLockedMedia.Lock();
4291 alock.acquire();
4292 }
4293 alock.release();
4294
4295 if (SUCCEEDED(rc))
4296 {
4297 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4298 /* Remove lock list in case of error. */
4299 if (FAILED(rc))
4300 {
4301 mData->mSession.mLockedMedia.Unlock();
4302 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4303 mData->mSession.mLockedMedia.Lock();
4304 }
4305 }
4306 }
4307
4308 return S_OK;
4309 }
4310 else if ( foundIt == oldAtts.end()
4311 || level > foundLevel /* prefer younger */
4312 )
4313 {
4314 foundIt = it;
4315 foundLevel = level;
4316 }
4317 }
4318 }
4319
4320 if (foundIt != oldAtts.end())
4321 {
4322 /* use the previously attached hard disk */
4323 medium = (*foundIt)->getMedium();
4324 mediumCaller.attach(medium);
4325 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4326 mediumLock.attach(medium);
4327 /* not implicit, doesn't require association with this VM */
4328 fIndirect = false;
4329 associate = false;
4330 /* go right to the MediumAttachment creation */
4331 break;
4332 }
4333 }
4334
4335 /* must give up the medium lock and medium tree lock as below we
4336 * go over snapshots, which needs a lock with higher lock order. */
4337 mediumLock.release();
4338 treeLock.release();
4339
4340 /* then, search through snapshots for the best diff in the given
4341 * hard disk's chain to base the new diff on */
4342
4343 ComObjPtr<Medium> base;
4344 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4345 while (snap)
4346 {
4347 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4348
4349 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4350
4351 MediumAttachment *pAttachFound = NULL;
4352 uint32_t foundLevel = 0;
4353
4354 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4355 it != snapAtts.end();
4356 ++it)
4357 {
4358 MediumAttachment *pAttach = *it;
4359 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4360 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4361 if (pMedium.isNull())
4362 continue;
4363
4364 uint32_t level = 0;
4365 if (pMedium->getBase(&level) == medium)
4366 {
4367 /* matched device, channel and bus (i.e. attached to the
4368 * same place) will win and immediately stop the search;
4369 * otherwise the attachment that has the youngest
4370 * descendant of medium will be used
4371 */
4372 if ( pAttach->getDevice() == aDevice
4373 && pAttach->getPort() == aControllerPort
4374 && pAttach->getControllerName() == aControllerName
4375 )
4376 {
4377 pAttachFound = pAttach;
4378 break;
4379 }
4380 else if ( !pAttachFound
4381 || level > foundLevel /* prefer younger */
4382 )
4383 {
4384 pAttachFound = pAttach;
4385 foundLevel = level;
4386 }
4387 }
4388 }
4389
4390 if (pAttachFound)
4391 {
4392 base = pAttachFound->getMedium();
4393 break;
4394 }
4395
4396 snap = snap->getParent();
4397 }
4398
4399 /* re-lock medium tree and the medium, as we need it below */
4400 treeLock.acquire();
4401 mediumLock.acquire();
4402
4403 /* found a suitable diff, use it as a base */
4404 if (!base.isNull())
4405 {
4406 medium = base;
4407 mediumCaller.attach(medium);
4408 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4409 mediumLock.attach(medium);
4410 }
4411 }
4412
4413 Utf8Str strFullSnapshotFolder;
4414 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4415
4416 ComObjPtr<Medium> diff;
4417 diff.createObject();
4418 // store this diff in the same registry as the parent
4419 Guid uuidRegistryParent;
4420 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4421 {
4422 // parent image has no registry: this can happen if we're attaching a new immutable
4423 // image that has not yet been attached (medium then points to the base and we're
4424 // creating the diff image for the immutable, and the parent is not yet registered);
4425 // put the parent in the machine registry then
4426 mediumLock.release();
4427 treeLock.release();
4428 alock.release();
4429 addMediumToRegistry(medium);
4430 alock.acquire();
4431 treeLock.acquire();
4432 mediumLock.acquire();
4433 medium->getFirstRegistryMachineId(uuidRegistryParent);
4434 }
4435 rc = diff->init(mParent,
4436 medium->getPreferredDiffFormat(),
4437 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4438 uuidRegistryParent);
4439 if (FAILED(rc)) return rc;
4440
4441 /* Apply the normal locking logic to the entire chain. */
4442 MediumLockList *pMediumLockList(new MediumLockList());
4443 mediumLock.release();
4444 treeLock.release();
4445 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4446 true /* fMediumLockWrite */,
4447 medium,
4448 *pMediumLockList);
4449 treeLock.acquire();
4450 mediumLock.acquire();
4451 if (SUCCEEDED(rc))
4452 {
4453 mediumLock.release();
4454 treeLock.release();
4455 rc = pMediumLockList->Lock();
4456 treeLock.acquire();
4457 mediumLock.acquire();
4458 if (FAILED(rc))
4459 setError(rc,
4460 tr("Could not lock medium when creating diff '%s'"),
4461 diff->getLocationFull().c_str());
4462 else
4463 {
4464 /* will release the lock before the potentially lengthy
4465 * operation, so protect with the special state */
4466 MachineState_T oldState = mData->mMachineState;
4467 setMachineState(MachineState_SettingUp);
4468
4469 mediumLock.release();
4470 treeLock.release();
4471 alock.release();
4472
4473 rc = medium->createDiffStorage(diff,
4474 MediumVariant_Standard,
4475 pMediumLockList,
4476 NULL /* aProgress */,
4477 true /* aWait */);
4478
4479 alock.acquire();
4480 treeLock.acquire();
4481 mediumLock.acquire();
4482
4483 setMachineState(oldState);
4484 }
4485 }
4486
4487 /* Unlock the media and free the associated memory. */
4488 delete pMediumLockList;
4489
4490 if (FAILED(rc)) return rc;
4491
4492 /* use the created diff for the actual attachment */
4493 medium = diff;
4494 mediumCaller.attach(medium);
4495 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4496 mediumLock.attach(medium);
4497 }
4498 while (0);
4499
4500 ComObjPtr<MediumAttachment> attachment;
4501 attachment.createObject();
4502 rc = attachment->init(this,
4503 medium,
4504 aControllerName,
4505 aControllerPort,
4506 aDevice,
4507 aType,
4508 fIndirect,
4509 false /* fPassthrough */,
4510 false /* fTempEject */,
4511 false /* fNonRotational */,
4512 false /* fDiscard */,
4513 Utf8Str::Empty);
4514 if (FAILED(rc)) return rc;
4515
4516 if (associate && !medium.isNull())
4517 {
4518 // as the last step, associate the medium to the VM
4519 rc = medium->addBackReference(mData->mUuid);
4520 // here we can fail because of Deleting, or being in process of creating a Diff
4521 if (FAILED(rc)) return rc;
4522
4523 mediumLock.release();
4524 treeLock.release();
4525 alock.release();
4526 addMediumToRegistry(medium);
4527 alock.acquire();
4528 treeLock.acquire();
4529 mediumLock.acquire();
4530 }
4531
4532 /* success: finally remember the attachment */
4533 setModified(IsModified_Storage);
4534 mMediaData.backup();
4535 mMediaData->mAttachments.push_back(attachment);
4536
4537 mediumLock.release();
4538 treeLock.release();
4539 alock.release();
4540
4541 if (fHotplug || fSilent)
4542 {
4543 if (!medium.isNull())
4544 {
4545 MediumLockList *pMediumLockList(new MediumLockList());
4546
4547 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4548 true /* fMediumLockWrite */,
4549 NULL,
4550 *pMediumLockList);
4551 alock.acquire();
4552 if (FAILED(rc))
4553 delete pMediumLockList;
4554 else
4555 {
4556 mData->mSession.mLockedMedia.Unlock();
4557 alock.release();
4558 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4559 mData->mSession.mLockedMedia.Lock();
4560 alock.acquire();
4561 }
4562 alock.release();
4563 }
4564
4565 if (SUCCEEDED(rc))
4566 {
4567 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4568 /* Remove lock list in case of error. */
4569 if (FAILED(rc))
4570 {
4571 mData->mSession.mLockedMedia.Unlock();
4572 mData->mSession.mLockedMedia.Remove(attachment);
4573 mData->mSession.mLockedMedia.Lock();
4574 }
4575 }
4576 }
4577
4578 mParent->saveModifiedRegistries();
4579
4580 return rc;
4581}
4582
4583STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4584 LONG aDevice)
4585{
4586 CheckComArgStrNotEmptyOrNull(aControllerName);
4587
4588 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4589 aControllerName, aControllerPort, aDevice));
4590
4591 AutoCaller autoCaller(this);
4592 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4593
4594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4595
4596 HRESULT rc = checkStateDependency(MutableStateDep);
4597 if (FAILED(rc)) return rc;
4598
4599 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4600
4601 /* Check for an existing controller. */
4602 ComObjPtr<StorageController> ctl;
4603 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4604 if (FAILED(rc)) return rc;
4605
4606 StorageControllerType_T ctrlType;
4607 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4608 if (FAILED(rc))
4609 return setError(E_FAIL,
4610 tr("Could not get type of controller '%ls'"),
4611 aControllerName);
4612
4613 bool fSilent = false;
4614 Utf8Str strReconfig;
4615
4616 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4617 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4618 if ( mData->mMachineState == MachineState_Paused
4619 && strReconfig == "1")
4620 fSilent = true;
4621
4622 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4623 bool fHotplug = false;
4624 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4625 fHotplug = true;
4626
4627 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4628 return setError(VBOX_E_INVALID_VM_STATE,
4629 tr("Controller '%ls' does not support hotplugging"),
4630 aControllerName);
4631
4632 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4633 aControllerName,
4634 aControllerPort,
4635 aDevice);
4636 if (!pAttach)
4637 return setError(VBOX_E_OBJECT_NOT_FOUND,
4638 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4639 aDevice, aControllerPort, aControllerName);
4640
4641 /*
4642 * The VM has to detach the device before we delete any implicit diffs.
4643 * If this fails we can roll back without loosing data.
4644 */
4645 if (fHotplug || fSilent)
4646 {
4647 alock.release();
4648 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4649 alock.acquire();
4650 }
4651 if (FAILED(rc)) return rc;
4652
4653 /* If we are here everything went well and we can delete the implicit now. */
4654 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4655
4656 alock.release();
4657
4658 mParent->saveModifiedRegistries();
4659
4660 return rc;
4661}
4662
4663STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4664 LONG aDevice, BOOL aPassthrough)
4665{
4666 CheckComArgStrNotEmptyOrNull(aControllerName);
4667
4668 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4669 aControllerName, aControllerPort, aDevice, aPassthrough));
4670
4671 AutoCaller autoCaller(this);
4672 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4673
4674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4675
4676 HRESULT rc = checkStateDependency(MutableStateDep);
4677 if (FAILED(rc)) return rc;
4678
4679 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4680
4681 if (Global::IsOnlineOrTransient(mData->mMachineState))
4682 return setError(VBOX_E_INVALID_VM_STATE,
4683 tr("Invalid machine state: %s"),
4684 Global::stringifyMachineState(mData->mMachineState));
4685
4686 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4687 aControllerName,
4688 aControllerPort,
4689 aDevice);
4690 if (!pAttach)
4691 return setError(VBOX_E_OBJECT_NOT_FOUND,
4692 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4693 aDevice, aControllerPort, aControllerName);
4694
4695
4696 setModified(IsModified_Storage);
4697 mMediaData.backup();
4698
4699 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4700
4701 if (pAttach->getType() != DeviceType_DVD)
4702 return setError(E_INVALIDARG,
4703 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4704 aDevice, aControllerPort, aControllerName);
4705 pAttach->updatePassthrough(!!aPassthrough);
4706
4707 return S_OK;
4708}
4709
4710STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4711 LONG aDevice, BOOL aTemporaryEject)
4712{
4713 CheckComArgStrNotEmptyOrNull(aControllerName);
4714
4715 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4716 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4717
4718 AutoCaller autoCaller(this);
4719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4720
4721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4722
4723 HRESULT rc = checkStateDependency(MutableStateDep);
4724 if (FAILED(rc)) return rc;
4725
4726 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4727 aControllerName,
4728 aControllerPort,
4729 aDevice);
4730 if (!pAttach)
4731 return setError(VBOX_E_OBJECT_NOT_FOUND,
4732 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4733 aDevice, aControllerPort, aControllerName);
4734
4735
4736 setModified(IsModified_Storage);
4737 mMediaData.backup();
4738
4739 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4740
4741 if (pAttach->getType() != DeviceType_DVD)
4742 return setError(E_INVALIDARG,
4743 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4744 aDevice, aControllerPort, aControllerName);
4745 pAttach->updateTempEject(!!aTemporaryEject);
4746
4747 return S_OK;
4748}
4749
4750STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4751 LONG aDevice, BOOL aNonRotational)
4752{
4753 CheckComArgStrNotEmptyOrNull(aControllerName);
4754
4755 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4756 aControllerName, aControllerPort, aDevice, aNonRotational));
4757
4758 AutoCaller autoCaller(this);
4759 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4760
4761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4762
4763 HRESULT rc = checkStateDependency(MutableStateDep);
4764 if (FAILED(rc)) return rc;
4765
4766 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4767
4768 if (Global::IsOnlineOrTransient(mData->mMachineState))
4769 return setError(VBOX_E_INVALID_VM_STATE,
4770 tr("Invalid machine state: %s"),
4771 Global::stringifyMachineState(mData->mMachineState));
4772
4773 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4774 aControllerName,
4775 aControllerPort,
4776 aDevice);
4777 if (!pAttach)
4778 return setError(VBOX_E_OBJECT_NOT_FOUND,
4779 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4780 aDevice, aControllerPort, aControllerName);
4781
4782
4783 setModified(IsModified_Storage);
4784 mMediaData.backup();
4785
4786 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4787
4788 if (pAttach->getType() != DeviceType_HardDisk)
4789 return setError(E_INVALIDARG,
4790 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"),
4791 aDevice, aControllerPort, aControllerName);
4792 pAttach->updateNonRotational(!!aNonRotational);
4793
4794 return S_OK;
4795}
4796
4797STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4798 LONG aDevice, BOOL aDiscard)
4799{
4800 CheckComArgStrNotEmptyOrNull(aControllerName);
4801
4802 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4803 aControllerName, aControllerPort, aDevice, aDiscard));
4804
4805 AutoCaller autoCaller(this);
4806 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4807
4808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4809
4810 HRESULT rc = checkStateDependency(MutableStateDep);
4811 if (FAILED(rc)) return rc;
4812
4813 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4814
4815 if (Global::IsOnlineOrTransient(mData->mMachineState))
4816 return setError(VBOX_E_INVALID_VM_STATE,
4817 tr("Invalid machine state: %s"),
4818 Global::stringifyMachineState(mData->mMachineState));
4819
4820 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4821 aControllerName,
4822 aControllerPort,
4823 aDevice);
4824 if (!pAttach)
4825 return setError(VBOX_E_OBJECT_NOT_FOUND,
4826 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4827 aDevice, aControllerPort, aControllerName);
4828
4829
4830 setModified(IsModified_Storage);
4831 mMediaData.backup();
4832
4833 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4834
4835 if (pAttach->getType() != DeviceType_HardDisk)
4836 return setError(E_INVALIDARG,
4837 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"),
4838 aDevice, aControllerPort, aControllerName);
4839 pAttach->updateDiscard(!!aDiscard);
4840
4841 return S_OK;
4842}
4843
4844STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4845 LONG aDevice)
4846{
4847 int rc = S_OK;
4848 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4849 aControllerName, aControllerPort, aDevice));
4850
4851 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4852
4853 return rc;
4854}
4855
4856STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4857 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4858{
4859 CheckComArgStrNotEmptyOrNull(aControllerName);
4860
4861 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4862 aControllerName, aControllerPort, aDevice));
4863
4864 AutoCaller autoCaller(this);
4865 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4866
4867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4868
4869 HRESULT rc = checkStateDependency(MutableStateDep);
4870 if (FAILED(rc)) return rc;
4871
4872 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4873
4874 if (Global::IsOnlineOrTransient(mData->mMachineState))
4875 return setError(VBOX_E_INVALID_VM_STATE,
4876 tr("Invalid machine state: %s"),
4877 Global::stringifyMachineState(mData->mMachineState));
4878
4879 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4880 aControllerName,
4881 aControllerPort,
4882 aDevice);
4883 if (!pAttach)
4884 return setError(VBOX_E_OBJECT_NOT_FOUND,
4885 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4886 aDevice, aControllerPort, aControllerName);
4887
4888
4889 setModified(IsModified_Storage);
4890 mMediaData.backup();
4891
4892 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4893 if (aBandwidthGroup && group.isNull())
4894 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4895
4896 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4897
4898 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4899 if (strBandwidthGroupOld.isNotEmpty())
4900 {
4901 /* Get the bandwidth group object and release it - this must not fail. */
4902 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4903 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4904 Assert(SUCCEEDED(rc));
4905
4906 pBandwidthGroupOld->release();
4907 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4908 }
4909
4910 if (!group.isNull())
4911 {
4912 group->reference();
4913 pAttach->updateBandwidthGroup(group->getName());
4914 }
4915
4916 return S_OK;
4917}
4918
4919STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4920 LONG aControllerPort,
4921 LONG aDevice,
4922 DeviceType_T aType)
4923{
4924 HRESULT rc = S_OK;
4925
4926 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4927 aControllerName, aControllerPort, aDevice, aType));
4928
4929 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4930
4931 return rc;
4932}
4933
4934
4935
4936STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4937 LONG aControllerPort,
4938 LONG aDevice,
4939 BOOL aForce)
4940{
4941 int rc = S_OK;
4942 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4943 aControllerName, aControllerPort, aForce));
4944
4945 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4946
4947 return rc;
4948}
4949
4950STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4951 LONG aControllerPort,
4952 LONG aDevice,
4953 IMedium *aMedium,
4954 BOOL aForce)
4955{
4956 int rc = S_OK;
4957 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4958 aControllerName, aControllerPort, aDevice, aForce));
4959
4960 CheckComArgStrNotEmptyOrNull(aControllerName);
4961
4962 AutoCaller autoCaller(this);
4963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4964
4965 // request the host lock first, since might be calling Host methods for getting host drives;
4966 // next, protect the media tree all the while we're in here, as well as our member variables
4967 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4968 this->lockHandle(),
4969 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4970
4971 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4972 aControllerName,
4973 aControllerPort,
4974 aDevice);
4975 if (pAttach.isNull())
4976 return setError(VBOX_E_OBJECT_NOT_FOUND,
4977 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4978 aDevice, aControllerPort, aControllerName);
4979
4980 /* Remember previously mounted medium. The medium before taking the
4981 * backup is not necessarily the same thing. */
4982 ComObjPtr<Medium> oldmedium;
4983 oldmedium = pAttach->getMedium();
4984
4985 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4986 if (aMedium && pMedium.isNull())
4987 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4988
4989 AutoCaller mediumCaller(pMedium);
4990 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4991
4992 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4993 if (pMedium)
4994 {
4995 DeviceType_T mediumType = pAttach->getType();
4996 switch (mediumType)
4997 {
4998 case DeviceType_DVD:
4999 case DeviceType_Floppy:
5000 break;
5001
5002 default:
5003 return setError(VBOX_E_INVALID_OBJECT_STATE,
5004 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5005 aControllerPort,
5006 aDevice,
5007 aControllerName);
5008 }
5009 }
5010
5011 setModified(IsModified_Storage);
5012 mMediaData.backup();
5013
5014 {
5015 // The backup operation makes the pAttach reference point to the
5016 // old settings. Re-get the correct reference.
5017 pAttach = findAttachment(mMediaData->mAttachments,
5018 aControllerName,
5019 aControllerPort,
5020 aDevice);
5021 if (!oldmedium.isNull())
5022 oldmedium->removeBackReference(mData->mUuid);
5023 if (!pMedium.isNull())
5024 {
5025 pMedium->addBackReference(mData->mUuid);
5026
5027 mediumLock.release();
5028 multiLock.release();
5029 addMediumToRegistry(pMedium);
5030 multiLock.acquire();
5031 mediumLock.acquire();
5032 }
5033
5034 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5035 pAttach->updateMedium(pMedium);
5036 }
5037
5038 setModified(IsModified_Storage);
5039
5040 mediumLock.release();
5041 multiLock.release();
5042 rc = onMediumChange(pAttach, aForce);
5043 multiLock.acquire();
5044 mediumLock.acquire();
5045
5046 /* On error roll back this change only. */
5047 if (FAILED(rc))
5048 {
5049 if (!pMedium.isNull())
5050 pMedium->removeBackReference(mData->mUuid);
5051 pAttach = findAttachment(mMediaData->mAttachments,
5052 aControllerName,
5053 aControllerPort,
5054 aDevice);
5055 /* If the attachment is gone in the meantime, bail out. */
5056 if (pAttach.isNull())
5057 return rc;
5058 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5059 if (!oldmedium.isNull())
5060 oldmedium->addBackReference(mData->mUuid);
5061 pAttach->updateMedium(oldmedium);
5062 }
5063
5064 mediumLock.release();
5065 multiLock.release();
5066
5067 mParent->saveModifiedRegistries();
5068
5069 return rc;
5070}
5071
5072STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5073 LONG aControllerPort,
5074 LONG aDevice,
5075 IMedium **aMedium)
5076{
5077 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5078 aControllerName, aControllerPort, aDevice));
5079
5080 CheckComArgStrNotEmptyOrNull(aControllerName);
5081 CheckComArgOutPointerValid(aMedium);
5082
5083 AutoCaller autoCaller(this);
5084 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5085
5086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5087
5088 *aMedium = NULL;
5089
5090 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5091 aControllerName,
5092 aControllerPort,
5093 aDevice);
5094 if (pAttach.isNull())
5095 return setError(VBOX_E_OBJECT_NOT_FOUND,
5096 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5097 aDevice, aControllerPort, aControllerName);
5098
5099 pAttach->getMedium().queryInterfaceTo(aMedium);
5100
5101 return S_OK;
5102}
5103
5104STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5105{
5106 CheckComArgOutPointerValid(port);
5107 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5108
5109 AutoCaller autoCaller(this);
5110 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5111
5112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5113
5114 mSerialPorts[slot].queryInterfaceTo(port);
5115
5116 return S_OK;
5117}
5118
5119STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5120{
5121 CheckComArgOutPointerValid(port);
5122 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5123
5124 AutoCaller autoCaller(this);
5125 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5126
5127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5128
5129 mParallelPorts[slot].queryInterfaceTo(port);
5130
5131 return S_OK;
5132}
5133
5134STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5135{
5136 CheckComArgOutPointerValid(adapter);
5137 /* Do not assert if slot is out of range, just return the advertised
5138 status. testdriver/vbox.py triggers this in logVmInfo. */
5139 if (slot >= mNetworkAdapters.size())
5140 return setError(E_INVALIDARG,
5141 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5142 slot, mNetworkAdapters.size());
5143
5144 AutoCaller autoCaller(this);
5145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5146
5147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5148
5149 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5150
5151 return S_OK;
5152}
5153
5154STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5155{
5156 CheckComArgOutSafeArrayPointerValid(aKeys);
5157
5158 AutoCaller autoCaller(this);
5159 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5160
5161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5162
5163 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5164 int i = 0;
5165 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5166 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5167 ++it, ++i)
5168 {
5169 const Utf8Str &strKey = it->first;
5170 strKey.cloneTo(&saKeys[i]);
5171 }
5172 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5173
5174 return S_OK;
5175 }
5176
5177 /**
5178 * @note Locks this object for reading.
5179 */
5180STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5181 BSTR *aValue)
5182{
5183 CheckComArgStrNotEmptyOrNull(aKey);
5184 CheckComArgOutPointerValid(aValue);
5185
5186 AutoCaller autoCaller(this);
5187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5188
5189 /* start with nothing found */
5190 Bstr bstrResult("");
5191
5192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5193
5194 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5195 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5196 // found:
5197 bstrResult = it->second; // source is a Utf8Str
5198
5199 /* return the result to caller (may be empty) */
5200 bstrResult.cloneTo(aValue);
5201
5202 return S_OK;
5203}
5204
5205 /**
5206 * @note Locks mParent for writing + this object for writing.
5207 */
5208STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5209{
5210 CheckComArgStrNotEmptyOrNull(aKey);
5211
5212 AutoCaller autoCaller(this);
5213 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5214
5215 Utf8Str strKey(aKey);
5216 Utf8Str strValue(aValue);
5217 Utf8Str strOldValue; // empty
5218
5219 // locking note: we only hold the read lock briefly to look up the old value,
5220 // then release it and call the onExtraCanChange callbacks. There is a small
5221 // chance of a race insofar as the callback might be called twice if two callers
5222 // change the same key at the same time, but that's a much better solution
5223 // than the deadlock we had here before. The actual changing of the extradata
5224 // is then performed under the write lock and race-free.
5225
5226 // look up the old value first; if nothing has changed then we need not do anything
5227 {
5228 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5229 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5230 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5231 strOldValue = it->second;
5232 }
5233
5234 bool fChanged;
5235 if ((fChanged = (strOldValue != strValue)))
5236 {
5237 // ask for permission from all listeners outside the locks;
5238 // onExtraDataCanChange() only briefly requests the VirtualBox
5239 // lock to copy the list of callbacks to invoke
5240 Bstr error;
5241 Bstr bstrValue(aValue);
5242
5243 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5244 {
5245 const char *sep = error.isEmpty() ? "" : ": ";
5246 CBSTR err = error.raw();
5247 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5248 sep, err));
5249 return setError(E_ACCESSDENIED,
5250 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5251 aKey,
5252 bstrValue.raw(),
5253 sep,
5254 err);
5255 }
5256
5257 // data is changing and change not vetoed: then write it out under the lock
5258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5259
5260 if (isSnapshotMachine())
5261 {
5262 HRESULT rc = checkStateDependency(MutableStateDep);
5263 if (FAILED(rc)) return rc;
5264 }
5265
5266 if (strValue.isEmpty())
5267 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5268 else
5269 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5270 // creates a new key if needed
5271
5272 bool fNeedsGlobalSaveSettings = false;
5273 saveSettings(&fNeedsGlobalSaveSettings);
5274
5275 if (fNeedsGlobalSaveSettings)
5276 {
5277 // save the global settings; for that we should hold only the VirtualBox lock
5278 alock.release();
5279 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5280 mParent->saveSettings();
5281 }
5282 }
5283
5284 // fire notification outside the lock
5285 if (fChanged)
5286 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5287
5288 return S_OK;
5289}
5290
5291STDMETHODIMP Machine::SaveSettings()
5292{
5293 AutoCaller autoCaller(this);
5294 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5295
5296 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5297
5298 /* when there was auto-conversion, we want to save the file even if
5299 * the VM is saved */
5300 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5301 if (FAILED(rc)) return rc;
5302
5303 /* the settings file path may never be null */
5304 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5305
5306 /* save all VM data excluding snapshots */
5307 bool fNeedsGlobalSaveSettings = false;
5308 rc = saveSettings(&fNeedsGlobalSaveSettings);
5309 mlock.release();
5310
5311 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5312 {
5313 // save the global settings; for that we should hold only the VirtualBox lock
5314 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5315 rc = mParent->saveSettings();
5316 }
5317
5318 return rc;
5319}
5320
5321STDMETHODIMP Machine::DiscardSettings()
5322{
5323 AutoCaller autoCaller(this);
5324 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5325
5326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5327
5328 HRESULT rc = checkStateDependency(MutableStateDep);
5329 if (FAILED(rc)) return rc;
5330
5331 /*
5332 * during this rollback, the session will be notified if data has
5333 * been actually changed
5334 */
5335 rollback(true /* aNotify */);
5336
5337 return S_OK;
5338}
5339
5340/** @note Locks objects! */
5341STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5342 ComSafeArrayOut(IMedium*, aMedia))
5343{
5344 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5345 AutoLimitedCaller autoCaller(this);
5346 AssertComRCReturnRC(autoCaller.rc());
5347
5348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5349
5350 Guid id(getId());
5351
5352 if (mData->mSession.mState != SessionState_Unlocked)
5353 return setError(VBOX_E_INVALID_OBJECT_STATE,
5354 tr("Cannot unregister the machine '%s' while it is locked"),
5355 mUserData->s.strName.c_str());
5356
5357 // wait for state dependents to drop to zero
5358 ensureNoStateDependencies();
5359
5360 if (!mData->mAccessible)
5361 {
5362 // inaccessible maschines can only be unregistered; uninitialize ourselves
5363 // here because currently there may be no unregistered that are inaccessible
5364 // (this state combination is not supported). Note releasing the caller and
5365 // leaving the lock before calling uninit()
5366 alock.release();
5367 autoCaller.release();
5368
5369 uninit();
5370
5371 mParent->unregisterMachine(this, id);
5372 // calls VirtualBox::saveSettings()
5373
5374 return S_OK;
5375 }
5376
5377 HRESULT rc = S_OK;
5378
5379 // discard saved state
5380 if (mData->mMachineState == MachineState_Saved)
5381 {
5382 // add the saved state file to the list of files the caller should delete
5383 Assert(!mSSData->strStateFilePath.isEmpty());
5384 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5385
5386 mSSData->strStateFilePath.setNull();
5387
5388 // unconditionally set the machine state to powered off, we now
5389 // know no session has locked the machine
5390 mData->mMachineState = MachineState_PoweredOff;
5391 }
5392
5393 size_t cSnapshots = 0;
5394 if (mData->mFirstSnapshot)
5395 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5396 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5397 // fail now before we start detaching media
5398 return setError(VBOX_E_INVALID_OBJECT_STATE,
5399 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5400 mUserData->s.strName.c_str(), cSnapshots);
5401
5402 // This list collects the medium objects from all medium attachments
5403 // which we will detach from the machine and its snapshots, in a specific
5404 // order which allows for closing all media without getting "media in use"
5405 // errors, simply by going through the list from the front to the back:
5406 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5407 // and must be closed before the parent media from the snapshots, or closing the parents
5408 // will fail because they still have children);
5409 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5410 // the root ("first") snapshot of the machine.
5411 MediaList llMedia;
5412
5413 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5414 && mMediaData->mAttachments.size()
5415 )
5416 {
5417 // we have media attachments: detach them all and add the Medium objects to our list
5418 if (cleanupMode != CleanupMode_UnregisterOnly)
5419 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5420 else
5421 return setError(VBOX_E_INVALID_OBJECT_STATE,
5422 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5423 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5424 }
5425
5426 if (cSnapshots)
5427 {
5428 // autoCleanup must be true here, or we would have failed above
5429
5430 // add the media from the medium attachments of the snapshots to llMedia
5431 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5432 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5433 // into the children first
5434
5435 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5436 MachineState_T oldState = mData->mMachineState;
5437 mData->mMachineState = MachineState_DeletingSnapshot;
5438
5439 // make a copy of the first snapshot so the refcount does not drop to 0
5440 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5441 // because of the AutoCaller voodoo)
5442 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5443
5444 // GO!
5445 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5446
5447 mData->mMachineState = oldState;
5448 }
5449
5450 if (FAILED(rc))
5451 {
5452 rollbackMedia();
5453 return rc;
5454 }
5455
5456 // commit all the media changes made above
5457 commitMedia();
5458
5459 mData->mRegistered = false;
5460
5461 // machine lock no longer needed
5462 alock.release();
5463
5464 // return media to caller
5465 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5466 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5467
5468 mParent->unregisterMachine(this, id);
5469 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5470
5471 return S_OK;
5472}
5473
5474struct Machine::DeleteTask
5475{
5476 ComObjPtr<Machine> pMachine;
5477 RTCList<ComPtr<IMedium> > llMediums;
5478 StringsList llFilesToDelete;
5479 ComObjPtr<Progress> pProgress;
5480};
5481
5482STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5483{
5484 LogFlowFuncEnter();
5485
5486 AutoCaller autoCaller(this);
5487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5488
5489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5490
5491 HRESULT rc = checkStateDependency(MutableStateDep);
5492 if (FAILED(rc)) return rc;
5493
5494 if (mData->mRegistered)
5495 return setError(VBOX_E_INVALID_VM_STATE,
5496 tr("Cannot delete settings of a registered machine"));
5497
5498 DeleteTask *pTask = new DeleteTask;
5499 pTask->pMachine = this;
5500 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5501
5502 // collect files to delete
5503 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5504
5505 for (size_t i = 0; i < sfaMedia.size(); ++i)
5506 {
5507 IMedium *pIMedium(sfaMedia[i]);
5508 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5509 if (pMedium.isNull())
5510 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5511 SafeArray<BSTR> ids;
5512 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5513 if (FAILED(rc)) return rc;
5514 /* At this point the medium should not have any back references
5515 * anymore. If it has it is attached to another VM and *must* not
5516 * deleted. */
5517 if (ids.size() < 1)
5518 pTask->llMediums.append(pMedium);
5519 }
5520 if (mData->pMachineConfigFile->fileExists())
5521 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5522
5523 pTask->pProgress.createObject();
5524 pTask->pProgress->init(getVirtualBox(),
5525 static_cast<IMachine*>(this) /* aInitiator */,
5526 Bstr(tr("Deleting files")).raw(),
5527 true /* fCancellable */,
5528 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5529 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5530
5531 int vrc = RTThreadCreate(NULL,
5532 Machine::deleteThread,
5533 (void*)pTask,
5534 0,
5535 RTTHREADTYPE_MAIN_WORKER,
5536 0,
5537 "MachineDelete");
5538
5539 pTask->pProgress.queryInterfaceTo(aProgress);
5540
5541 if (RT_FAILURE(vrc))
5542 {
5543 delete pTask;
5544 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5545 }
5546
5547 LogFlowFuncLeave();
5548
5549 return S_OK;
5550}
5551
5552/**
5553 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5554 * calls Machine::deleteTaskWorker() on the actual machine object.
5555 * @param Thread
5556 * @param pvUser
5557 * @return
5558 */
5559/*static*/
5560DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5561{
5562 LogFlowFuncEnter();
5563
5564 DeleteTask *pTask = (DeleteTask*)pvUser;
5565 Assert(pTask);
5566 Assert(pTask->pMachine);
5567 Assert(pTask->pProgress);
5568
5569 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5570 pTask->pProgress->notifyComplete(rc);
5571
5572 delete pTask;
5573
5574 LogFlowFuncLeave();
5575
5576 NOREF(Thread);
5577
5578 return VINF_SUCCESS;
5579}
5580
5581/**
5582 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5583 * @param task
5584 * @return
5585 */
5586HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5587{
5588 AutoCaller autoCaller(this);
5589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5590
5591 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5592
5593 HRESULT rc = S_OK;
5594
5595 try
5596 {
5597 ULONG uLogHistoryCount = 3;
5598 ComPtr<ISystemProperties> systemProperties;
5599 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5600 if (FAILED(rc)) throw rc;
5601
5602 if (!systemProperties.isNull())
5603 {
5604 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5605 if (FAILED(rc)) throw rc;
5606 }
5607
5608 MachineState_T oldState = mData->mMachineState;
5609 setMachineState(MachineState_SettingUp);
5610 alock.release();
5611 for (size_t i = 0; i < task.llMediums.size(); ++i)
5612 {
5613 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5614 {
5615 AutoCaller mac(pMedium);
5616 if (FAILED(mac.rc())) throw mac.rc();
5617 Utf8Str strLocation = pMedium->getLocationFull();
5618 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5619 if (FAILED(rc)) throw rc;
5620 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5621 }
5622 ComPtr<IProgress> pProgress2;
5623 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5624 if (FAILED(rc)) throw rc;
5625 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5626 if (FAILED(rc)) throw rc;
5627 /* Check the result of the asynchrony process. */
5628 LONG iRc;
5629 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5630 if (FAILED(rc)) throw rc;
5631 /* If the thread of the progress object has an error, then
5632 * retrieve the error info from there, or it'll be lost. */
5633 if (FAILED(iRc))
5634 throw setError(ProgressErrorInfo(pProgress2));
5635 }
5636 setMachineState(oldState);
5637 alock.acquire();
5638
5639 // delete the files pushed on the task list by Machine::Delete()
5640 // (this includes saved states of the machine and snapshots and
5641 // medium storage files from the IMedium list passed in, and the
5642 // machine XML file)
5643 StringsList::const_iterator it = task.llFilesToDelete.begin();
5644 while (it != task.llFilesToDelete.end())
5645 {
5646 const Utf8Str &strFile = *it;
5647 LogFunc(("Deleting file %s\n", strFile.c_str()));
5648 int vrc = RTFileDelete(strFile.c_str());
5649 if (RT_FAILURE(vrc))
5650 throw setError(VBOX_E_IPRT_ERROR,
5651 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5652
5653 ++it;
5654 if (it == task.llFilesToDelete.end())
5655 {
5656 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5657 if (FAILED(rc)) throw rc;
5658 break;
5659 }
5660
5661 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5662 if (FAILED(rc)) throw rc;
5663 }
5664
5665 /* delete the settings only when the file actually exists */
5666 if (mData->pMachineConfigFile->fileExists())
5667 {
5668 /* Delete any backup or uncommitted XML files. Ignore failures.
5669 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5670 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5671 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5672 RTFileDelete(otherXml.c_str());
5673 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5674 RTFileDelete(otherXml.c_str());
5675
5676 /* delete the Logs folder, nothing important should be left
5677 * there (we don't check for errors because the user might have
5678 * some private files there that we don't want to delete) */
5679 Utf8Str logFolder;
5680 getLogFolder(logFolder);
5681 Assert(logFolder.length());
5682 if (RTDirExists(logFolder.c_str()))
5683 {
5684 /* Delete all VBox.log[.N] files from the Logs folder
5685 * (this must be in sync with the rotation logic in
5686 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5687 * files that may have been created by the GUI. */
5688 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5689 logFolder.c_str(), RTPATH_DELIMITER);
5690 RTFileDelete(log.c_str());
5691 log = Utf8StrFmt("%s%cVBox.png",
5692 logFolder.c_str(), RTPATH_DELIMITER);
5693 RTFileDelete(log.c_str());
5694 for (int i = uLogHistoryCount; i > 0; i--)
5695 {
5696 log = Utf8StrFmt("%s%cVBox.log.%d",
5697 logFolder.c_str(), RTPATH_DELIMITER, i);
5698 RTFileDelete(log.c_str());
5699 log = Utf8StrFmt("%s%cVBox.png.%d",
5700 logFolder.c_str(), RTPATH_DELIMITER, i);
5701 RTFileDelete(log.c_str());
5702 }
5703
5704 RTDirRemove(logFolder.c_str());
5705 }
5706
5707 /* delete the Snapshots folder, nothing important should be left
5708 * there (we don't check for errors because the user might have
5709 * some private files there that we don't want to delete) */
5710 Utf8Str strFullSnapshotFolder;
5711 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5712 Assert(!strFullSnapshotFolder.isEmpty());
5713 if (RTDirExists(strFullSnapshotFolder.c_str()))
5714 RTDirRemove(strFullSnapshotFolder.c_str());
5715
5716 // delete the directory that contains the settings file, but only
5717 // if it matches the VM name
5718 Utf8Str settingsDir;
5719 if (isInOwnDir(&settingsDir))
5720 RTDirRemove(settingsDir.c_str());
5721 }
5722
5723 alock.release();
5724
5725 mParent->saveModifiedRegistries();
5726 }
5727 catch (HRESULT aRC) { rc = aRC; }
5728
5729 return rc;
5730}
5731
5732STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5733{
5734 CheckComArgOutPointerValid(aSnapshot);
5735
5736 AutoCaller autoCaller(this);
5737 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5738
5739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5740
5741 ComObjPtr<Snapshot> pSnapshot;
5742 HRESULT rc;
5743
5744 if (!aNameOrId || !*aNameOrId)
5745 // null case (caller wants root snapshot): findSnapshotById() handles this
5746 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5747 else
5748 {
5749 Guid uuid(aNameOrId);
5750 if (uuid.isValid())
5751 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5752 else
5753 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5754 }
5755 pSnapshot.queryInterfaceTo(aSnapshot);
5756
5757 return rc;
5758}
5759
5760STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5761{
5762 CheckComArgStrNotEmptyOrNull(aName);
5763 CheckComArgStrNotEmptyOrNull(aHostPath);
5764
5765 AutoCaller autoCaller(this);
5766 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5767
5768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5769
5770 HRESULT rc = checkStateDependency(MutableStateDep);
5771 if (FAILED(rc)) return rc;
5772
5773 Utf8Str strName(aName);
5774
5775 ComObjPtr<SharedFolder> sharedFolder;
5776 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5777 if (SUCCEEDED(rc))
5778 return setError(VBOX_E_OBJECT_IN_USE,
5779 tr("Shared folder named '%s' already exists"),
5780 strName.c_str());
5781
5782 sharedFolder.createObject();
5783 rc = sharedFolder->init(getMachine(),
5784 strName,
5785 aHostPath,
5786 !!aWritable,
5787 !!aAutoMount,
5788 true /* fFailOnError */);
5789 if (FAILED(rc)) return rc;
5790
5791 setModified(IsModified_SharedFolders);
5792 mHWData.backup();
5793 mHWData->mSharedFolders.push_back(sharedFolder);
5794
5795 /* inform the direct session if any */
5796 alock.release();
5797 onSharedFolderChange();
5798
5799 return S_OK;
5800}
5801
5802STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5803{
5804 CheckComArgStrNotEmptyOrNull(aName);
5805
5806 AutoCaller autoCaller(this);
5807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5808
5809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5810
5811 HRESULT rc = checkStateDependency(MutableStateDep);
5812 if (FAILED(rc)) return rc;
5813
5814 ComObjPtr<SharedFolder> sharedFolder;
5815 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5816 if (FAILED(rc)) return rc;
5817
5818 setModified(IsModified_SharedFolders);
5819 mHWData.backup();
5820 mHWData->mSharedFolders.remove(sharedFolder);
5821
5822 /* inform the direct session if any */
5823 alock.release();
5824 onSharedFolderChange();
5825
5826 return S_OK;
5827}
5828
5829STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5830{
5831 CheckComArgOutPointerValid(aCanShow);
5832
5833 /* start with No */
5834 *aCanShow = FALSE;
5835
5836 AutoCaller autoCaller(this);
5837 AssertComRCReturnRC(autoCaller.rc());
5838
5839 ComPtr<IInternalSessionControl> directControl;
5840 {
5841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5842
5843 if (mData->mSession.mState != SessionState_Locked)
5844 return setError(VBOX_E_INVALID_VM_STATE,
5845 tr("Machine is not locked for session (session state: %s)"),
5846 Global::stringifySessionState(mData->mSession.mState));
5847
5848 directControl = mData->mSession.mDirectControl;
5849 }
5850
5851 /* ignore calls made after #OnSessionEnd() is called */
5852 if (!directControl)
5853 return S_OK;
5854
5855 LONG64 dummy;
5856 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5857}
5858
5859STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5860{
5861 CheckComArgOutPointerValid(aWinId);
5862
5863 AutoCaller autoCaller(this);
5864 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5865
5866 ComPtr<IInternalSessionControl> directControl;
5867 {
5868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5869
5870 if (mData->mSession.mState != SessionState_Locked)
5871 return setError(E_FAIL,
5872 tr("Machine is not locked for session (session state: %s)"),
5873 Global::stringifySessionState(mData->mSession.mState));
5874
5875 directControl = mData->mSession.mDirectControl;
5876 }
5877
5878 /* ignore calls made after #OnSessionEnd() is called */
5879 if (!directControl)
5880 return S_OK;
5881
5882 BOOL dummy;
5883 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5884}
5885
5886#ifdef VBOX_WITH_GUEST_PROPS
5887/**
5888 * Look up a guest property in VBoxSVC's internal structures.
5889 */
5890HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5891 BSTR *aValue,
5892 LONG64 *aTimestamp,
5893 BSTR *aFlags) const
5894{
5895 using namespace guestProp;
5896
5897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5898 Utf8Str strName(aName);
5899 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5900
5901 if (it != mHWData->mGuestProperties.end())
5902 {
5903 char szFlags[MAX_FLAGS_LEN + 1];
5904 it->second.strValue.cloneTo(aValue);
5905 *aTimestamp = it->second.mTimestamp;
5906 writeFlags(it->second.mFlags, szFlags);
5907 Bstr(szFlags).cloneTo(aFlags);
5908 }
5909
5910 return S_OK;
5911}
5912
5913/**
5914 * Query the VM that a guest property belongs to for the property.
5915 * @returns E_ACCESSDENIED if the VM process is not available or not
5916 * currently handling queries and the lookup should then be done in
5917 * VBoxSVC.
5918 */
5919HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5920 BSTR *aValue,
5921 LONG64 *aTimestamp,
5922 BSTR *aFlags) const
5923{
5924 HRESULT rc;
5925 ComPtr<IInternalSessionControl> directControl;
5926 directControl = mData->mSession.mDirectControl;
5927
5928 /* fail if we were called after #OnSessionEnd() is called. This is a
5929 * silly race condition. */
5930
5931 /** @todo This code is bothering API clients (like python script clients) with
5932 * the AccessGuestProperty call, creating unncessary IPC. Need to
5933 * have a way of figuring out which kind of direct session it is... */
5934 if (!directControl)
5935 rc = E_ACCESSDENIED;
5936 else
5937 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5938 false /* isSetter */,
5939 aValue, aTimestamp, aFlags);
5940 return rc;
5941}
5942#endif // VBOX_WITH_GUEST_PROPS
5943
5944STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5945 BSTR *aValue,
5946 LONG64 *aTimestamp,
5947 BSTR *aFlags)
5948{
5949#ifndef VBOX_WITH_GUEST_PROPS
5950 ReturnComNotImplemented();
5951#else // VBOX_WITH_GUEST_PROPS
5952 CheckComArgStrNotEmptyOrNull(aName);
5953 CheckComArgOutPointerValid(aValue);
5954 CheckComArgOutPointerValid(aTimestamp);
5955 CheckComArgOutPointerValid(aFlags);
5956
5957 AutoCaller autoCaller(this);
5958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5959
5960 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5961 if (rc == E_ACCESSDENIED)
5962 /* The VM is not running or the service is not (yet) accessible */
5963 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5964 return rc;
5965#endif // VBOX_WITH_GUEST_PROPS
5966}
5967
5968STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5969{
5970 LONG64 dummyTimestamp;
5971 Bstr dummyFlags;
5972 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5973}
5974
5975STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5976{
5977 Bstr dummyValue;
5978 Bstr dummyFlags;
5979 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5980}
5981
5982#ifdef VBOX_WITH_GUEST_PROPS
5983/**
5984 * Set a guest property in VBoxSVC's internal structures.
5985 */
5986HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5987 IN_BSTR aFlags)
5988{
5989 using namespace guestProp;
5990
5991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5992 HRESULT rc = S_OK;
5993
5994 rc = checkStateDependency(MutableStateDep);
5995 if (FAILED(rc)) return rc;
5996
5997 try
5998 {
5999 Utf8Str utf8Name(aName);
6000 Utf8Str utf8Flags(aFlags);
6001 uint32_t fFlags = NILFLAG;
6002 if ( aFlags != NULL
6003 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6004 return setError(E_INVALIDARG,
6005 tr("Invalid guest property flag values: '%ls'"),
6006 aFlags);
6007
6008 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6009 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6010 if (it == mHWData->mGuestProperties.end())
6011 {
6012 if (!fDelete)
6013 {
6014 setModified(IsModified_MachineData);
6015 mHWData.backupEx();
6016
6017 RTTIMESPEC time;
6018 HWData::GuestProperty prop;
6019 prop.strValue = aValue;
6020 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6021 prop.mFlags = fFlags;
6022 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6023 }
6024 }
6025 else
6026 {
6027 if (it->second.mFlags & (RDONLYHOST))
6028 {
6029 rc = setError(E_ACCESSDENIED,
6030 tr("The property '%ls' cannot be changed by the host"),
6031 aName);
6032 }
6033 else
6034 {
6035 setModified(IsModified_MachineData);
6036 mHWData.backupEx();
6037
6038 /* The backupEx() operation invalidates our iterator,
6039 * so get a new one. */
6040 it = mHWData->mGuestProperties.find(utf8Name);
6041 Assert(it != mHWData->mGuestProperties.end());
6042
6043 if (!fDelete)
6044 {
6045 RTTIMESPEC time;
6046 it->second.strValue = aValue;
6047 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6048 it->second.mFlags = fFlags;
6049 }
6050 else
6051 mHWData->mGuestProperties.erase(it);
6052 }
6053 }
6054
6055 if ( SUCCEEDED(rc)
6056 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6057 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6058 RTSTR_MAX,
6059 utf8Name.c_str(),
6060 RTSTR_MAX,
6061 NULL)
6062 )
6063 )
6064 {
6065 alock.release();
6066
6067 mParent->onGuestPropertyChange(mData->mUuid, aName,
6068 aValue ? aValue : Bstr("").raw(),
6069 aFlags ? aFlags : Bstr("").raw());
6070 }
6071 }
6072 catch (std::bad_alloc &)
6073 {
6074 rc = E_OUTOFMEMORY;
6075 }
6076
6077 return rc;
6078}
6079
6080/**
6081 * Set a property on the VM that that property belongs to.
6082 * @returns E_ACCESSDENIED if the VM process is not available or not
6083 * currently handling queries and the setting should then be done in
6084 * VBoxSVC.
6085 */
6086HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6087 IN_BSTR aFlags)
6088{
6089 HRESULT rc;
6090
6091 try
6092 {
6093 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6094
6095 BSTR dummy = NULL; /* will not be changed (setter) */
6096 LONG64 dummy64;
6097 if (!directControl)
6098 rc = E_ACCESSDENIED;
6099 else
6100 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6101 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6102 true /* isSetter */,
6103 &dummy, &dummy64, &dummy);
6104 }
6105 catch (std::bad_alloc &)
6106 {
6107 rc = E_OUTOFMEMORY;
6108 }
6109
6110 return rc;
6111}
6112#endif // VBOX_WITH_GUEST_PROPS
6113
6114STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6115 IN_BSTR aFlags)
6116{
6117#ifndef VBOX_WITH_GUEST_PROPS
6118 ReturnComNotImplemented();
6119#else // VBOX_WITH_GUEST_PROPS
6120 CheckComArgStrNotEmptyOrNull(aName);
6121 CheckComArgMaybeNull(aFlags);
6122 CheckComArgMaybeNull(aValue);
6123
6124 AutoCaller autoCaller(this);
6125 if (FAILED(autoCaller.rc()))
6126 return autoCaller.rc();
6127
6128 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6129 if (rc == E_ACCESSDENIED)
6130 /* The VM is not running or the service is not (yet) accessible */
6131 rc = setGuestPropertyToService(aName, aValue, aFlags);
6132 return rc;
6133#endif // VBOX_WITH_GUEST_PROPS
6134}
6135
6136STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6137{
6138 return SetGuestProperty(aName, aValue, NULL);
6139}
6140
6141STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6142{
6143 return SetGuestProperty(aName, NULL, NULL);
6144}
6145
6146#ifdef VBOX_WITH_GUEST_PROPS
6147/**
6148 * Enumerate the guest properties in VBoxSVC's internal structures.
6149 */
6150HRESULT Machine::enumerateGuestPropertiesInService
6151 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6152 ComSafeArrayOut(BSTR, aValues),
6153 ComSafeArrayOut(LONG64, aTimestamps),
6154 ComSafeArrayOut(BSTR, aFlags))
6155{
6156 using namespace guestProp;
6157
6158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6159 Utf8Str strPatterns(aPatterns);
6160
6161 HWData::GuestPropertyMap propMap;
6162
6163 /*
6164 * Look for matching patterns and build up a list.
6165 */
6166 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6167 while (it != mHWData->mGuestProperties.end())
6168 {
6169 if ( strPatterns.isEmpty()
6170 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6171 RTSTR_MAX,
6172 it->first.c_str(),
6173 RTSTR_MAX,
6174 NULL)
6175 )
6176 {
6177 propMap.insert(*it);
6178 }
6179
6180 it++;
6181 }
6182
6183 alock.release();
6184
6185 /*
6186 * And build up the arrays for returning the property information.
6187 */
6188 size_t cEntries = propMap.size();
6189 SafeArray<BSTR> names(cEntries);
6190 SafeArray<BSTR> values(cEntries);
6191 SafeArray<LONG64> timestamps(cEntries);
6192 SafeArray<BSTR> flags(cEntries);
6193 size_t iProp = 0;
6194
6195 it = propMap.begin();
6196 while (it != propMap.end())
6197 {
6198 char szFlags[MAX_FLAGS_LEN + 1];
6199 it->first.cloneTo(&names[iProp]);
6200 it->second.strValue.cloneTo(&values[iProp]);
6201 timestamps[iProp] = it->second.mTimestamp;
6202 writeFlags(it->second.mFlags, szFlags);
6203 Bstr(szFlags).cloneTo(&flags[iProp++]);
6204 it++;
6205 }
6206 names.detachTo(ComSafeArrayOutArg(aNames));
6207 values.detachTo(ComSafeArrayOutArg(aValues));
6208 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6209 flags.detachTo(ComSafeArrayOutArg(aFlags));
6210 return S_OK;
6211}
6212
6213/**
6214 * Enumerate the properties managed by a VM.
6215 * @returns E_ACCESSDENIED if the VM process is not available or not
6216 * currently handling queries and the setting should then be done in
6217 * VBoxSVC.
6218 */
6219HRESULT Machine::enumerateGuestPropertiesOnVM
6220 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6221 ComSafeArrayOut(BSTR, aValues),
6222 ComSafeArrayOut(LONG64, aTimestamps),
6223 ComSafeArrayOut(BSTR, aFlags))
6224{
6225 HRESULT rc;
6226 ComPtr<IInternalSessionControl> directControl;
6227 directControl = mData->mSession.mDirectControl;
6228
6229 if (!directControl)
6230 rc = E_ACCESSDENIED;
6231 else
6232 rc = directControl->EnumerateGuestProperties
6233 (aPatterns, ComSafeArrayOutArg(aNames),
6234 ComSafeArrayOutArg(aValues),
6235 ComSafeArrayOutArg(aTimestamps),
6236 ComSafeArrayOutArg(aFlags));
6237 return rc;
6238}
6239#endif // VBOX_WITH_GUEST_PROPS
6240
6241STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6242 ComSafeArrayOut(BSTR, aNames),
6243 ComSafeArrayOut(BSTR, aValues),
6244 ComSafeArrayOut(LONG64, aTimestamps),
6245 ComSafeArrayOut(BSTR, aFlags))
6246{
6247#ifndef VBOX_WITH_GUEST_PROPS
6248 ReturnComNotImplemented();
6249#else // VBOX_WITH_GUEST_PROPS
6250 CheckComArgMaybeNull(aPatterns);
6251 CheckComArgOutSafeArrayPointerValid(aNames);
6252 CheckComArgOutSafeArrayPointerValid(aValues);
6253 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6254 CheckComArgOutSafeArrayPointerValid(aFlags);
6255
6256 AutoCaller autoCaller(this);
6257 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6258
6259 HRESULT rc = enumerateGuestPropertiesOnVM
6260 (aPatterns, ComSafeArrayOutArg(aNames),
6261 ComSafeArrayOutArg(aValues),
6262 ComSafeArrayOutArg(aTimestamps),
6263 ComSafeArrayOutArg(aFlags));
6264 if (rc == E_ACCESSDENIED)
6265 /* The VM is not running or the service is not (yet) accessible */
6266 rc = enumerateGuestPropertiesInService
6267 (aPatterns, ComSafeArrayOutArg(aNames),
6268 ComSafeArrayOutArg(aValues),
6269 ComSafeArrayOutArg(aTimestamps),
6270 ComSafeArrayOutArg(aFlags));
6271 return rc;
6272#endif // VBOX_WITH_GUEST_PROPS
6273}
6274
6275STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6276 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6277{
6278 MediaData::AttachmentList atts;
6279
6280 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6281 if (FAILED(rc)) return rc;
6282
6283 SafeIfaceArray<IMediumAttachment> attachments(atts);
6284 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6285
6286 return S_OK;
6287}
6288
6289STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6290 LONG aControllerPort,
6291 LONG aDevice,
6292 IMediumAttachment **aAttachment)
6293{
6294 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6295 aControllerName, aControllerPort, aDevice));
6296
6297 CheckComArgStrNotEmptyOrNull(aControllerName);
6298 CheckComArgOutPointerValid(aAttachment);
6299
6300 AutoCaller autoCaller(this);
6301 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6302
6303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6304
6305 *aAttachment = NULL;
6306
6307 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6308 aControllerName,
6309 aControllerPort,
6310 aDevice);
6311 if (pAttach.isNull())
6312 return setError(VBOX_E_OBJECT_NOT_FOUND,
6313 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6314 aDevice, aControllerPort, aControllerName);
6315
6316 pAttach.queryInterfaceTo(aAttachment);
6317
6318 return S_OK;
6319}
6320
6321STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6322 StorageBus_T aConnectionType,
6323 IStorageController **controller)
6324{
6325 CheckComArgStrNotEmptyOrNull(aName);
6326
6327 if ( (aConnectionType <= StorageBus_Null)
6328 || (aConnectionType > StorageBus_SAS))
6329 return setError(E_INVALIDARG,
6330 tr("Invalid connection type: %d"),
6331 aConnectionType);
6332
6333 AutoCaller autoCaller(this);
6334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6335
6336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6337
6338 HRESULT rc = checkStateDependency(MutableStateDep);
6339 if (FAILED(rc)) return rc;
6340
6341 /* try to find one with the name first. */
6342 ComObjPtr<StorageController> ctrl;
6343
6344 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6345 if (SUCCEEDED(rc))
6346 return setError(VBOX_E_OBJECT_IN_USE,
6347 tr("Storage controller named '%ls' already exists"),
6348 aName);
6349
6350 ctrl.createObject();
6351
6352 /* get a new instance number for the storage controller */
6353 ULONG ulInstance = 0;
6354 bool fBootable = true;
6355 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6356 it != mStorageControllers->end();
6357 ++it)
6358 {
6359 if ((*it)->getStorageBus() == aConnectionType)
6360 {
6361 ULONG ulCurInst = (*it)->getInstance();
6362
6363 if (ulCurInst >= ulInstance)
6364 ulInstance = ulCurInst + 1;
6365
6366 /* Only one controller of each type can be marked as bootable. */
6367 if ((*it)->getBootable())
6368 fBootable = false;
6369 }
6370 }
6371
6372 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6373 if (FAILED(rc)) return rc;
6374
6375 setModified(IsModified_Storage);
6376 mStorageControllers.backup();
6377 mStorageControllers->push_back(ctrl);
6378
6379 ctrl.queryInterfaceTo(controller);
6380
6381 /* inform the direct session if any */
6382 alock.release();
6383 onStorageControllerChange();
6384
6385 return S_OK;
6386}
6387
6388STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6389 IStorageController **aStorageController)
6390{
6391 CheckComArgStrNotEmptyOrNull(aName);
6392
6393 AutoCaller autoCaller(this);
6394 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6395
6396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6397
6398 ComObjPtr<StorageController> ctrl;
6399
6400 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6401 if (SUCCEEDED(rc))
6402 ctrl.queryInterfaceTo(aStorageController);
6403
6404 return rc;
6405}
6406
6407STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6408 IStorageController **aStorageController)
6409{
6410 AutoCaller autoCaller(this);
6411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6412
6413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6414
6415 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6416 it != mStorageControllers->end();
6417 ++it)
6418 {
6419 if ((*it)->getInstance() == aInstance)
6420 {
6421 (*it).queryInterfaceTo(aStorageController);
6422 return S_OK;
6423 }
6424 }
6425
6426 return setError(VBOX_E_OBJECT_NOT_FOUND,
6427 tr("Could not find a storage controller with instance number '%lu'"),
6428 aInstance);
6429}
6430
6431STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6432{
6433 AutoCaller autoCaller(this);
6434 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6435
6436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6437
6438 HRESULT rc = checkStateDependency(MutableStateDep);
6439 if (FAILED(rc)) return rc;
6440
6441 ComObjPtr<StorageController> ctrl;
6442
6443 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6444 if (SUCCEEDED(rc))
6445 {
6446 /* Ensure that only one controller of each type is marked as bootable. */
6447 if (fBootable == TRUE)
6448 {
6449 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6450 it != mStorageControllers->end();
6451 ++it)
6452 {
6453 ComObjPtr<StorageController> aCtrl = (*it);
6454
6455 if ( (aCtrl->getName() != Utf8Str(aName))
6456 && aCtrl->getBootable() == TRUE
6457 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6458 && aCtrl->getControllerType() == ctrl->getControllerType())
6459 {
6460 aCtrl->setBootable(FALSE);
6461 break;
6462 }
6463 }
6464 }
6465
6466 if (SUCCEEDED(rc))
6467 {
6468 ctrl->setBootable(fBootable);
6469 setModified(IsModified_Storage);
6470 }
6471 }
6472
6473 if (SUCCEEDED(rc))
6474 {
6475 /* inform the direct session if any */
6476 alock.release();
6477 onStorageControllerChange();
6478 }
6479
6480 return rc;
6481}
6482
6483STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6484{
6485 CheckComArgStrNotEmptyOrNull(aName);
6486
6487 AutoCaller autoCaller(this);
6488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6489
6490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6491
6492 HRESULT rc = checkStateDependency(MutableStateDep);
6493 if (FAILED(rc)) return rc;
6494
6495 ComObjPtr<StorageController> ctrl;
6496 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6497 if (FAILED(rc)) return rc;
6498
6499 {
6500 /* find all attached devices to the appropriate storage controller and detach them all */
6501 // make a temporary list because detachDevice invalidates iterators into
6502 // mMediaData->mAttachments
6503 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6504
6505 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6506 it != llAttachments2.end();
6507 ++it)
6508 {
6509 MediumAttachment *pAttachTemp = *it;
6510
6511 AutoCaller localAutoCaller(pAttachTemp);
6512 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6513
6514 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6515
6516 if (pAttachTemp->getControllerName() == aName)
6517 {
6518 rc = detachDevice(pAttachTemp, alock, NULL);
6519 if (FAILED(rc)) return rc;
6520 }
6521 }
6522 }
6523
6524 /* We can remove it now. */
6525 setModified(IsModified_Storage);
6526 mStorageControllers.backup();
6527
6528 ctrl->unshare();
6529
6530 mStorageControllers->remove(ctrl);
6531
6532 /* inform the direct session if any */
6533 alock.release();
6534 onStorageControllerChange();
6535
6536 return S_OK;
6537}
6538
6539STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6540 IUSBController **controller)
6541{
6542 if ( (aType <= USBControllerType_Null)
6543 || (aType >= USBControllerType_Last))
6544 return setError(E_INVALIDARG,
6545 tr("Invalid USB controller type: %d"),
6546 aType);
6547
6548 AutoCaller autoCaller(this);
6549 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6550
6551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6552
6553 HRESULT rc = checkStateDependency(MutableStateDep);
6554 if (FAILED(rc)) return rc;
6555
6556 /* try to find one with the same type first. */
6557 ComObjPtr<USBController> ctrl;
6558
6559 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6560 if (SUCCEEDED(rc))
6561 return setError(VBOX_E_OBJECT_IN_USE,
6562 tr("USB controller named '%ls' already exists"),
6563 aName);
6564
6565 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6566 ULONG maxInstances;
6567 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6568 if (FAILED(rc))
6569 return rc;
6570
6571 ULONG cInstances = getUSBControllerCountByType(aType);
6572 if (cInstances >= maxInstances)
6573 return setError(E_INVALIDARG,
6574 tr("Too many USB controllers of this type"));
6575
6576 ctrl.createObject();
6577
6578 rc = ctrl->init(this, aName, aType);
6579 if (FAILED(rc)) return rc;
6580
6581 setModified(IsModified_USB);
6582 mUSBControllers.backup();
6583 mUSBControllers->push_back(ctrl);
6584
6585 ctrl.queryInterfaceTo(controller);
6586
6587 /* inform the direct session if any */
6588 alock.release();
6589 onUSBControllerChange();
6590
6591 return S_OK;
6592}
6593
6594STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6595{
6596 CheckComArgStrNotEmptyOrNull(aName);
6597
6598 AutoCaller autoCaller(this);
6599 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6600
6601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6602
6603 ComObjPtr<USBController> ctrl;
6604
6605 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6606 if (SUCCEEDED(rc))
6607 ctrl.queryInterfaceTo(aUSBController);
6608
6609 return rc;
6610}
6611
6612STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6613 ULONG *aControllers)
6614{
6615 CheckComArgOutPointerValid(aControllers);
6616
6617 if ( (aType <= USBControllerType_Null)
6618 || (aType >= USBControllerType_Last))
6619 return setError(E_INVALIDARG,
6620 tr("Invalid USB controller type: %d"),
6621 aType);
6622
6623 AutoCaller autoCaller(this);
6624 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6625
6626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6627
6628 ComObjPtr<USBController> ctrl;
6629
6630 *aControllers = getUSBControllerCountByType(aType);
6631
6632 return S_OK;
6633}
6634
6635STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6636{
6637 CheckComArgStrNotEmptyOrNull(aName);
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 ComObjPtr<USBController> ctrl;
6648 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6649 if (FAILED(rc)) return rc;
6650
6651 setModified(IsModified_USB);
6652 mUSBControllers.backup();
6653
6654 ctrl->unshare();
6655
6656 mUSBControllers->remove(ctrl);
6657
6658 /* inform the direct session if any */
6659 alock.release();
6660 onUSBControllerChange();
6661
6662 return S_OK;
6663}
6664
6665STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6666 ULONG *puOriginX,
6667 ULONG *puOriginY,
6668 ULONG *puWidth,
6669 ULONG *puHeight,
6670 BOOL *pfEnabled)
6671{
6672 LogFlowThisFunc(("\n"));
6673
6674 CheckComArgNotNull(puOriginX);
6675 CheckComArgNotNull(puOriginY);
6676 CheckComArgNotNull(puWidth);
6677 CheckComArgNotNull(puHeight);
6678 CheckComArgNotNull(pfEnabled);
6679
6680 uint32_t u32OriginX= 0;
6681 uint32_t u32OriginY= 0;
6682 uint32_t u32Width = 0;
6683 uint32_t u32Height = 0;
6684 uint16_t u16Flags = 0;
6685
6686 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6687 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6688 if (RT_FAILURE(vrc))
6689 {
6690#ifdef RT_OS_WINDOWS
6691 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6692 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6693 * So just assign fEnable to TRUE again.
6694 * The right fix would be to change GUI API wrappers to make sure that parameters
6695 * are changed only if API succeeds.
6696 */
6697 *pfEnabled = TRUE;
6698#endif
6699 return setError(VBOX_E_IPRT_ERROR,
6700 tr("Saved guest size is not available (%Rrc)"),
6701 vrc);
6702 }
6703
6704 *puOriginX = u32OriginX;
6705 *puOriginY = u32OriginY;
6706 *puWidth = u32Width;
6707 *puHeight = u32Height;
6708 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6709
6710 return S_OK;
6711}
6712
6713STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6714{
6715 LogFlowThisFunc(("\n"));
6716
6717 CheckComArgNotNull(aSize);
6718 CheckComArgNotNull(aWidth);
6719 CheckComArgNotNull(aHeight);
6720
6721 if (aScreenId != 0)
6722 return E_NOTIMPL;
6723
6724 AutoCaller autoCaller(this);
6725 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6726
6727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6728
6729 uint8_t *pu8Data = NULL;
6730 uint32_t cbData = 0;
6731 uint32_t u32Width = 0;
6732 uint32_t u32Height = 0;
6733
6734 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6735
6736 if (RT_FAILURE(vrc))
6737 return setError(VBOX_E_IPRT_ERROR,
6738 tr("Saved screenshot data is not available (%Rrc)"),
6739 vrc);
6740
6741 *aSize = cbData;
6742 *aWidth = u32Width;
6743 *aHeight = u32Height;
6744
6745 freeSavedDisplayScreenshot(pu8Data);
6746
6747 return S_OK;
6748}
6749
6750STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6751{
6752 LogFlowThisFunc(("\n"));
6753
6754 CheckComArgNotNull(aWidth);
6755 CheckComArgNotNull(aHeight);
6756 CheckComArgOutSafeArrayPointerValid(aData);
6757
6758 if (aScreenId != 0)
6759 return E_NOTIMPL;
6760
6761 AutoCaller autoCaller(this);
6762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6763
6764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6765
6766 uint8_t *pu8Data = NULL;
6767 uint32_t cbData = 0;
6768 uint32_t u32Width = 0;
6769 uint32_t u32Height = 0;
6770
6771 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6772
6773 if (RT_FAILURE(vrc))
6774 return setError(VBOX_E_IPRT_ERROR,
6775 tr("Saved screenshot data is not available (%Rrc)"),
6776 vrc);
6777
6778 *aWidth = u32Width;
6779 *aHeight = u32Height;
6780
6781 com::SafeArray<BYTE> bitmap(cbData);
6782 /* Convert pixels to format expected by the API caller. */
6783 if (aBGR)
6784 {
6785 /* [0] B, [1] G, [2] R, [3] A. */
6786 for (unsigned i = 0; i < cbData; i += 4)
6787 {
6788 bitmap[i] = pu8Data[i];
6789 bitmap[i + 1] = pu8Data[i + 1];
6790 bitmap[i + 2] = pu8Data[i + 2];
6791 bitmap[i + 3] = 0xff;
6792 }
6793 }
6794 else
6795 {
6796 /* [0] R, [1] G, [2] B, [3] A. */
6797 for (unsigned i = 0; i < cbData; i += 4)
6798 {
6799 bitmap[i] = pu8Data[i + 2];
6800 bitmap[i + 1] = pu8Data[i + 1];
6801 bitmap[i + 2] = pu8Data[i];
6802 bitmap[i + 3] = 0xff;
6803 }
6804 }
6805 bitmap.detachTo(ComSafeArrayOutArg(aData));
6806
6807 freeSavedDisplayScreenshot(pu8Data);
6808
6809 return S_OK;
6810}
6811
6812
6813STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6814{
6815 LogFlowThisFunc(("\n"));
6816
6817 CheckComArgNotNull(aWidth);
6818 CheckComArgNotNull(aHeight);
6819 CheckComArgOutSafeArrayPointerValid(aData);
6820
6821 if (aScreenId != 0)
6822 return E_NOTIMPL;
6823
6824 AutoCaller autoCaller(this);
6825 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6826
6827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6828
6829 uint8_t *pu8Data = NULL;
6830 uint32_t cbData = 0;
6831 uint32_t u32Width = 0;
6832 uint32_t u32Height = 0;
6833
6834 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6835
6836 if (RT_FAILURE(vrc))
6837 return setError(VBOX_E_IPRT_ERROR,
6838 tr("Saved screenshot data is not available (%Rrc)"),
6839 vrc);
6840
6841 *aWidth = u32Width;
6842 *aHeight = u32Height;
6843
6844 HRESULT rc = S_OK;
6845 uint8_t *pu8PNG = NULL;
6846 uint32_t cbPNG = 0;
6847 uint32_t cxPNG = 0;
6848 uint32_t cyPNG = 0;
6849
6850 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6851
6852 if (RT_SUCCESS(vrc))
6853 {
6854 com::SafeArray<BYTE> screenData(cbPNG);
6855 screenData.initFrom(pu8PNG, cbPNG);
6856 if (pu8PNG)
6857 RTMemFree(pu8PNG);
6858 screenData.detachTo(ComSafeArrayOutArg(aData));
6859 }
6860 else
6861 {
6862 if (pu8PNG)
6863 RTMemFree(pu8PNG);
6864 return setError(VBOX_E_IPRT_ERROR,
6865 tr("Could not convert screenshot to PNG (%Rrc)"),
6866 vrc);
6867 }
6868
6869 freeSavedDisplayScreenshot(pu8Data);
6870
6871 return rc;
6872}
6873
6874STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6875{
6876 LogFlowThisFunc(("\n"));
6877
6878 CheckComArgNotNull(aSize);
6879 CheckComArgNotNull(aWidth);
6880 CheckComArgNotNull(aHeight);
6881
6882 if (aScreenId != 0)
6883 return E_NOTIMPL;
6884
6885 AutoCaller autoCaller(this);
6886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6887
6888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6889
6890 uint8_t *pu8Data = NULL;
6891 uint32_t cbData = 0;
6892 uint32_t u32Width = 0;
6893 uint32_t u32Height = 0;
6894
6895 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6896
6897 if (RT_FAILURE(vrc))
6898 return setError(VBOX_E_IPRT_ERROR,
6899 tr("Saved screenshot data is not available (%Rrc)"),
6900 vrc);
6901
6902 *aSize = cbData;
6903 *aWidth = u32Width;
6904 *aHeight = u32Height;
6905
6906 freeSavedDisplayScreenshot(pu8Data);
6907
6908 return S_OK;
6909}
6910
6911STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6912{
6913 LogFlowThisFunc(("\n"));
6914
6915 CheckComArgNotNull(aWidth);
6916 CheckComArgNotNull(aHeight);
6917 CheckComArgOutSafeArrayPointerValid(aData);
6918
6919 if (aScreenId != 0)
6920 return E_NOTIMPL;
6921
6922 AutoCaller autoCaller(this);
6923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6924
6925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6926
6927 uint8_t *pu8Data = NULL;
6928 uint32_t cbData = 0;
6929 uint32_t u32Width = 0;
6930 uint32_t u32Height = 0;
6931
6932 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6933
6934 if (RT_FAILURE(vrc))
6935 return setError(VBOX_E_IPRT_ERROR,
6936 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6937 vrc);
6938
6939 *aWidth = u32Width;
6940 *aHeight = u32Height;
6941
6942 com::SafeArray<BYTE> png(cbData);
6943 png.initFrom(pu8Data, cbData);
6944 png.detachTo(ComSafeArrayOutArg(aData));
6945
6946 freeSavedDisplayScreenshot(pu8Data);
6947
6948 return S_OK;
6949}
6950
6951STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6952{
6953 HRESULT rc = S_OK;
6954 LogFlowThisFunc(("\n"));
6955
6956 AutoCaller autoCaller(this);
6957 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6958
6959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6960
6961 if (!mHWData->mCPUHotPlugEnabled)
6962 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6963
6964 if (aCpu >= mHWData->mCPUCount)
6965 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6966
6967 if (mHWData->mCPUAttached[aCpu])
6968 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6969
6970 alock.release();
6971 rc = onCPUChange(aCpu, false);
6972 alock.acquire();
6973 if (FAILED(rc)) return rc;
6974
6975 setModified(IsModified_MachineData);
6976 mHWData.backup();
6977 mHWData->mCPUAttached[aCpu] = true;
6978
6979 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6980 if (Global::IsOnline(mData->mMachineState))
6981 saveSettings(NULL);
6982
6983 return S_OK;
6984}
6985
6986STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6987{
6988 HRESULT rc = S_OK;
6989 LogFlowThisFunc(("\n"));
6990
6991 AutoCaller autoCaller(this);
6992 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6993
6994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6995
6996 if (!mHWData->mCPUHotPlugEnabled)
6997 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6998
6999 if (aCpu >= SchemaDefs::MaxCPUCount)
7000 return setError(E_INVALIDARG,
7001 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7002 SchemaDefs::MaxCPUCount);
7003
7004 if (!mHWData->mCPUAttached[aCpu])
7005 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7006
7007 /* CPU 0 can't be detached */
7008 if (aCpu == 0)
7009 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7010
7011 alock.release();
7012 rc = onCPUChange(aCpu, true);
7013 alock.acquire();
7014 if (FAILED(rc)) return rc;
7015
7016 setModified(IsModified_MachineData);
7017 mHWData.backup();
7018 mHWData->mCPUAttached[aCpu] = false;
7019
7020 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7021 if (Global::IsOnline(mData->mMachineState))
7022 saveSettings(NULL);
7023
7024 return S_OK;
7025}
7026
7027STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7028{
7029 LogFlowThisFunc(("\n"));
7030
7031 CheckComArgNotNull(aCpuAttached);
7032
7033 *aCpuAttached = false;
7034
7035 AutoCaller autoCaller(this);
7036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7037
7038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7039
7040 /* If hotplug is enabled the CPU is always enabled. */
7041 if (!mHWData->mCPUHotPlugEnabled)
7042 {
7043 if (aCpu < mHWData->mCPUCount)
7044 *aCpuAttached = true;
7045 }
7046 else
7047 {
7048 if (aCpu < SchemaDefs::MaxCPUCount)
7049 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7050 }
7051
7052 return S_OK;
7053}
7054
7055STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7056{
7057 CheckComArgOutPointerValid(aName);
7058
7059 AutoCaller autoCaller(this);
7060 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7061
7062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7063
7064 Utf8Str log = queryLogFilename(aIdx);
7065 if (!RTFileExists(log.c_str()))
7066 log.setNull();
7067 log.cloneTo(aName);
7068
7069 return S_OK;
7070}
7071
7072STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7073{
7074 LogFlowThisFunc(("\n"));
7075 CheckComArgOutSafeArrayPointerValid(aData);
7076 if (aSize < 0)
7077 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7078
7079 AutoCaller autoCaller(this);
7080 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7081
7082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7083
7084 HRESULT rc = S_OK;
7085 Utf8Str log = queryLogFilename(aIdx);
7086
7087 /* do not unnecessarily hold the lock while doing something which does
7088 * not need the lock and potentially takes a long time. */
7089 alock.release();
7090
7091 /* Limit the chunk size to 32K for now, as that gives better performance
7092 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7093 * One byte expands to approx. 25 bytes of breathtaking XML. */
7094 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7095 com::SafeArray<BYTE> logData(cbData);
7096
7097 RTFILE LogFile;
7098 int vrc = RTFileOpen(&LogFile, log.c_str(),
7099 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7100 if (RT_SUCCESS(vrc))
7101 {
7102 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7103 if (RT_SUCCESS(vrc))
7104 logData.resize(cbData);
7105 else
7106 rc = setError(VBOX_E_IPRT_ERROR,
7107 tr("Could not read log file '%s' (%Rrc)"),
7108 log.c_str(), vrc);
7109 RTFileClose(LogFile);
7110 }
7111 else
7112 rc = setError(VBOX_E_IPRT_ERROR,
7113 tr("Could not open log file '%s' (%Rrc)"),
7114 log.c_str(), vrc);
7115
7116 if (FAILED(rc))
7117 logData.resize(0);
7118 logData.detachTo(ComSafeArrayOutArg(aData));
7119
7120 return rc;
7121}
7122
7123
7124/**
7125 * Currently this method doesn't attach device to the running VM,
7126 * just makes sure it's plugged on next VM start.
7127 */
7128STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7129{
7130 AutoCaller autoCaller(this);
7131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7132
7133 // lock scope
7134 {
7135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7136
7137 HRESULT rc = checkStateDependency(MutableStateDep);
7138 if (FAILED(rc)) return rc;
7139
7140 ChipsetType_T aChipset = ChipsetType_PIIX3;
7141 COMGETTER(ChipsetType)(&aChipset);
7142
7143 if (aChipset != ChipsetType_ICH9)
7144 {
7145 return setError(E_INVALIDARG,
7146 tr("Host PCI attachment only supported with ICH9 chipset"));
7147 }
7148
7149 // check if device with this host PCI address already attached
7150 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7151 it != mHWData->mPCIDeviceAssignments.end();
7152 ++it)
7153 {
7154 LONG iHostAddress = -1;
7155 ComPtr<PCIDeviceAttachment> pAttach;
7156 pAttach = *it;
7157 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7158 if (iHostAddress == hostAddress)
7159 return setError(E_INVALIDARG,
7160 tr("Device with host PCI address already attached to this VM"));
7161 }
7162
7163 ComObjPtr<PCIDeviceAttachment> pda;
7164 char name[32];
7165
7166 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7167 Bstr bname(name);
7168 pda.createObject();
7169 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7170 setModified(IsModified_MachineData);
7171 mHWData.backup();
7172 mHWData->mPCIDeviceAssignments.push_back(pda);
7173 }
7174
7175 return S_OK;
7176}
7177
7178/**
7179 * Currently this method doesn't detach device from the running VM,
7180 * just makes sure it's not plugged on next VM start.
7181 */
7182STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7183{
7184 AutoCaller autoCaller(this);
7185 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7186
7187 ComObjPtr<PCIDeviceAttachment> pAttach;
7188 bool fRemoved = false;
7189 HRESULT rc;
7190
7191 // lock scope
7192 {
7193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7194
7195 rc = checkStateDependency(MutableStateDep);
7196 if (FAILED(rc)) return rc;
7197
7198 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7199 it != mHWData->mPCIDeviceAssignments.end();
7200 ++it)
7201 {
7202 LONG iHostAddress = -1;
7203 pAttach = *it;
7204 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7205 if (iHostAddress != -1 && iHostAddress == hostAddress)
7206 {
7207 setModified(IsModified_MachineData);
7208 mHWData.backup();
7209 mHWData->mPCIDeviceAssignments.remove(pAttach);
7210 fRemoved = true;
7211 break;
7212 }
7213 }
7214 }
7215
7216
7217 /* Fire event outside of the lock */
7218 if (fRemoved)
7219 {
7220 Assert(!pAttach.isNull());
7221 ComPtr<IEventSource> es;
7222 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7223 Assert(SUCCEEDED(rc));
7224 Bstr mid;
7225 rc = this->COMGETTER(Id)(mid.asOutParam());
7226 Assert(SUCCEEDED(rc));
7227 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7228 }
7229
7230 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7231 tr("No host PCI device %08x attached"),
7232 hostAddress
7233 );
7234}
7235
7236STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7237{
7238 CheckComArgOutSafeArrayPointerValid(aAssignments);
7239
7240 AutoCaller autoCaller(this);
7241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7242
7243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7244
7245 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7246 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7247
7248 return S_OK;
7249}
7250
7251STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7252{
7253 CheckComArgOutPointerValid(aBandwidthControl);
7254
7255 AutoCaller autoCaller(this);
7256 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7257
7258 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7259
7260 return S_OK;
7261}
7262
7263STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7264{
7265 CheckComArgOutPointerValid(pfEnabled);
7266 AutoCaller autoCaller(this);
7267 HRESULT hrc = autoCaller.rc();
7268 if (SUCCEEDED(hrc))
7269 {
7270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7271 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7272 }
7273 return hrc;
7274}
7275
7276STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7277{
7278 AutoCaller autoCaller(this);
7279 HRESULT hrc = autoCaller.rc();
7280 if (SUCCEEDED(hrc))
7281 {
7282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7283 hrc = checkStateDependency(MutableStateDep);
7284 if (SUCCEEDED(hrc))
7285 {
7286 hrc = mHWData.backupEx();
7287 if (SUCCEEDED(hrc))
7288 {
7289 setModified(IsModified_MachineData);
7290 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7291 }
7292 }
7293 }
7294 return hrc;
7295}
7296
7297STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7298{
7299 CheckComArgOutPointerValid(pbstrConfig);
7300 AutoCaller autoCaller(this);
7301 HRESULT hrc = autoCaller.rc();
7302 if (SUCCEEDED(hrc))
7303 {
7304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7305 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7306 }
7307 return hrc;
7308}
7309
7310STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7311{
7312 CheckComArgStr(bstrConfig);
7313 AutoCaller autoCaller(this);
7314 HRESULT hrc = autoCaller.rc();
7315 if (SUCCEEDED(hrc))
7316 {
7317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7318 hrc = checkStateDependency(MutableStateDep);
7319 if (SUCCEEDED(hrc))
7320 {
7321 hrc = mHWData.backupEx();
7322 if (SUCCEEDED(hrc))
7323 {
7324 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7325 if (SUCCEEDED(hrc))
7326 setModified(IsModified_MachineData);
7327 }
7328 }
7329 }
7330 return hrc;
7331
7332}
7333
7334STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7335{
7336 CheckComArgOutPointerValid(pfAllow);
7337 AutoCaller autoCaller(this);
7338 HRESULT hrc = autoCaller.rc();
7339 if (SUCCEEDED(hrc))
7340 {
7341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7342 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7343 }
7344 return hrc;
7345}
7346
7347STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7348{
7349 AutoCaller autoCaller(this);
7350 HRESULT hrc = autoCaller.rc();
7351 if (SUCCEEDED(hrc))
7352 {
7353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7354 hrc = checkStateDependency(MutableStateDep);
7355 if (SUCCEEDED(hrc))
7356 {
7357 hrc = mHWData.backupEx();
7358 if (SUCCEEDED(hrc))
7359 {
7360 setModified(IsModified_MachineData);
7361 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7362 }
7363 }
7364 }
7365 return hrc;
7366}
7367
7368STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7369{
7370 CheckComArgOutPointerValid(pfEnabled);
7371 AutoCaller autoCaller(this);
7372 HRESULT hrc = autoCaller.rc();
7373 if (SUCCEEDED(hrc))
7374 {
7375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7376 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7377 }
7378 return hrc;
7379}
7380
7381STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7382{
7383 AutoCaller autoCaller(this);
7384 HRESULT hrc = autoCaller.rc();
7385 if (SUCCEEDED(hrc))
7386 {
7387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7388 hrc = checkStateDependency(MutableStateDep);
7389 if ( SUCCEEDED(hrc)
7390 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7391 {
7392 AutostartDb *autostartDb = mParent->getAutostartDb();
7393 int vrc;
7394
7395 if (fEnabled)
7396 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7397 else
7398 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7399
7400 if (RT_SUCCESS(vrc))
7401 {
7402 hrc = mHWData.backupEx();
7403 if (SUCCEEDED(hrc))
7404 {
7405 setModified(IsModified_MachineData);
7406 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7407 }
7408 }
7409 else if (vrc == VERR_NOT_SUPPORTED)
7410 hrc = setError(VBOX_E_NOT_SUPPORTED,
7411 tr("The VM autostart feature is not supported on this platform"));
7412 else if (vrc == VERR_PATH_NOT_FOUND)
7413 hrc = setError(E_FAIL,
7414 tr("The path to the autostart database is not set"));
7415 else
7416 hrc = setError(E_UNEXPECTED,
7417 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7418 fEnabled ? "Adding" : "Removing",
7419 mUserData->s.strName.c_str(), vrc);
7420 }
7421 }
7422 return hrc;
7423}
7424
7425STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7426{
7427 CheckComArgOutPointerValid(puDelay);
7428 AutoCaller autoCaller(this);
7429 HRESULT hrc = autoCaller.rc();
7430 if (SUCCEEDED(hrc))
7431 {
7432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7433 *puDelay = mHWData->mAutostart.uAutostartDelay;
7434 }
7435 return hrc;
7436}
7437
7438STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
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->mAutostart.uAutostartDelay = uDelay;
7453 }
7454 }
7455 }
7456 return hrc;
7457}
7458
7459STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7460{
7461 CheckComArgOutPointerValid(penmAutostopType);
7462 AutoCaller autoCaller(this);
7463 HRESULT hrc = autoCaller.rc();
7464 if (SUCCEEDED(hrc))
7465 {
7466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7467 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7468 }
7469 return hrc;
7470}
7471
7472STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
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.enmAutostopType != enmAutostopType)
7482 {
7483 AutostartDb *autostartDb = mParent->getAutostartDb();
7484 int vrc;
7485
7486 if (enmAutostopType != AutostopType_Disabled)
7487 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7488 else
7489 vrc = autostartDb->removeAutostopVM(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.enmAutostopType = enmAutostopType;
7498 }
7499 }
7500 else if (vrc == VERR_NOT_SUPPORTED)
7501 hrc = setError(VBOX_E_NOT_SUPPORTED,
7502 tr("The VM autostop 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 autostop database failed with %Rrc"),
7509 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7510 mUserData->s.strName.c_str(), vrc);
7511 }
7512 }
7513 return hrc;
7514}
7515
7516STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7517{
7518 CheckComArgOutPointerValid(aDefaultFrontend);
7519 AutoCaller autoCaller(this);
7520 HRESULT hrc = autoCaller.rc();
7521 if (SUCCEEDED(hrc))
7522 {
7523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7524 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7525 }
7526 return hrc;
7527}
7528
7529STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7530{
7531 CheckComArgStr(aDefaultFrontend);
7532 AutoCaller autoCaller(this);
7533 HRESULT hrc = autoCaller.rc();
7534 if (SUCCEEDED(hrc))
7535 {
7536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7537 hrc = checkStateDependency(MutableOrSavedStateDep);
7538 if (SUCCEEDED(hrc))
7539 {
7540 hrc = mHWData.backupEx();
7541 if (SUCCEEDED(hrc))
7542 {
7543 setModified(IsModified_MachineData);
7544 mHWData->mDefaultFrontend = aDefaultFrontend;
7545 }
7546 }
7547 }
7548 return hrc;
7549}
7550
7551STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7552{
7553 CheckComArgSafeArrayNotNull(aIcon);
7554 CheckComArgOutSafeArrayPointerValid(aIcon);
7555 AutoCaller autoCaller(this);
7556 HRESULT hrc = autoCaller.rc();
7557 if (SUCCEEDED(hrc))
7558 {
7559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7560 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7561 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7562 icon.detachTo(ComSafeArrayOutArg(aIcon));
7563 }
7564 return hrc;
7565}
7566
7567STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7568{
7569 CheckComArgSafeArrayNotNull(aIcon);
7570 AutoCaller autoCaller(this);
7571 HRESULT hrc = autoCaller.rc();
7572 if (SUCCEEDED(hrc))
7573 {
7574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7575 hrc = checkStateDependency(MutableOrSavedStateDep);
7576 if (SUCCEEDED(hrc))
7577 {
7578 setModified(IsModified_MachineData);
7579 mUserData.backup();
7580 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7581 mUserData->mIcon.resize(icon.size());
7582 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7583 }
7584 }
7585 return hrc;
7586}
7587
7588STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7589{
7590 CheckComArgOutPointerValid(aAvailable);
7591
7592 AutoCaller autoCaller(this);
7593 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7594
7595#ifdef VBOX_WITH_USB
7596 *aAvailable = true;
7597#else
7598 *aAvailable = false;
7599#endif
7600 return S_OK;
7601}
7602
7603STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7604{
7605 LogFlowFuncEnter();
7606
7607 CheckComArgNotNull(pTarget);
7608 CheckComArgOutPointerValid(pProgress);
7609
7610 /* Convert the options. */
7611 RTCList<CloneOptions_T> optList;
7612 if (options != NULL)
7613 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7614
7615 if (optList.contains(CloneOptions_Link))
7616 {
7617 if (!isSnapshotMachine())
7618 return setError(E_INVALIDARG,
7619 tr("Linked clone can only be created from a snapshot"));
7620 if (mode != CloneMode_MachineState)
7621 return setError(E_INVALIDARG,
7622 tr("Linked clone can only be created for a single machine state"));
7623 }
7624 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7625
7626 AutoCaller autoCaller(this);
7627 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7628
7629
7630 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7631
7632 HRESULT rc = pWorker->start(pProgress);
7633
7634 LogFlowFuncLeave();
7635
7636 return rc;
7637}
7638
7639// public methods for internal purposes
7640/////////////////////////////////////////////////////////////////////////////
7641
7642/**
7643 * Adds the given IsModified_* flag to the dirty flags of the machine.
7644 * This must be called either during loadSettings or under the machine write lock.
7645 * @param fl
7646 */
7647void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7648{
7649 mData->flModifications |= fl;
7650 if (fAllowStateModification && isStateModificationAllowed())
7651 mData->mCurrentStateModified = true;
7652}
7653
7654/**
7655 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7656 * care of the write locking.
7657 *
7658 * @param fModifications The flag to add.
7659 */
7660void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7661{
7662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7663 setModified(fModification, fAllowStateModification);
7664}
7665
7666/**
7667 * Saves the registry entry of this machine to the given configuration node.
7668 *
7669 * @param aEntryNode Node to save the registry entry to.
7670 *
7671 * @note locks this object for reading.
7672 */
7673HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7674{
7675 AutoLimitedCaller autoCaller(this);
7676 AssertComRCReturnRC(autoCaller.rc());
7677
7678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7679
7680 data.uuid = mData->mUuid;
7681 data.strSettingsFile = mData->m_strConfigFile;
7682
7683 return S_OK;
7684}
7685
7686/**
7687 * Calculates the absolute path of the given path taking the directory of the
7688 * machine settings file as the current directory.
7689 *
7690 * @param aPath Path to calculate the absolute path for.
7691 * @param aResult Where to put the result (used only on success, can be the
7692 * same Utf8Str instance as passed in @a aPath).
7693 * @return IPRT result.
7694 *
7695 * @note Locks this object for reading.
7696 */
7697int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7698{
7699 AutoCaller autoCaller(this);
7700 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7701
7702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7703
7704 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7705
7706 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7707
7708 strSettingsDir.stripFilename();
7709 char folder[RTPATH_MAX];
7710 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7711 if (RT_SUCCESS(vrc))
7712 aResult = folder;
7713
7714 return vrc;
7715}
7716
7717/**
7718 * Copies strSource to strTarget, making it relative to the machine folder
7719 * if it is a subdirectory thereof, or simply copying it otherwise.
7720 *
7721 * @param strSource Path to evaluate and copy.
7722 * @param strTarget Buffer to receive target path.
7723 *
7724 * @note Locks this object for reading.
7725 */
7726void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7727 Utf8Str &strTarget)
7728{
7729 AutoCaller autoCaller(this);
7730 AssertComRCReturn(autoCaller.rc(), (void)0);
7731
7732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7733
7734 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7735 // use strTarget as a temporary buffer to hold the machine settings dir
7736 strTarget = mData->m_strConfigFileFull;
7737 strTarget.stripFilename();
7738 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7739 {
7740 // is relative: then append what's left
7741 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7742 // for empty paths (only possible for subdirs) use "." to avoid
7743 // triggering default settings for not present config attributes.
7744 if (strTarget.isEmpty())
7745 strTarget = ".";
7746 }
7747 else
7748 // is not relative: then overwrite
7749 strTarget = strSource;
7750}
7751
7752/**
7753 * Returns the full path to the machine's log folder in the
7754 * \a aLogFolder argument.
7755 */
7756void Machine::getLogFolder(Utf8Str &aLogFolder)
7757{
7758 AutoCaller autoCaller(this);
7759 AssertComRCReturnVoid(autoCaller.rc());
7760
7761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7762
7763 char szTmp[RTPATH_MAX];
7764 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7765 if (RT_SUCCESS(vrc))
7766 {
7767 if (szTmp[0] && !mUserData.isNull())
7768 {
7769 char szTmp2[RTPATH_MAX];
7770 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7771 if (RT_SUCCESS(vrc))
7772 aLogFolder = BstrFmt("%s%c%s",
7773 szTmp2,
7774 RTPATH_DELIMITER,
7775 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7776 }
7777 else
7778 vrc = VERR_PATH_IS_RELATIVE;
7779 }
7780
7781 if (RT_FAILURE(vrc))
7782 {
7783 // fallback if VBOX_USER_LOGHOME is not set or invalid
7784 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7785 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7786 aLogFolder.append(RTPATH_DELIMITER);
7787 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7788 }
7789}
7790
7791/**
7792 * Returns the full path to the machine's log file for an given index.
7793 */
7794Utf8Str Machine::queryLogFilename(ULONG idx)
7795{
7796 Utf8Str logFolder;
7797 getLogFolder(logFolder);
7798 Assert(logFolder.length());
7799 Utf8Str log;
7800 if (idx == 0)
7801 log = Utf8StrFmt("%s%cVBox.log",
7802 logFolder.c_str(), RTPATH_DELIMITER);
7803 else
7804 log = Utf8StrFmt("%s%cVBox.log.%d",
7805 logFolder.c_str(), RTPATH_DELIMITER, idx);
7806 return log;
7807}
7808
7809/**
7810 * Composes a unique saved state filename based on the current system time. The filename is
7811 * granular to the second so this will work so long as no more than one snapshot is taken on
7812 * a machine per second.
7813 *
7814 * Before version 4.1, we used this formula for saved state files:
7815 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7816 * which no longer works because saved state files can now be shared between the saved state of the
7817 * "saved" machine and an online snapshot, and the following would cause problems:
7818 * 1) save machine
7819 * 2) create online snapshot from that machine state --> reusing saved state file
7820 * 3) save machine again --> filename would be reused, breaking the online snapshot
7821 *
7822 * So instead we now use a timestamp.
7823 *
7824 * @param str
7825 */
7826void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7827{
7828 AutoCaller autoCaller(this);
7829 AssertComRCReturnVoid(autoCaller.rc());
7830
7831 {
7832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7833 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7834 }
7835
7836 RTTIMESPEC ts;
7837 RTTimeNow(&ts);
7838 RTTIME time;
7839 RTTimeExplode(&time, &ts);
7840
7841 strStateFilePath += RTPATH_DELIMITER;
7842 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7843 time.i32Year, time.u8Month, time.u8MonthDay,
7844 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7845}
7846
7847/**
7848 * Returns the full path to the default video capture file.
7849 */
7850void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7851{
7852 AutoCaller autoCaller(this);
7853 AssertComRCReturnVoid(autoCaller.rc());
7854
7855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7856
7857 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7858 strFile.stripExt(); // path/to/machinesfolder/vmname/vmname
7859 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7860}
7861
7862/**
7863 * Returns whether at least one USB controller is present for the VM.
7864 */
7865bool Machine::isUSBControllerPresent()
7866{
7867 AutoCaller autoCaller(this);
7868 AssertComRCReturn(autoCaller.rc(), false);
7869
7870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7871
7872 return (mUSBControllers->size() > 0);
7873}
7874
7875/**
7876 * @note Locks this object for writing, calls the client process
7877 * (inside the lock).
7878 */
7879HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7880 const Utf8Str &strFrontend,
7881 const Utf8Str &strEnvironment,
7882 ProgressProxy *aProgress)
7883{
7884 LogFlowThisFuncEnter();
7885
7886 AssertReturn(aControl, E_FAIL);
7887 AssertReturn(aProgress, E_FAIL);
7888 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7889
7890 AutoCaller autoCaller(this);
7891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7892
7893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7894
7895 if (!mData->mRegistered)
7896 return setError(E_UNEXPECTED,
7897 tr("The machine '%s' is not registered"),
7898 mUserData->s.strName.c_str());
7899
7900 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7901
7902 if ( mData->mSession.mState == SessionState_Locked
7903 || mData->mSession.mState == SessionState_Spawning
7904 || mData->mSession.mState == SessionState_Unlocking)
7905 return setError(VBOX_E_INVALID_OBJECT_STATE,
7906 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7907 mUserData->s.strName.c_str());
7908
7909 /* may not be busy */
7910 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7911
7912 /* get the path to the executable */
7913 char szPath[RTPATH_MAX];
7914 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7915 size_t sz = strlen(szPath);
7916 szPath[sz++] = RTPATH_DELIMITER;
7917 szPath[sz] = 0;
7918 char *cmd = szPath + sz;
7919 sz = sizeof(szPath) - sz;
7920
7921 int vrc = VINF_SUCCESS;
7922 RTPROCESS pid = NIL_RTPROCESS;
7923
7924 RTENV env = RTENV_DEFAULT;
7925
7926 if (!strEnvironment.isEmpty())
7927 {
7928 char *newEnvStr = NULL;
7929
7930 do
7931 {
7932 /* clone the current environment */
7933 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7934 AssertRCBreakStmt(vrc2, vrc = vrc2);
7935
7936 newEnvStr = RTStrDup(strEnvironment.c_str());
7937 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7938
7939 /* put new variables to the environment
7940 * (ignore empty variable names here since RTEnv API
7941 * intentionally doesn't do that) */
7942 char *var = newEnvStr;
7943 for (char *p = newEnvStr; *p; ++p)
7944 {
7945 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7946 {
7947 *p = '\0';
7948 if (*var)
7949 {
7950 char *val = strchr(var, '=');
7951 if (val)
7952 {
7953 *val++ = '\0';
7954 vrc2 = RTEnvSetEx(env, var, val);
7955 }
7956 else
7957 vrc2 = RTEnvUnsetEx(env, var);
7958 if (RT_FAILURE(vrc2))
7959 break;
7960 }
7961 var = p + 1;
7962 }
7963 }
7964 if (RT_SUCCESS(vrc2) && *var)
7965 vrc2 = RTEnvPutEx(env, var);
7966
7967 AssertRCBreakStmt(vrc2, vrc = vrc2);
7968 }
7969 while (0);
7970
7971 if (newEnvStr != NULL)
7972 RTStrFree(newEnvStr);
7973 }
7974
7975#ifdef VBOX_WITH_QTGUI
7976 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7977 {
7978# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7979 /* Modify the base path so that we don't need to use ".." below. */
7980 RTPathStripTrailingSlash(szPath);
7981 RTPathStripFilename(szPath);
7982 sz = strlen(szPath);
7983 cmd = szPath + sz;
7984 sz = sizeof(szPath) - sz;
7985
7986#define OSX_APP_NAME "VirtualBoxVM"
7987#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7988
7989 Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7990 if ( strAppOverride.contains(".")
7991 || strAppOverride.contains("/")
7992 || strAppOverride.contains("\\")
7993 || strAppOverride.contains(":"))
7994 strAppOverride.setNull();
7995 Utf8Str strAppPath;
7996 if (!strAppOverride.isEmpty())
7997 {
7998 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7999 Utf8Str strFullPath(szPath);
8000 strFullPath.append(strAppPath);
8001 /* there is a race, but people using this deserve the failure */
8002 if (!RTFileExists(strFullPath.c_str()))
8003 strAppOverride.setNull();
8004 }
8005 if (strAppOverride.isEmpty())
8006 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
8007 const char *VirtualBox_exe = strAppPath.c_str();
8008 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
8009# else
8010 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8011 Assert(sz >= sizeof(VirtualBox_exe));
8012# endif
8013 strcpy(cmd, VirtualBox_exe);
8014
8015 Utf8Str idStr = mData->mUuid.toString();
8016 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8017 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8018 }
8019#else /* !VBOX_WITH_QTGUI */
8020 if (0)
8021 ;
8022#endif /* VBOX_WITH_QTGUI */
8023
8024 else
8025
8026#ifdef VBOX_WITH_VBOXSDL
8027 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8028 {
8029 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8030 Assert(sz >= sizeof(VBoxSDL_exe));
8031 strcpy(cmd, VBoxSDL_exe);
8032
8033 Utf8Str idStr = mData->mUuid.toString();
8034 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8035 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8036 }
8037#else /* !VBOX_WITH_VBOXSDL */
8038 if (0)
8039 ;
8040#endif /* !VBOX_WITH_VBOXSDL */
8041
8042 else
8043
8044#ifdef VBOX_WITH_HEADLESS
8045 if ( strFrontend == "headless"
8046 || strFrontend == "capture"
8047 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8048 )
8049 {
8050 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8051 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8052 * and a VM works even if the server has not been installed.
8053 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8054 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8055 * differently in 4.0 and 3.x.
8056 */
8057 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8058 Assert(sz >= sizeof(VBoxHeadless_exe));
8059 strcpy(cmd, VBoxHeadless_exe);
8060
8061 Utf8Str idStr = mData->mUuid.toString();
8062 /* Leave space for "--capture" arg. */
8063 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8064 "--startvm", idStr.c_str(),
8065 "--vrde", "config",
8066 0, /* For "--capture". */
8067 0 };
8068 if (strFrontend == "capture")
8069 {
8070 unsigned pos = RT_ELEMENTS(args) - 2;
8071 args[pos] = "--capture";
8072 }
8073 vrc = RTProcCreate(szPath, args, env,
8074#ifdef RT_OS_WINDOWS
8075 RTPROC_FLAGS_NO_WINDOW
8076#else
8077 0
8078#endif
8079 , &pid);
8080 }
8081#else /* !VBOX_WITH_HEADLESS */
8082 if (0)
8083 ;
8084#endif /* !VBOX_WITH_HEADLESS */
8085 else
8086 {
8087 RTEnvDestroy(env);
8088 return setError(E_INVALIDARG,
8089 tr("Invalid frontend name: '%s'"),
8090 strFrontend.c_str());
8091 }
8092
8093 RTEnvDestroy(env);
8094
8095 if (RT_FAILURE(vrc))
8096 return setError(VBOX_E_IPRT_ERROR,
8097 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8098 mUserData->s.strName.c_str(), vrc);
8099
8100 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8101
8102 /*
8103 * Note that we don't release the lock here before calling the client,
8104 * because it doesn't need to call us back if called with a NULL argument.
8105 * Releasing the lock here is dangerous because we didn't prepare the
8106 * launch data yet, but the client we've just started may happen to be
8107 * too fast and call LockMachine() that will fail (because of PID, etc.),
8108 * so that the Machine will never get out of the Spawning session state.
8109 */
8110
8111 /* inform the session that it will be a remote one */
8112 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8113#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8114 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8115#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8116 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8117#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8118 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8119
8120 if (FAILED(rc))
8121 {
8122 /* restore the session state */
8123 mData->mSession.mState = SessionState_Unlocked;
8124 /* The failure may occur w/o any error info (from RPC), so provide one */
8125 return setError(VBOX_E_VM_ERROR,
8126 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8127 }
8128
8129 /* attach launch data to the machine */
8130 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8131 mData->mSession.mRemoteControls.push_back(aControl);
8132 mData->mSession.mProgress = aProgress;
8133 mData->mSession.mPID = pid;
8134 mData->mSession.mState = SessionState_Spawning;
8135 mData->mSession.mType = strFrontend;
8136
8137 LogFlowThisFuncLeave();
8138 return S_OK;
8139}
8140
8141/**
8142 * Returns @c true if the given session machine instance has an open direct
8143 * session (and optionally also for direct sessions which are closing) and
8144 * returns the session control machine instance if so.
8145 *
8146 * Note that when the method returns @c false, the arguments remain unchanged.
8147 *
8148 * @param aMachine Session machine object.
8149 * @param aControl Direct session control object (optional).
8150 * @param aAllowClosing If true then additionally a session which is currently
8151 * being closed will also be allowed.
8152 *
8153 * @note locks this object for reading.
8154 */
8155bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8156 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8157 bool aAllowClosing /*= false*/)
8158{
8159 AutoLimitedCaller autoCaller(this);
8160 AssertComRCReturn(autoCaller.rc(), false);
8161
8162 /* just return false for inaccessible machines */
8163 if (autoCaller.state() != Ready)
8164 return false;
8165
8166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8167
8168 if ( mData->mSession.mState == SessionState_Locked
8169 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8170 )
8171 {
8172 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8173
8174 aMachine = mData->mSession.mMachine;
8175
8176 if (aControl != NULL)
8177 *aControl = mData->mSession.mDirectControl;
8178
8179 return true;
8180 }
8181
8182 return false;
8183}
8184
8185/**
8186 * Returns @c true if the given machine has an spawning direct session.
8187 *
8188 * @note locks this object for reading.
8189 */
8190bool Machine::isSessionSpawning()
8191{
8192 AutoLimitedCaller autoCaller(this);
8193 AssertComRCReturn(autoCaller.rc(), false);
8194
8195 /* just return false for inaccessible machines */
8196 if (autoCaller.state() != Ready)
8197 return false;
8198
8199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8200
8201 if (mData->mSession.mState == SessionState_Spawning)
8202 return true;
8203
8204 return false;
8205}
8206
8207#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8208/**
8209 * Called from the client watcher thread to check for unexpected client process
8210 * death during Session_Spawning state (e.g. before it successfully opened a
8211 * direct session).
8212 *
8213 * On Win32 and on OS/2, this method is called only when we've got the
8214 * direct client's process termination notification, so it always returns @c
8215 * true.
8216 *
8217 * On other platforms, this method returns @c true if the client process is
8218 * terminated and @c false if it's still alive.
8219 *
8220 * @note Locks this object for writing.
8221 */
8222bool Machine::checkForSpawnFailure()
8223{
8224 AutoCaller autoCaller(this);
8225 if (!autoCaller.isOk())
8226 {
8227 /* nothing to do */
8228 LogFlowThisFunc(("Already uninitialized!\n"));
8229 return true;
8230 }
8231
8232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8233
8234 if (mData->mSession.mState != SessionState_Spawning)
8235 {
8236 /* nothing to do */
8237 LogFlowThisFunc(("Not spawning any more!\n"));
8238 return true;
8239 }
8240
8241 HRESULT rc = S_OK;
8242
8243 /* PID not yet initialized, skip check. */
8244 if (mData->mSession.mPID == NIL_RTPROCESS)
8245 return false;
8246
8247 RTPROCSTATUS status;
8248 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8249
8250 if (vrc != VERR_PROCESS_RUNNING)
8251 {
8252 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8253 rc = setError(E_FAIL,
8254 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8255 getName().c_str(), status.iStatus);
8256 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8257 rc = setError(E_FAIL,
8258 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8259 getName().c_str(), status.iStatus);
8260 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8261 rc = setError(E_FAIL,
8262 tr("The virtual machine '%s' has terminated abnormally"),
8263 getName().c_str(), status.iStatus);
8264 else
8265 rc = setError(E_FAIL,
8266 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8267 getName().c_str(), vrc);
8268 }
8269
8270 if (FAILED(rc))
8271 {
8272 /* Close the remote session, remove the remote control from the list
8273 * and reset session state to Closed (@note keep the code in sync with
8274 * the relevant part in LockMachine()). */
8275
8276 Assert(mData->mSession.mRemoteControls.size() == 1);
8277 if (mData->mSession.mRemoteControls.size() == 1)
8278 {
8279 ErrorInfoKeeper eik;
8280 mData->mSession.mRemoteControls.front()->Uninitialize();
8281 }
8282
8283 mData->mSession.mRemoteControls.clear();
8284 mData->mSession.mState = SessionState_Unlocked;
8285
8286 /* finalize the progress after setting the state */
8287 if (!mData->mSession.mProgress.isNull())
8288 {
8289 mData->mSession.mProgress->notifyComplete(rc);
8290 mData->mSession.mProgress.setNull();
8291 }
8292
8293 mData->mSession.mPID = NIL_RTPROCESS;
8294
8295 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8296 return true;
8297 }
8298
8299 return false;
8300}
8301#endif /* !VBOX_WITH_GENERIC_SESSION_WATCHER */
8302
8303/**
8304 * Checks whether the machine can be registered. If so, commits and saves
8305 * all settings.
8306 *
8307 * @note Must be called from mParent's write lock. Locks this object and
8308 * children for writing.
8309 */
8310HRESULT Machine::prepareRegister()
8311{
8312 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8313
8314 AutoLimitedCaller autoCaller(this);
8315 AssertComRCReturnRC(autoCaller.rc());
8316
8317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8318
8319 /* wait for state dependents to drop to zero */
8320 ensureNoStateDependencies();
8321
8322 if (!mData->mAccessible)
8323 return setError(VBOX_E_INVALID_OBJECT_STATE,
8324 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8325 mUserData->s.strName.c_str(),
8326 mData->mUuid.toString().c_str());
8327
8328 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8329
8330 if (mData->mRegistered)
8331 return setError(VBOX_E_INVALID_OBJECT_STATE,
8332 tr("The machine '%s' with UUID {%s} is already registered"),
8333 mUserData->s.strName.c_str(),
8334 mData->mUuid.toString().c_str());
8335
8336 HRESULT rc = S_OK;
8337
8338 // Ensure the settings are saved. If we are going to be registered and
8339 // no config file exists yet, create it by calling saveSettings() too.
8340 if ( (mData->flModifications)
8341 || (!mData->pMachineConfigFile->fileExists())
8342 )
8343 {
8344 rc = saveSettings(NULL);
8345 // no need to check whether VirtualBox.xml needs saving too since
8346 // we can't have a machine XML file rename pending
8347 if (FAILED(rc)) return rc;
8348 }
8349
8350 /* more config checking goes here */
8351
8352 if (SUCCEEDED(rc))
8353 {
8354 /* we may have had implicit modifications we want to fix on success */
8355 commit();
8356
8357 mData->mRegistered = true;
8358 }
8359 else
8360 {
8361 /* we may have had implicit modifications we want to cancel on failure*/
8362 rollback(false /* aNotify */);
8363 }
8364
8365 return rc;
8366}
8367
8368/**
8369 * Increases the number of objects dependent on the machine state or on the
8370 * registered state. Guarantees that these two states will not change at least
8371 * until #releaseStateDependency() is called.
8372 *
8373 * Depending on the @a aDepType value, additional state checks may be made.
8374 * These checks will set extended error info on failure. See
8375 * #checkStateDependency() for more info.
8376 *
8377 * If this method returns a failure, the dependency is not added and the caller
8378 * is not allowed to rely on any particular machine state or registration state
8379 * value and may return the failed result code to the upper level.
8380 *
8381 * @param aDepType Dependency type to add.
8382 * @param aState Current machine state (NULL if not interested).
8383 * @param aRegistered Current registered state (NULL if not interested).
8384 *
8385 * @note Locks this object for writing.
8386 */
8387HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8388 MachineState_T *aState /* = NULL */,
8389 BOOL *aRegistered /* = NULL */)
8390{
8391 AutoCaller autoCaller(this);
8392 AssertComRCReturnRC(autoCaller.rc());
8393
8394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8395
8396 HRESULT rc = checkStateDependency(aDepType);
8397 if (FAILED(rc)) return rc;
8398
8399 {
8400 if (mData->mMachineStateChangePending != 0)
8401 {
8402 /* ensureNoStateDependencies() is waiting for state dependencies to
8403 * drop to zero so don't add more. It may make sense to wait a bit
8404 * and retry before reporting an error (since the pending state
8405 * transition should be really quick) but let's just assert for
8406 * now to see if it ever happens on practice. */
8407
8408 AssertFailed();
8409
8410 return setError(E_ACCESSDENIED,
8411 tr("Machine state change is in progress. Please retry the operation later."));
8412 }
8413
8414 ++mData->mMachineStateDeps;
8415 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8416 }
8417
8418 if (aState)
8419 *aState = mData->mMachineState;
8420 if (aRegistered)
8421 *aRegistered = mData->mRegistered;
8422
8423 return S_OK;
8424}
8425
8426/**
8427 * Decreases the number of objects dependent on the machine state.
8428 * Must always complete the #addStateDependency() call after the state
8429 * dependency is no more necessary.
8430 */
8431void Machine::releaseStateDependency()
8432{
8433 AutoCaller autoCaller(this);
8434 AssertComRCReturnVoid(autoCaller.rc());
8435
8436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8437
8438 /* releaseStateDependency() w/o addStateDependency()? */
8439 AssertReturnVoid(mData->mMachineStateDeps != 0);
8440 -- mData->mMachineStateDeps;
8441
8442 if (mData->mMachineStateDeps == 0)
8443 {
8444 /* inform ensureNoStateDependencies() that there are no more deps */
8445 if (mData->mMachineStateChangePending != 0)
8446 {
8447 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8448 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8449 }
8450 }
8451}
8452
8453Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8454{
8455 /* start with nothing found */
8456 Utf8Str strResult("");
8457
8458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8459
8460 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8461 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8462 // found:
8463 strResult = it->second; // source is a Utf8Str
8464
8465 return strResult;
8466}
8467
8468// protected methods
8469/////////////////////////////////////////////////////////////////////////////
8470
8471/**
8472 * Performs machine state checks based on the @a aDepType value. If a check
8473 * fails, this method will set extended error info, otherwise it will return
8474 * S_OK. It is supposed, that on failure, the caller will immediately return
8475 * the return value of this method to the upper level.
8476 *
8477 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8478 *
8479 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8480 * current state of this machine object allows to change settings of the
8481 * machine (i.e. the machine is not registered, or registered but not running
8482 * and not saved). It is useful to call this method from Machine setters
8483 * before performing any change.
8484 *
8485 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8486 * as for MutableStateDep except that if the machine is saved, S_OK is also
8487 * returned. This is useful in setters which allow changing machine
8488 * properties when it is in the saved state.
8489 *
8490 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8491 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8492 * Aborted).
8493 *
8494 * @param aDepType Dependency type to check.
8495 *
8496 * @note Non Machine based classes should use #addStateDependency() and
8497 * #releaseStateDependency() methods or the smart AutoStateDependency
8498 * template.
8499 *
8500 * @note This method must be called from under this object's read or write
8501 * lock.
8502 */
8503HRESULT Machine::checkStateDependency(StateDependency aDepType)
8504{
8505 switch (aDepType)
8506 {
8507 case AnyStateDep:
8508 {
8509 break;
8510 }
8511 case MutableStateDep:
8512 {
8513 if ( mData->mRegistered
8514 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8515 || ( mData->mMachineState != MachineState_Paused
8516 && mData->mMachineState != MachineState_Running
8517 && mData->mMachineState != MachineState_Aborted
8518 && mData->mMachineState != MachineState_Teleported
8519 && mData->mMachineState != MachineState_PoweredOff
8520 )
8521 )
8522 )
8523 return setError(VBOX_E_INVALID_VM_STATE,
8524 tr("The machine is not mutable (state is %s)"),
8525 Global::stringifyMachineState(mData->mMachineState));
8526 break;
8527 }
8528 case MutableOrSavedStateDep:
8529 {
8530 if ( mData->mRegistered
8531 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8532 || ( mData->mMachineState != MachineState_Paused
8533 && mData->mMachineState != MachineState_Running
8534 && mData->mMachineState != MachineState_Aborted
8535 && mData->mMachineState != MachineState_Teleported
8536 && mData->mMachineState != MachineState_Saved
8537 && mData->mMachineState != MachineState_PoweredOff
8538 )
8539 )
8540 )
8541 return setError(VBOX_E_INVALID_VM_STATE,
8542 tr("The machine is not mutable (state is %s)"),
8543 Global::stringifyMachineState(mData->mMachineState));
8544 break;
8545 }
8546 case OfflineStateDep:
8547 {
8548 if ( mData->mRegistered
8549 && ( !isSessionMachine()
8550 || ( mData->mMachineState != MachineState_PoweredOff
8551 && mData->mMachineState != MachineState_Saved
8552 && mData->mMachineState != MachineState_Aborted
8553 && mData->mMachineState != MachineState_Teleported
8554 )
8555 )
8556 )
8557 return setError(VBOX_E_INVALID_VM_STATE,
8558 tr("The machine is not offline (state is %s)"),
8559 Global::stringifyMachineState(mData->mMachineState));
8560 break;
8561 }
8562 }
8563
8564 return S_OK;
8565}
8566
8567/**
8568 * Helper to initialize all associated child objects and allocate data
8569 * structures.
8570 *
8571 * This method must be called as a part of the object's initialization procedure
8572 * (usually done in the #init() method).
8573 *
8574 * @note Must be called only from #init() or from #registeredInit().
8575 */
8576HRESULT Machine::initDataAndChildObjects()
8577{
8578 AutoCaller autoCaller(this);
8579 AssertComRCReturnRC(autoCaller.rc());
8580 AssertComRCReturn(autoCaller.state() == InInit ||
8581 autoCaller.state() == Limited, E_FAIL);
8582
8583 AssertReturn(!mData->mAccessible, E_FAIL);
8584
8585 /* allocate data structures */
8586 mSSData.allocate();
8587 mUserData.allocate();
8588 mHWData.allocate();
8589 mMediaData.allocate();
8590 mStorageControllers.allocate();
8591 mUSBControllers.allocate();
8592
8593 /* initialize mOSTypeId */
8594 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8595
8596 /* create associated BIOS settings object */
8597 unconst(mBIOSSettings).createObject();
8598 mBIOSSettings->init(this);
8599
8600 /* create an associated VRDE object (default is disabled) */
8601 unconst(mVRDEServer).createObject();
8602 mVRDEServer->init(this);
8603
8604 /* create associated serial port objects */
8605 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8606 {
8607 unconst(mSerialPorts[slot]).createObject();
8608 mSerialPorts[slot]->init(this, slot);
8609 }
8610
8611 /* create associated parallel port objects */
8612 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8613 {
8614 unconst(mParallelPorts[slot]).createObject();
8615 mParallelPorts[slot]->init(this, slot);
8616 }
8617
8618 /* create the audio adapter object (always present, default is disabled) */
8619 unconst(mAudioAdapter).createObject();
8620 mAudioAdapter->init(this);
8621
8622 /* create the USB device filters object (always present) */
8623 unconst(mUSBDeviceFilters).createObject();
8624 mUSBDeviceFilters->init(this);
8625
8626 /* create associated network adapter objects */
8627 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8628 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8629 {
8630 unconst(mNetworkAdapters[slot]).createObject();
8631 mNetworkAdapters[slot]->init(this, slot);
8632 }
8633
8634 /* create the bandwidth control */
8635 unconst(mBandwidthControl).createObject();
8636 mBandwidthControl->init(this);
8637
8638 return S_OK;
8639}
8640
8641/**
8642 * Helper to uninitialize all associated child objects and to free all data
8643 * structures.
8644 *
8645 * This method must be called as a part of the object's uninitialization
8646 * procedure (usually done in the #uninit() method).
8647 *
8648 * @note Must be called only from #uninit() or from #registeredInit().
8649 */
8650void Machine::uninitDataAndChildObjects()
8651{
8652 AutoCaller autoCaller(this);
8653 AssertComRCReturnVoid(autoCaller.rc());
8654 AssertComRCReturnVoid( autoCaller.state() == InUninit
8655 || autoCaller.state() == Limited);
8656
8657 /* tell all our other child objects we've been uninitialized */
8658 if (mBandwidthControl)
8659 {
8660 mBandwidthControl->uninit();
8661 unconst(mBandwidthControl).setNull();
8662 }
8663
8664 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8665 {
8666 if (mNetworkAdapters[slot])
8667 {
8668 mNetworkAdapters[slot]->uninit();
8669 unconst(mNetworkAdapters[slot]).setNull();
8670 }
8671 }
8672
8673 if (mUSBDeviceFilters)
8674 {
8675 mUSBDeviceFilters->uninit();
8676 unconst(mUSBDeviceFilters).setNull();
8677 }
8678
8679 if (mAudioAdapter)
8680 {
8681 mAudioAdapter->uninit();
8682 unconst(mAudioAdapter).setNull();
8683 }
8684
8685 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8686 {
8687 if (mParallelPorts[slot])
8688 {
8689 mParallelPorts[slot]->uninit();
8690 unconst(mParallelPorts[slot]).setNull();
8691 }
8692 }
8693
8694 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8695 {
8696 if (mSerialPorts[slot])
8697 {
8698 mSerialPorts[slot]->uninit();
8699 unconst(mSerialPorts[slot]).setNull();
8700 }
8701 }
8702
8703 if (mVRDEServer)
8704 {
8705 mVRDEServer->uninit();
8706 unconst(mVRDEServer).setNull();
8707 }
8708
8709 if (mBIOSSettings)
8710 {
8711 mBIOSSettings->uninit();
8712 unconst(mBIOSSettings).setNull();
8713 }
8714
8715 /* Deassociate media (only when a real Machine or a SnapshotMachine
8716 * instance is uninitialized; SessionMachine instances refer to real
8717 * Machine media). This is necessary for a clean re-initialization of
8718 * the VM after successfully re-checking the accessibility state. Note
8719 * that in case of normal Machine or SnapshotMachine uninitialization (as
8720 * a result of unregistering or deleting the snapshot), outdated media
8721 * attachments will already be uninitialized and deleted, so this
8722 * code will not affect them. */
8723 if ( !!mMediaData
8724 && (!isSessionMachine())
8725 )
8726 {
8727 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8728 it != mMediaData->mAttachments.end();
8729 ++it)
8730 {
8731 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8732 if (pMedium.isNull())
8733 continue;
8734 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8735 AssertComRC(rc);
8736 }
8737 }
8738
8739 if (!isSessionMachine() && !isSnapshotMachine())
8740 {
8741 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8742 if (mData->mFirstSnapshot)
8743 {
8744 // snapshots tree is protected by machine write lock; strictly
8745 // this isn't necessary here since we're deleting the entire
8746 // machine, but otherwise we assert in Snapshot::uninit()
8747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8748 mData->mFirstSnapshot->uninit();
8749 mData->mFirstSnapshot.setNull();
8750 }
8751
8752 mData->mCurrentSnapshot.setNull();
8753 }
8754
8755 /* free data structures (the essential mData structure is not freed here
8756 * since it may be still in use) */
8757 mMediaData.free();
8758 mStorageControllers.free();
8759 mUSBControllers.free();
8760 mHWData.free();
8761 mUserData.free();
8762 mSSData.free();
8763}
8764
8765/**
8766 * Returns a pointer to the Machine object for this machine that acts like a
8767 * parent for complex machine data objects such as shared folders, etc.
8768 *
8769 * For primary Machine objects and for SnapshotMachine objects, returns this
8770 * object's pointer itself. For SessionMachine objects, returns the peer
8771 * (primary) machine pointer.
8772 */
8773Machine* Machine::getMachine()
8774{
8775 if (isSessionMachine())
8776 return (Machine*)mPeer;
8777 return this;
8778}
8779
8780/**
8781 * Makes sure that there are no machine state dependents. If necessary, waits
8782 * for the number of dependents to drop to zero.
8783 *
8784 * Make sure this method is called from under this object's write lock to
8785 * guarantee that no new dependents may be added when this method returns
8786 * control to the caller.
8787 *
8788 * @note Locks this object for writing. The lock will be released while waiting
8789 * (if necessary).
8790 *
8791 * @warning To be used only in methods that change the machine state!
8792 */
8793void Machine::ensureNoStateDependencies()
8794{
8795 AssertReturnVoid(isWriteLockOnCurrentThread());
8796
8797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8798
8799 /* Wait for all state dependents if necessary */
8800 if (mData->mMachineStateDeps != 0)
8801 {
8802 /* lazy semaphore creation */
8803 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8804 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8805
8806 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8807 mData->mMachineStateDeps));
8808
8809 ++mData->mMachineStateChangePending;
8810
8811 /* reset the semaphore before waiting, the last dependent will signal
8812 * it */
8813 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8814
8815 alock.release();
8816
8817 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8818
8819 alock.acquire();
8820
8821 -- mData->mMachineStateChangePending;
8822 }
8823}
8824
8825/**
8826 * Changes the machine state and informs callbacks.
8827 *
8828 * This method is not intended to fail so it either returns S_OK or asserts (and
8829 * returns a failure).
8830 *
8831 * @note Locks this object for writing.
8832 */
8833HRESULT Machine::setMachineState(MachineState_T aMachineState)
8834{
8835 LogFlowThisFuncEnter();
8836 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8837
8838 AutoCaller autoCaller(this);
8839 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8840
8841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8842
8843 /* wait for state dependents to drop to zero */
8844 ensureNoStateDependencies();
8845
8846 if (mData->mMachineState != aMachineState)
8847 {
8848 mData->mMachineState = aMachineState;
8849
8850 RTTimeNow(&mData->mLastStateChange);
8851
8852 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8853 }
8854
8855 LogFlowThisFuncLeave();
8856 return S_OK;
8857}
8858
8859/**
8860 * Searches for a shared folder with the given logical name
8861 * in the collection of shared folders.
8862 *
8863 * @param aName logical name of the shared folder
8864 * @param aSharedFolder where to return the found object
8865 * @param aSetError whether to set the error info if the folder is
8866 * not found
8867 * @return
8868 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8869 *
8870 * @note
8871 * must be called from under the object's lock!
8872 */
8873HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8874 ComObjPtr<SharedFolder> &aSharedFolder,
8875 bool aSetError /* = false */)
8876{
8877 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8878 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8879 it != mHWData->mSharedFolders.end();
8880 ++it)
8881 {
8882 SharedFolder *pSF = *it;
8883 AutoCaller autoCaller(pSF);
8884 if (pSF->getName() == aName)
8885 {
8886 aSharedFolder = pSF;
8887 rc = S_OK;
8888 break;
8889 }
8890 }
8891
8892 if (aSetError && FAILED(rc))
8893 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8894
8895 return rc;
8896}
8897
8898/**
8899 * Initializes all machine instance data from the given settings structures
8900 * from XML. The exception is the machine UUID which needs special handling
8901 * depending on the caller's use case, so the caller needs to set that herself.
8902 *
8903 * This gets called in several contexts during machine initialization:
8904 *
8905 * -- When machine XML exists on disk already and needs to be loaded into memory,
8906 * for example, from registeredInit() to load all registered machines on
8907 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8908 * attached to the machine should be part of some media registry already.
8909 *
8910 * -- During OVF import, when a machine config has been constructed from an
8911 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8912 * ensure that the media listed as attachments in the config (which have
8913 * been imported from the OVF) receive the correct registry ID.
8914 *
8915 * -- During VM cloning.
8916 *
8917 * @param config Machine settings from XML.
8918 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8919 * @return
8920 */
8921HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8922 const Guid *puuidRegistry)
8923{
8924 // copy name, description, OS type, teleporter, UTC etc.
8925 mUserData->s = config.machineUserData;
8926
8927 // Decode the Icon overide data from config userdata and set onto Machine.
8928 #define DECODE_STR_MAX _1M
8929 const char* pszStr = config.machineUserData.ovIcon.c_str();
8930 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8931 if (cbOut > DECODE_STR_MAX)
8932 return setError(E_FAIL,
8933 tr("Icon Data too long.'%d' > '%d'"),
8934 cbOut,
8935 DECODE_STR_MAX);
8936 com::SafeArray<BYTE> iconByte(cbOut);
8937 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8938 if (FAILED(rc))
8939 return setError(E_FAIL,
8940 tr("Failure to Decode Icon Data. '%s' (%d)"),
8941 pszStr,
8942 rc);
8943 mUserData->mIcon.resize(iconByte.size());
8944 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8945
8946 // look up the object by Id to check it is valid
8947 ComPtr<IGuestOSType> guestOSType;
8948 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8949 guestOSType.asOutParam());
8950 if (FAILED(rc)) return rc;
8951
8952 // stateFile (optional)
8953 if (config.strStateFile.isEmpty())
8954 mSSData->strStateFilePath.setNull();
8955 else
8956 {
8957 Utf8Str stateFilePathFull(config.strStateFile);
8958 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8959 if (RT_FAILURE(vrc))
8960 return setError(E_FAIL,
8961 tr("Invalid saved state file path '%s' (%Rrc)"),
8962 config.strStateFile.c_str(),
8963 vrc);
8964 mSSData->strStateFilePath = stateFilePathFull;
8965 }
8966
8967 // snapshot folder needs special processing so set it again
8968 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8969 if (FAILED(rc)) return rc;
8970
8971 /* Copy the extra data items (Not in any case config is already the same as
8972 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8973 * make sure the extra data map is copied). */
8974 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8975
8976 /* currentStateModified (optional, default is true) */
8977 mData->mCurrentStateModified = config.fCurrentStateModified;
8978
8979 mData->mLastStateChange = config.timeLastStateChange;
8980
8981 /*
8982 * note: all mUserData members must be assigned prior this point because
8983 * we need to commit changes in order to let mUserData be shared by all
8984 * snapshot machine instances.
8985 */
8986 mUserData.commitCopy();
8987
8988 // machine registry, if present (must be loaded before snapshots)
8989 if (config.canHaveOwnMediaRegistry())
8990 {
8991 // determine machine folder
8992 Utf8Str strMachineFolder = getSettingsFileFull();
8993 strMachineFolder.stripFilename();
8994 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8995 config.mediaRegistry,
8996 strMachineFolder);
8997 if (FAILED(rc)) return rc;
8998 }
8999
9000 /* Snapshot node (optional) */
9001 size_t cRootSnapshots;
9002 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9003 {
9004 // there must be only one root snapshot
9005 Assert(cRootSnapshots == 1);
9006
9007 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9008
9009 rc = loadSnapshot(snap,
9010 config.uuidCurrentSnapshot,
9011 NULL); // no parent == first snapshot
9012 if (FAILED(rc)) return rc;
9013 }
9014
9015 // hardware data
9016 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9017 if (FAILED(rc)) return rc;
9018
9019 // load storage controllers
9020 rc = loadStorageControllers(config.storageMachine,
9021 puuidRegistry,
9022 NULL /* puuidSnapshot */);
9023 if (FAILED(rc)) return rc;
9024
9025 /*
9026 * NOTE: the assignment below must be the last thing to do,
9027 * otherwise it will be not possible to change the settings
9028 * somewhere in the code above because all setters will be
9029 * blocked by checkStateDependency(MutableStateDep).
9030 */
9031
9032 /* set the machine state to Aborted or Saved when appropriate */
9033 if (config.fAborted)
9034 {
9035 mSSData->strStateFilePath.setNull();
9036
9037 /* no need to use setMachineState() during init() */
9038 mData->mMachineState = MachineState_Aborted;
9039 }
9040 else if (!mSSData->strStateFilePath.isEmpty())
9041 {
9042 /* no need to use setMachineState() during init() */
9043 mData->mMachineState = MachineState_Saved;
9044 }
9045
9046 // after loading settings, we are no longer different from the XML on disk
9047 mData->flModifications = 0;
9048
9049 return S_OK;
9050}
9051
9052/**
9053 * Recursively loads all snapshots starting from the given.
9054 *
9055 * @param aNode <Snapshot> node.
9056 * @param aCurSnapshotId Current snapshot ID from the settings file.
9057 * @param aParentSnapshot Parent snapshot.
9058 */
9059HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9060 const Guid &aCurSnapshotId,
9061 Snapshot *aParentSnapshot)
9062{
9063 AssertReturn(!isSnapshotMachine(), E_FAIL);
9064 AssertReturn(!isSessionMachine(), E_FAIL);
9065
9066 HRESULT rc = S_OK;
9067
9068 Utf8Str strStateFile;
9069 if (!data.strStateFile.isEmpty())
9070 {
9071 /* optional */
9072 strStateFile = data.strStateFile;
9073 int vrc = calculateFullPath(strStateFile, strStateFile);
9074 if (RT_FAILURE(vrc))
9075 return setError(E_FAIL,
9076 tr("Invalid saved state file path '%s' (%Rrc)"),
9077 strStateFile.c_str(),
9078 vrc);
9079 }
9080
9081 /* create a snapshot machine object */
9082 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9083 pSnapshotMachine.createObject();
9084 rc = pSnapshotMachine->initFromSettings(this,
9085 data.hardware,
9086 &data.debugging,
9087 &data.autostart,
9088 data.storage,
9089 data.uuid.ref(),
9090 strStateFile);
9091 if (FAILED(rc)) return rc;
9092
9093 /* create a snapshot object */
9094 ComObjPtr<Snapshot> pSnapshot;
9095 pSnapshot.createObject();
9096 /* initialize the snapshot */
9097 rc = pSnapshot->init(mParent, // VirtualBox object
9098 data.uuid,
9099 data.strName,
9100 data.strDescription,
9101 data.timestamp,
9102 pSnapshotMachine,
9103 aParentSnapshot);
9104 if (FAILED(rc)) return rc;
9105
9106 /* memorize the first snapshot if necessary */
9107 if (!mData->mFirstSnapshot)
9108 mData->mFirstSnapshot = pSnapshot;
9109
9110 /* memorize the current snapshot when appropriate */
9111 if ( !mData->mCurrentSnapshot
9112 && pSnapshot->getId() == aCurSnapshotId
9113 )
9114 mData->mCurrentSnapshot = pSnapshot;
9115
9116 // now create the children
9117 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9118 it != data.llChildSnapshots.end();
9119 ++it)
9120 {
9121 const settings::Snapshot &childData = *it;
9122 // recurse
9123 rc = loadSnapshot(childData,
9124 aCurSnapshotId,
9125 pSnapshot); // parent = the one we created above
9126 if (FAILED(rc)) return rc;
9127 }
9128
9129 return rc;
9130}
9131
9132/**
9133 * Loads settings into mHWData.
9134 *
9135 * @param data Reference to the hardware settings.
9136 * @param pDbg Pointer to the debugging settings.
9137 * @param pAutostart Pointer to the autostart settings.
9138 */
9139HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9140 const settings::Autostart *pAutostart)
9141{
9142 AssertReturn(!isSessionMachine(), E_FAIL);
9143
9144 HRESULT rc = S_OK;
9145
9146 try
9147 {
9148 /* The hardware version attribute (optional). */
9149 mHWData->mHWVersion = data.strVersion;
9150 mHWData->mHardwareUUID = data.uuid;
9151
9152 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9153 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9154 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9155 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9156 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9157 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9158 mHWData->mPAEEnabled = data.fPAE;
9159 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9160 mHWData->mLongMode = data.enmLongMode;
9161 mHWData->mCPUCount = data.cCPUs;
9162 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9163 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9164
9165 // cpu
9166 if (mHWData->mCPUHotPlugEnabled)
9167 {
9168 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9169 it != data.llCpus.end();
9170 ++it)
9171 {
9172 const settings::Cpu &cpu = *it;
9173
9174 mHWData->mCPUAttached[cpu.ulId] = true;
9175 }
9176 }
9177
9178 // cpuid leafs
9179 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9180 it != data.llCpuIdLeafs.end();
9181 ++it)
9182 {
9183 const settings::CpuIdLeaf &leaf = *it;
9184
9185 switch (leaf.ulId)
9186 {
9187 case 0x0:
9188 case 0x1:
9189 case 0x2:
9190 case 0x3:
9191 case 0x4:
9192 case 0x5:
9193 case 0x6:
9194 case 0x7:
9195 case 0x8:
9196 case 0x9:
9197 case 0xA:
9198 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9199 break;
9200
9201 case 0x80000000:
9202 case 0x80000001:
9203 case 0x80000002:
9204 case 0x80000003:
9205 case 0x80000004:
9206 case 0x80000005:
9207 case 0x80000006:
9208 case 0x80000007:
9209 case 0x80000008:
9210 case 0x80000009:
9211 case 0x8000000A:
9212 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9213 break;
9214
9215 default:
9216 /* just ignore */
9217 break;
9218 }
9219 }
9220
9221 mHWData->mMemorySize = data.ulMemorySizeMB;
9222 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9223
9224 // boot order
9225 for (size_t i = 0;
9226 i < RT_ELEMENTS(mHWData->mBootOrder);
9227 i++)
9228 {
9229 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9230 if (it == data.mapBootOrder.end())
9231 mHWData->mBootOrder[i] = DeviceType_Null;
9232 else
9233 mHWData->mBootOrder[i] = it->second;
9234 }
9235
9236 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9237 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9238 mHWData->mMonitorCount = data.cMonitors;
9239 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9240 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9241 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9242 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9243 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9244 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9245 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9246 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9247 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9248 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9249 if (!data.strVideoCaptureFile.isEmpty())
9250 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9251 else
9252 mHWData->mVideoCaptureFile.setNull();
9253 mHWData->mFirmwareType = data.firmwareType;
9254 mHWData->mPointingHIDType = data.pointingHIDType;
9255 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9256 mHWData->mChipsetType = data.chipsetType;
9257 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9258 mHWData->mHPETEnabled = data.fHPETEnabled;
9259
9260 /* VRDEServer */
9261 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9262 if (FAILED(rc)) return rc;
9263
9264 /* BIOS */
9265 rc = mBIOSSettings->loadSettings(data.biosSettings);
9266 if (FAILED(rc)) return rc;
9267
9268 // Bandwidth control (must come before network adapters)
9269 rc = mBandwidthControl->loadSettings(data.ioSettings);
9270 if (FAILED(rc)) return rc;
9271
9272 /* Shared folders */
9273 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9274 it != data.usbSettings.llUSBControllers.end();
9275 ++it)
9276 {
9277 const settings::USBController &settingsCtrl = *it;
9278 ComObjPtr<USBController> newCtrl;
9279
9280 newCtrl.createObject();
9281 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9282 mUSBControllers->push_back(newCtrl);
9283 }
9284
9285 /* USB device filters */
9286 rc = mUSBDeviceFilters->loadSettings(data.usbSettings);
9287 if (FAILED(rc)) return rc;
9288
9289 // network adapters
9290 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9291 uint32_t oldCount = mNetworkAdapters.size();
9292 if (newCount > oldCount)
9293 {
9294 mNetworkAdapters.resize(newCount);
9295 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9296 {
9297 unconst(mNetworkAdapters[slot]).createObject();
9298 mNetworkAdapters[slot]->init(this, slot);
9299 }
9300 }
9301 else if (newCount < oldCount)
9302 mNetworkAdapters.resize(newCount);
9303 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9304 it != data.llNetworkAdapters.end();
9305 ++it)
9306 {
9307 const settings::NetworkAdapter &nic = *it;
9308
9309 /* slot unicity is guaranteed by XML Schema */
9310 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9311 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9312 if (FAILED(rc)) return rc;
9313 }
9314
9315 // serial ports
9316 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9317 it != data.llSerialPorts.end();
9318 ++it)
9319 {
9320 const settings::SerialPort &s = *it;
9321
9322 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9323 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9324 if (FAILED(rc)) return rc;
9325 }
9326
9327 // parallel ports (optional)
9328 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9329 it != data.llParallelPorts.end();
9330 ++it)
9331 {
9332 const settings::ParallelPort &p = *it;
9333
9334 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9335 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9336 if (FAILED(rc)) return rc;
9337 }
9338
9339 /* AudioAdapter */
9340 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9341 if (FAILED(rc)) return rc;
9342
9343 /* Shared folders */
9344 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9345 it != data.llSharedFolders.end();
9346 ++it)
9347 {
9348 const settings::SharedFolder &sf = *it;
9349
9350 ComObjPtr<SharedFolder> sharedFolder;
9351 /* Check for double entries. Not allowed! */
9352 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9353 if (SUCCEEDED(rc))
9354 return setError(VBOX_E_OBJECT_IN_USE,
9355 tr("Shared folder named '%s' already exists"),
9356 sf.strName.c_str());
9357
9358 /* Create the new shared folder. Don't break on error. This will be
9359 * reported when the machine starts. */
9360 sharedFolder.createObject();
9361 rc = sharedFolder->init(getMachine(),
9362 sf.strName,
9363 sf.strHostPath,
9364 RT_BOOL(sf.fWritable),
9365 RT_BOOL(sf.fAutoMount),
9366 false /* fFailOnError */);
9367 if (FAILED(rc)) return rc;
9368 mHWData->mSharedFolders.push_back(sharedFolder);
9369 }
9370
9371 // Clipboard
9372 mHWData->mClipboardMode = data.clipboardMode;
9373
9374 // drag'n'drop
9375 mHWData->mDragAndDropMode = data.dragAndDropMode;
9376
9377 // guest settings
9378 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9379
9380 // IO settings
9381 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9382 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9383
9384 // Host PCI devices
9385 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9386 it != data.pciAttachments.end();
9387 ++it)
9388 {
9389 const settings::HostPCIDeviceAttachment &hpda = *it;
9390 ComObjPtr<PCIDeviceAttachment> pda;
9391
9392 pda.createObject();
9393 pda->loadSettings(this, hpda);
9394 mHWData->mPCIDeviceAssignments.push_back(pda);
9395 }
9396
9397 /*
9398 * (The following isn't really real hardware, but it lives in HWData
9399 * for reasons of convenience.)
9400 */
9401
9402#ifdef VBOX_WITH_GUEST_PROPS
9403 /* Guest properties (optional) */
9404 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9405 it != data.llGuestProperties.end();
9406 ++it)
9407 {
9408 const settings::GuestProperty &prop = *it;
9409 uint32_t fFlags = guestProp::NILFLAG;
9410 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9411 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9412 mHWData->mGuestProperties[prop.strName] = property;
9413 }
9414
9415 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9416#endif /* VBOX_WITH_GUEST_PROPS defined */
9417
9418 rc = loadDebugging(pDbg);
9419 if (FAILED(rc))
9420 return rc;
9421
9422 mHWData->mAutostart = *pAutostart;
9423
9424 /* default frontend */
9425 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9426 }
9427 catch(std::bad_alloc &)
9428 {
9429 return E_OUTOFMEMORY;
9430 }
9431
9432 AssertComRC(rc);
9433 return rc;
9434}
9435
9436/**
9437 * Called from Machine::loadHardware() to load the debugging settings of the
9438 * machine.
9439 *
9440 * @param pDbg Pointer to the settings.
9441 */
9442HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9443{
9444 mHWData->mDebugging = *pDbg;
9445 /* no more processing currently required, this will probably change. */
9446 return S_OK;
9447}
9448
9449/**
9450 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9451 *
9452 * @param data
9453 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9454 * @param puuidSnapshot
9455 * @return
9456 */
9457HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9458 const Guid *puuidRegistry,
9459 const Guid *puuidSnapshot)
9460{
9461 AssertReturn(!isSessionMachine(), E_FAIL);
9462
9463 HRESULT rc = S_OK;
9464
9465 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9466 it != data.llStorageControllers.end();
9467 ++it)
9468 {
9469 const settings::StorageController &ctlData = *it;
9470
9471 ComObjPtr<StorageController> pCtl;
9472 /* Try to find one with the name first. */
9473 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9474 if (SUCCEEDED(rc))
9475 return setError(VBOX_E_OBJECT_IN_USE,
9476 tr("Storage controller named '%s' already exists"),
9477 ctlData.strName.c_str());
9478
9479 pCtl.createObject();
9480 rc = pCtl->init(this,
9481 ctlData.strName,
9482 ctlData.storageBus,
9483 ctlData.ulInstance,
9484 ctlData.fBootable);
9485 if (FAILED(rc)) return rc;
9486
9487 mStorageControllers->push_back(pCtl);
9488
9489 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9490 if (FAILED(rc)) return rc;
9491
9492 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9493 if (FAILED(rc)) return rc;
9494
9495 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9496 if (FAILED(rc)) return rc;
9497
9498 /* Set IDE emulation settings (only for AHCI controller). */
9499 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9500 {
9501 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9502 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9503 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9504 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9505 )
9506 return rc;
9507 }
9508
9509 /* Load the attached devices now. */
9510 rc = loadStorageDevices(pCtl,
9511 ctlData,
9512 puuidRegistry,
9513 puuidSnapshot);
9514 if (FAILED(rc)) return rc;
9515 }
9516
9517 return S_OK;
9518}
9519
9520/**
9521 * Called from loadStorageControllers for a controller's devices.
9522 *
9523 * @param aStorageController
9524 * @param data
9525 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9526 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9527 * @return
9528 */
9529HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9530 const settings::StorageController &data,
9531 const Guid *puuidRegistry,
9532 const Guid *puuidSnapshot)
9533{
9534 HRESULT rc = S_OK;
9535
9536 /* paranoia: detect duplicate attachments */
9537 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9538 it != data.llAttachedDevices.end();
9539 ++it)
9540 {
9541 const settings::AttachedDevice &ad = *it;
9542
9543 for (settings::AttachedDevicesList::const_iterator it2 = it;
9544 it2 != data.llAttachedDevices.end();
9545 ++it2)
9546 {
9547 if (it == it2)
9548 continue;
9549
9550 const settings::AttachedDevice &ad2 = *it2;
9551
9552 if ( ad.lPort == ad2.lPort
9553 && ad.lDevice == ad2.lDevice)
9554 {
9555 return setError(E_FAIL,
9556 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9557 aStorageController->getName().c_str(),
9558 ad.lPort,
9559 ad.lDevice,
9560 mUserData->s.strName.c_str());
9561 }
9562 }
9563 }
9564
9565 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9566 it != data.llAttachedDevices.end();
9567 ++it)
9568 {
9569 const settings::AttachedDevice &dev = *it;
9570 ComObjPtr<Medium> medium;
9571
9572 switch (dev.deviceType)
9573 {
9574 case DeviceType_Floppy:
9575 case DeviceType_DVD:
9576 if (dev.strHostDriveSrc.isNotEmpty())
9577 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9578 else
9579 rc = mParent->findRemoveableMedium(dev.deviceType,
9580 dev.uuid,
9581 false /* fRefresh */,
9582 false /* aSetError */,
9583 medium);
9584 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9585 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9586 rc = S_OK;
9587 break;
9588
9589 case DeviceType_HardDisk:
9590 {
9591 /* find a hard disk by UUID */
9592 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9593 if (FAILED(rc))
9594 {
9595 if (isSnapshotMachine())
9596 {
9597 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9598 // so the user knows that the bad disk is in a snapshot somewhere
9599 com::ErrorInfo info;
9600 return setError(E_FAIL,
9601 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9602 puuidSnapshot->raw(),
9603 info.getText().raw());
9604 }
9605 else
9606 return rc;
9607 }
9608
9609 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9610
9611 if (medium->getType() == MediumType_Immutable)
9612 {
9613 if (isSnapshotMachine())
9614 return setError(E_FAIL,
9615 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9616 "of the virtual machine '%s' ('%s')"),
9617 medium->getLocationFull().c_str(),
9618 dev.uuid.raw(),
9619 puuidSnapshot->raw(),
9620 mUserData->s.strName.c_str(),
9621 mData->m_strConfigFileFull.c_str());
9622
9623 return setError(E_FAIL,
9624 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9625 medium->getLocationFull().c_str(),
9626 dev.uuid.raw(),
9627 mUserData->s.strName.c_str(),
9628 mData->m_strConfigFileFull.c_str());
9629 }
9630
9631 if (medium->getType() == MediumType_MultiAttach)
9632 {
9633 if (isSnapshotMachine())
9634 return setError(E_FAIL,
9635 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9636 "of the virtual machine '%s' ('%s')"),
9637 medium->getLocationFull().c_str(),
9638 dev.uuid.raw(),
9639 puuidSnapshot->raw(),
9640 mUserData->s.strName.c_str(),
9641 mData->m_strConfigFileFull.c_str());
9642
9643 return setError(E_FAIL,
9644 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9645 medium->getLocationFull().c_str(),
9646 dev.uuid.raw(),
9647 mUserData->s.strName.c_str(),
9648 mData->m_strConfigFileFull.c_str());
9649 }
9650
9651 if ( !isSnapshotMachine()
9652 && medium->getChildren().size() != 0
9653 )
9654 return setError(E_FAIL,
9655 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9656 "because it has %d differencing child hard disks"),
9657 medium->getLocationFull().c_str(),
9658 dev.uuid.raw(),
9659 mUserData->s.strName.c_str(),
9660 mData->m_strConfigFileFull.c_str(),
9661 medium->getChildren().size());
9662
9663 if (findAttachment(mMediaData->mAttachments,
9664 medium))
9665 return setError(E_FAIL,
9666 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9667 medium->getLocationFull().c_str(),
9668 dev.uuid.raw(),
9669 mUserData->s.strName.c_str(),
9670 mData->m_strConfigFileFull.c_str());
9671
9672 break;
9673 }
9674
9675 default:
9676 return setError(E_FAIL,
9677 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9678 medium->getLocationFull().c_str(),
9679 mUserData->s.strName.c_str(),
9680 mData->m_strConfigFileFull.c_str());
9681 }
9682
9683 if (FAILED(rc))
9684 break;
9685
9686 /* Bandwidth groups are loaded at this point. */
9687 ComObjPtr<BandwidthGroup> pBwGroup;
9688
9689 if (!dev.strBwGroup.isEmpty())
9690 {
9691 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9692 if (FAILED(rc))
9693 return setError(E_FAIL,
9694 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9695 medium->getLocationFull().c_str(),
9696 dev.strBwGroup.c_str(),
9697 mUserData->s.strName.c_str(),
9698 mData->m_strConfigFileFull.c_str());
9699 pBwGroup->reference();
9700 }
9701
9702 const Bstr controllerName = aStorageController->getName();
9703 ComObjPtr<MediumAttachment> pAttachment;
9704 pAttachment.createObject();
9705 rc = pAttachment->init(this,
9706 medium,
9707 controllerName,
9708 dev.lPort,
9709 dev.lDevice,
9710 dev.deviceType,
9711 false,
9712 dev.fPassThrough,
9713 dev.fTempEject,
9714 dev.fNonRotational,
9715 dev.fDiscard,
9716 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9717 if (FAILED(rc)) break;
9718
9719 /* associate the medium with this machine and snapshot */
9720 if (!medium.isNull())
9721 {
9722 AutoCaller medCaller(medium);
9723 if (FAILED(medCaller.rc())) return medCaller.rc();
9724 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9725
9726 if (isSnapshotMachine())
9727 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9728 else
9729 rc = medium->addBackReference(mData->mUuid);
9730 /* If the medium->addBackReference fails it sets an appropriate
9731 * error message, so no need to do any guesswork here. */
9732
9733 if (puuidRegistry)
9734 // caller wants registry ID to be set on all attached media (OVF import case)
9735 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9736 }
9737
9738 if (FAILED(rc))
9739 break;
9740
9741 /* back up mMediaData to let registeredInit() properly rollback on failure
9742 * (= limited accessibility) */
9743 setModified(IsModified_Storage);
9744 mMediaData.backup();
9745 mMediaData->mAttachments.push_back(pAttachment);
9746 }
9747
9748 return rc;
9749}
9750
9751/**
9752 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9753 *
9754 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9755 * @param aSnapshot where to return the found snapshot
9756 * @param aSetError true to set extended error info on failure
9757 */
9758HRESULT Machine::findSnapshotById(const Guid &aId,
9759 ComObjPtr<Snapshot> &aSnapshot,
9760 bool aSetError /* = false */)
9761{
9762 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9763
9764 if (!mData->mFirstSnapshot)
9765 {
9766 if (aSetError)
9767 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9768 return E_FAIL;
9769 }
9770
9771 if (aId.isZero())
9772 aSnapshot = mData->mFirstSnapshot;
9773 else
9774 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9775
9776 if (!aSnapshot)
9777 {
9778 if (aSetError)
9779 return setError(E_FAIL,
9780 tr("Could not find a snapshot with UUID {%s}"),
9781 aId.toString().c_str());
9782 return E_FAIL;
9783 }
9784
9785 return S_OK;
9786}
9787
9788/**
9789 * Returns the snapshot with the given name or fails of no such snapshot.
9790 *
9791 * @param aName snapshot name to find
9792 * @param aSnapshot where to return the found snapshot
9793 * @param aSetError true to set extended error info on failure
9794 */
9795HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9796 ComObjPtr<Snapshot> &aSnapshot,
9797 bool aSetError /* = false */)
9798{
9799 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9800
9801 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9802
9803 if (!mData->mFirstSnapshot)
9804 {
9805 if (aSetError)
9806 return setError(VBOX_E_OBJECT_NOT_FOUND,
9807 tr("This machine does not have any snapshots"));
9808 return VBOX_E_OBJECT_NOT_FOUND;
9809 }
9810
9811 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9812
9813 if (!aSnapshot)
9814 {
9815 if (aSetError)
9816 return setError(VBOX_E_OBJECT_NOT_FOUND,
9817 tr("Could not find a snapshot named '%s'"), strName.c_str());
9818 return VBOX_E_OBJECT_NOT_FOUND;
9819 }
9820
9821 return S_OK;
9822}
9823
9824/**
9825 * Returns a storage controller object with the given name.
9826 *
9827 * @param aName storage controller name to find
9828 * @param aStorageController where to return the found storage controller
9829 * @param aSetError true to set extended error info on failure
9830 */
9831HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9832 ComObjPtr<StorageController> &aStorageController,
9833 bool aSetError /* = false */)
9834{
9835 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9836
9837 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9838 it != mStorageControllers->end();
9839 ++it)
9840 {
9841 if ((*it)->getName() == aName)
9842 {
9843 aStorageController = (*it);
9844 return S_OK;
9845 }
9846 }
9847
9848 if (aSetError)
9849 return setError(VBOX_E_OBJECT_NOT_FOUND,
9850 tr("Could not find a storage controller named '%s'"),
9851 aName.c_str());
9852 return VBOX_E_OBJECT_NOT_FOUND;
9853}
9854
9855/**
9856 * Returns a USB controller object with the given name.
9857 *
9858 * @param aName USB controller name to find
9859 * @param aUSBController where to return the found USB controller
9860 * @param aSetError true to set extended error info on failure
9861 */
9862HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9863 ComObjPtr<USBController> &aUSBController,
9864 bool aSetError /* = false */)
9865{
9866 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9867
9868 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9869 it != mUSBControllers->end();
9870 ++it)
9871 {
9872 if ((*it)->getName() == aName)
9873 {
9874 aUSBController = (*it);
9875 return S_OK;
9876 }
9877 }
9878
9879 if (aSetError)
9880 return setError(VBOX_E_OBJECT_NOT_FOUND,
9881 tr("Could not find a storage controller named '%s'"),
9882 aName.c_str());
9883 return VBOX_E_OBJECT_NOT_FOUND;
9884}
9885
9886/**
9887 * Returns the number of USB controller instance of the given type.
9888 *
9889 * @param enmType USB controller type.
9890 */
9891ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9892{
9893 ULONG cCtrls = 0;
9894
9895 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9896 it != mUSBControllers->end();
9897 ++it)
9898 {
9899 if ((*it)->getControllerType() == enmType)
9900 cCtrls++;
9901 }
9902
9903 return cCtrls;
9904}
9905
9906HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9907 MediaData::AttachmentList &atts)
9908{
9909 AutoCaller autoCaller(this);
9910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9911
9912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9913
9914 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9915 it != mMediaData->mAttachments.end();
9916 ++it)
9917 {
9918 const ComObjPtr<MediumAttachment> &pAtt = *it;
9919
9920 // should never happen, but deal with NULL pointers in the list.
9921 AssertStmt(!pAtt.isNull(), continue);
9922
9923 // getControllerName() needs caller+read lock
9924 AutoCaller autoAttCaller(pAtt);
9925 if (FAILED(autoAttCaller.rc()))
9926 {
9927 atts.clear();
9928 return autoAttCaller.rc();
9929 }
9930 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9931
9932 if (pAtt->getControllerName() == aName)
9933 atts.push_back(pAtt);
9934 }
9935
9936 return S_OK;
9937}
9938
9939/**
9940 * Helper for #saveSettings. Cares about renaming the settings directory and
9941 * file if the machine name was changed and about creating a new settings file
9942 * if this is a new machine.
9943 *
9944 * @note Must be never called directly but only from #saveSettings().
9945 */
9946HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9947{
9948 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9949
9950 HRESULT rc = S_OK;
9951
9952 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9953
9954 /// @todo need to handle primary group change, too
9955
9956 /* attempt to rename the settings file if machine name is changed */
9957 if ( mUserData->s.fNameSync
9958 && mUserData.isBackedUp()
9959 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9960 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9961 )
9962 {
9963 bool dirRenamed = false;
9964 bool fileRenamed = false;
9965
9966 Utf8Str configFile, newConfigFile;
9967 Utf8Str configFilePrev, newConfigFilePrev;
9968 Utf8Str configDir, newConfigDir;
9969
9970 do
9971 {
9972 int vrc = VINF_SUCCESS;
9973
9974 Utf8Str name = mUserData.backedUpData()->s.strName;
9975 Utf8Str newName = mUserData->s.strName;
9976 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9977 if (group == "/")
9978 group.setNull();
9979 Utf8Str newGroup = mUserData->s.llGroups.front();
9980 if (newGroup == "/")
9981 newGroup.setNull();
9982
9983 configFile = mData->m_strConfigFileFull;
9984
9985 /* first, rename the directory if it matches the group and machine name */
9986 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9987 group.c_str(), RTPATH_DELIMITER, name.c_str());
9988 /** @todo hack, make somehow use of ComposeMachineFilename */
9989 if (mUserData->s.fDirectoryIncludesUUID)
9990 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9991 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9992 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9993 /** @todo hack, make somehow use of ComposeMachineFilename */
9994 if (mUserData->s.fDirectoryIncludesUUID)
9995 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9996 configDir = configFile;
9997 configDir.stripFilename();
9998 newConfigDir = configDir;
9999 if ( configDir.length() >= groupPlusName.length()
10000 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
10001 {
10002 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10003 Utf8Str newConfigBaseDir(newConfigDir);
10004 newConfigDir.append(newGroupPlusName);
10005 /* consistency: use \ if appropriate on the platform */
10006 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10007 /* new dir and old dir cannot be equal here because of 'if'
10008 * above and because name != newName */
10009 Assert(configDir != newConfigDir);
10010 if (!fSettingsFileIsNew)
10011 {
10012 /* perform real rename only if the machine is not new */
10013 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10014 if ( vrc == VERR_FILE_NOT_FOUND
10015 || vrc == VERR_PATH_NOT_FOUND)
10016 {
10017 /* create the parent directory, then retry renaming */
10018 Utf8Str parent(newConfigDir);
10019 parent.stripFilename();
10020 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10021 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10022 }
10023 if (RT_FAILURE(vrc))
10024 {
10025 rc = setError(E_FAIL,
10026 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10027 configDir.c_str(),
10028 newConfigDir.c_str(),
10029 vrc);
10030 break;
10031 }
10032 /* delete subdirectories which are no longer needed */
10033 Utf8Str dir(configDir);
10034 dir.stripFilename();
10035 while (dir != newConfigBaseDir && dir != ".")
10036 {
10037 vrc = RTDirRemove(dir.c_str());
10038 if (RT_FAILURE(vrc))
10039 break;
10040 dir.stripFilename();
10041 }
10042 dirRenamed = true;
10043 }
10044 }
10045
10046 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10047 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10048
10049 /* then try to rename the settings file itself */
10050 if (newConfigFile != configFile)
10051 {
10052 /* get the path to old settings file in renamed directory */
10053 configFile = Utf8StrFmt("%s%c%s",
10054 newConfigDir.c_str(),
10055 RTPATH_DELIMITER,
10056 RTPathFilename(configFile.c_str()));
10057 if (!fSettingsFileIsNew)
10058 {
10059 /* perform real rename only if the machine is not new */
10060 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10061 if (RT_FAILURE(vrc))
10062 {
10063 rc = setError(E_FAIL,
10064 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10065 configFile.c_str(),
10066 newConfigFile.c_str(),
10067 vrc);
10068 break;
10069 }
10070 fileRenamed = true;
10071 configFilePrev = configFile;
10072 configFilePrev += "-prev";
10073 newConfigFilePrev = newConfigFile;
10074 newConfigFilePrev += "-prev";
10075 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10076 }
10077 }
10078
10079 // update m_strConfigFileFull amd mConfigFile
10080 mData->m_strConfigFileFull = newConfigFile;
10081 // compute the relative path too
10082 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10083
10084 // store the old and new so that VirtualBox::saveSettings() can update
10085 // the media registry
10086 if ( mData->mRegistered
10087 && configDir != newConfigDir)
10088 {
10089 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10090
10091 if (pfNeedsGlobalSaveSettings)
10092 *pfNeedsGlobalSaveSettings = true;
10093 }
10094
10095 // in the saved state file path, replace the old directory with the new directory
10096 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10097 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10098
10099 // and do the same thing for the saved state file paths of all the online snapshots
10100 if (mData->mFirstSnapshot)
10101 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
10102 newConfigDir.c_str());
10103 }
10104 while (0);
10105
10106 if (FAILED(rc))
10107 {
10108 /* silently try to rename everything back */
10109 if (fileRenamed)
10110 {
10111 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10112 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10113 }
10114 if (dirRenamed)
10115 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10116 }
10117
10118 if (FAILED(rc)) return rc;
10119 }
10120
10121 if (fSettingsFileIsNew)
10122 {
10123 /* create a virgin config file */
10124 int vrc = VINF_SUCCESS;
10125
10126 /* ensure the settings directory exists */
10127 Utf8Str path(mData->m_strConfigFileFull);
10128 path.stripFilename();
10129 if (!RTDirExists(path.c_str()))
10130 {
10131 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10132 if (RT_FAILURE(vrc))
10133 {
10134 return setError(E_FAIL,
10135 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10136 path.c_str(),
10137 vrc);
10138 }
10139 }
10140
10141 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10142 path = Utf8Str(mData->m_strConfigFileFull);
10143 RTFILE f = NIL_RTFILE;
10144 vrc = RTFileOpen(&f, path.c_str(),
10145 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10146 if (RT_FAILURE(vrc))
10147 return setError(E_FAIL,
10148 tr("Could not create the settings file '%s' (%Rrc)"),
10149 path.c_str(),
10150 vrc);
10151 RTFileClose(f);
10152 }
10153
10154 return rc;
10155}
10156
10157/**
10158 * Saves and commits machine data, user data and hardware data.
10159 *
10160 * Note that on failure, the data remains uncommitted.
10161 *
10162 * @a aFlags may combine the following flags:
10163 *
10164 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10165 * Used when saving settings after an operation that makes them 100%
10166 * correspond to the settings from the current snapshot.
10167 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10168 * #isReallyModified() returns false. This is necessary for cases when we
10169 * change machine data directly, not through the backup()/commit() mechanism.
10170 * - SaveS_Force: settings will be saved without doing a deep compare of the
10171 * settings structures. This is used when this is called because snapshots
10172 * have changed to avoid the overhead of the deep compare.
10173 *
10174 * @note Must be called from under this object's write lock. Locks children for
10175 * writing.
10176 *
10177 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10178 * initialized to false and that will be set to true by this function if
10179 * the caller must invoke VirtualBox::saveSettings() because the global
10180 * settings have changed. This will happen if a machine rename has been
10181 * saved and the global machine and media registries will therefore need
10182 * updating.
10183 */
10184HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10185 int aFlags /*= 0*/)
10186{
10187 LogFlowThisFuncEnter();
10188
10189 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10190
10191 /* make sure child objects are unable to modify the settings while we are
10192 * saving them */
10193 ensureNoStateDependencies();
10194
10195 AssertReturn(!isSnapshotMachine(),
10196 E_FAIL);
10197
10198 HRESULT rc = S_OK;
10199 bool fNeedsWrite = false;
10200
10201 /* First, prepare to save settings. It will care about renaming the
10202 * settings directory and file if the machine name was changed and about
10203 * creating a new settings file if this is a new machine. */
10204 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10205 if (FAILED(rc)) return rc;
10206
10207 // keep a pointer to the current settings structures
10208 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10209 settings::MachineConfigFile *pNewConfig = NULL;
10210
10211 try
10212 {
10213 // make a fresh one to have everyone write stuff into
10214 pNewConfig = new settings::MachineConfigFile(NULL);
10215 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10216
10217 // now go and copy all the settings data from COM to the settings structures
10218 // (this calles saveSettings() on all the COM objects in the machine)
10219 copyMachineDataToSettings(*pNewConfig);
10220
10221 if (aFlags & SaveS_ResetCurStateModified)
10222 {
10223 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10224 mData->mCurrentStateModified = FALSE;
10225 fNeedsWrite = true; // always, no need to compare
10226 }
10227 else if (aFlags & SaveS_Force)
10228 {
10229 fNeedsWrite = true; // always, no need to compare
10230 }
10231 else
10232 {
10233 if (!mData->mCurrentStateModified)
10234 {
10235 // do a deep compare of the settings that we just saved with the settings
10236 // previously stored in the config file; this invokes MachineConfigFile::operator==
10237 // which does a deep compare of all the settings, which is expensive but less expensive
10238 // than writing out XML in vain
10239 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10240
10241 // could still be modified if any settings changed
10242 mData->mCurrentStateModified = fAnySettingsChanged;
10243
10244 fNeedsWrite = fAnySettingsChanged;
10245 }
10246 else
10247 fNeedsWrite = true;
10248 }
10249
10250 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10251
10252 if (fNeedsWrite)
10253 // now spit it all out!
10254 pNewConfig->write(mData->m_strConfigFileFull);
10255
10256 mData->pMachineConfigFile = pNewConfig;
10257 delete pOldConfig;
10258 commit();
10259
10260 // after saving settings, we are no longer different from the XML on disk
10261 mData->flModifications = 0;
10262 }
10263 catch (HRESULT err)
10264 {
10265 // we assume that error info is set by the thrower
10266 rc = err;
10267
10268 // restore old config
10269 delete pNewConfig;
10270 mData->pMachineConfigFile = pOldConfig;
10271 }
10272 catch (...)
10273 {
10274 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10275 }
10276
10277 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10278 {
10279 /* Fire the data change event, even on failure (since we've already
10280 * committed all data). This is done only for SessionMachines because
10281 * mutable Machine instances are always not registered (i.e. private
10282 * to the client process that creates them) and thus don't need to
10283 * inform callbacks. */
10284 if (isSessionMachine())
10285 mParent->onMachineDataChange(mData->mUuid);
10286 }
10287
10288 LogFlowThisFunc(("rc=%08X\n", rc));
10289 LogFlowThisFuncLeave();
10290 return rc;
10291}
10292
10293/**
10294 * Implementation for saving the machine settings into the given
10295 * settings::MachineConfigFile instance. This copies machine extradata
10296 * from the previous machine config file in the instance data, if any.
10297 *
10298 * This gets called from two locations:
10299 *
10300 * -- Machine::saveSettings(), during the regular XML writing;
10301 *
10302 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10303 * exported to OVF and we write the VirtualBox proprietary XML
10304 * into a <vbox:Machine> tag.
10305 *
10306 * This routine fills all the fields in there, including snapshots, *except*
10307 * for the following:
10308 *
10309 * -- fCurrentStateModified. There is some special logic associated with that.
10310 *
10311 * The caller can then call MachineConfigFile::write() or do something else
10312 * with it.
10313 *
10314 * Caller must hold the machine lock!
10315 *
10316 * This throws XML errors and HRESULT, so the caller must have a catch block!
10317 */
10318void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10319{
10320 // deep copy extradata
10321 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10322
10323 config.uuid = mData->mUuid;
10324
10325 // copy name, description, OS type, teleport, UTC etc.
10326 config.machineUserData = mUserData->s;
10327
10328 // Encode the Icon Override data from Machine and store on config userdata.
10329 com::SafeArray<BYTE> iconByte;
10330 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10331 ssize_t cbData = iconByte.size();
10332 if (cbData > 0)
10333 {
10334 ssize_t cchOut = RTBase64EncodedLength(cbData);
10335 Utf8Str strIconData;
10336 strIconData.reserve(cchOut+1);
10337 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10338 strIconData.mutableRaw(), strIconData.capacity(),
10339 NULL);
10340 if (RT_FAILURE(vrc))
10341 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10342 strIconData.jolt();
10343 config.machineUserData.ovIcon = strIconData;
10344 }
10345 else
10346 config.machineUserData.ovIcon.setNull();
10347
10348 if ( mData->mMachineState == MachineState_Saved
10349 || mData->mMachineState == MachineState_Restoring
10350 // when deleting a snapshot we may or may not have a saved state in the current state,
10351 // so let's not assert here please
10352 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10353 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10354 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10355 && (!mSSData->strStateFilePath.isEmpty())
10356 )
10357 )
10358 {
10359 Assert(!mSSData->strStateFilePath.isEmpty());
10360 /* try to make the file name relative to the settings file dir */
10361 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10362 }
10363 else
10364 {
10365 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10366 config.strStateFile.setNull();
10367 }
10368
10369 if (mData->mCurrentSnapshot)
10370 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10371 else
10372 config.uuidCurrentSnapshot.clear();
10373
10374 config.timeLastStateChange = mData->mLastStateChange;
10375 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10376 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10377
10378 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10379 if (FAILED(rc)) throw rc;
10380
10381 rc = saveStorageControllers(config.storageMachine);
10382 if (FAILED(rc)) throw rc;
10383
10384 // save machine's media registry if this is VirtualBox 4.0 or later
10385 if (config.canHaveOwnMediaRegistry())
10386 {
10387 // determine machine folder
10388 Utf8Str strMachineFolder = getSettingsFileFull();
10389 strMachineFolder.stripFilename();
10390 mParent->saveMediaRegistry(config.mediaRegistry,
10391 getId(), // only media with registry ID == machine UUID
10392 strMachineFolder);
10393 // this throws HRESULT
10394 }
10395
10396 // save snapshots
10397 rc = saveAllSnapshots(config);
10398 if (FAILED(rc)) throw rc;
10399}
10400
10401/**
10402 * Saves all snapshots of the machine into the given machine config file. Called
10403 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10404 * @param config
10405 * @return
10406 */
10407HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10408{
10409 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10410
10411 HRESULT rc = S_OK;
10412
10413 try
10414 {
10415 config.llFirstSnapshot.clear();
10416
10417 if (mData->mFirstSnapshot)
10418 {
10419 settings::Snapshot snapNew;
10420 config.llFirstSnapshot.push_back(snapNew);
10421
10422 // get reference to the fresh copy of the snapshot on the list and
10423 // work on that copy directly to avoid excessive copying later
10424 settings::Snapshot &snap = config.llFirstSnapshot.front();
10425
10426 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10427 if (FAILED(rc)) throw rc;
10428 }
10429
10430// if (mType == IsSessionMachine)
10431// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10432
10433 }
10434 catch (HRESULT err)
10435 {
10436 /* we assume that error info is set by the thrower */
10437 rc = err;
10438 }
10439 catch (...)
10440 {
10441 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10442 }
10443
10444 return rc;
10445}
10446
10447/**
10448 * Saves the VM hardware configuration. It is assumed that the
10449 * given node is empty.
10450 *
10451 * @param data Reference to the settings object for the hardware config.
10452 * @param pDbg Pointer to the settings object for the debugging config
10453 * which happens to live in mHWData.
10454 * @param pAutostart Pointer to the settings object for the autostart config
10455 * which happens to live in mHWData.
10456 */
10457HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10458 settings::Autostart *pAutostart)
10459{
10460 HRESULT rc = S_OK;
10461
10462 try
10463 {
10464 /* The hardware version attribute (optional).
10465 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10466 if ( mHWData->mHWVersion == "1"
10467 && mSSData->strStateFilePath.isEmpty()
10468 )
10469 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. */
10470
10471 data.strVersion = mHWData->mHWVersion;
10472 data.uuid = mHWData->mHardwareUUID;
10473
10474 // CPU
10475 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10476 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10477 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10478 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10479 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10480 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10481 data.fPAE = !!mHWData->mPAEEnabled;
10482 data.enmLongMode = mHWData->mLongMode;
10483 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10484
10485 /* Standard and Extended CPUID leafs. */
10486 data.llCpuIdLeafs.clear();
10487 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10488 {
10489 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10490 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10491 }
10492 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10493 {
10494 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10495 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10496 }
10497
10498 data.cCPUs = mHWData->mCPUCount;
10499 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10500 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10501
10502 data.llCpus.clear();
10503 if (data.fCpuHotPlug)
10504 {
10505 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10506 {
10507 if (mHWData->mCPUAttached[idx])
10508 {
10509 settings::Cpu cpu;
10510 cpu.ulId = idx;
10511 data.llCpus.push_back(cpu);
10512 }
10513 }
10514 }
10515
10516 // memory
10517 data.ulMemorySizeMB = mHWData->mMemorySize;
10518 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10519
10520 // firmware
10521 data.firmwareType = mHWData->mFirmwareType;
10522
10523 // HID
10524 data.pointingHIDType = mHWData->mPointingHIDType;
10525 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10526
10527 // chipset
10528 data.chipsetType = mHWData->mChipsetType;
10529
10530 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10531
10532 // HPET
10533 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10534
10535 // boot order
10536 data.mapBootOrder.clear();
10537 for (size_t i = 0;
10538 i < RT_ELEMENTS(mHWData->mBootOrder);
10539 ++i)
10540 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10541
10542 // display
10543 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10544 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10545 data.cMonitors = mHWData->mMonitorCount;
10546 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10547 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10548 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10549 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10550 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10551 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10552 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10553 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10554 {
10555 if (mHWData->maVideoCaptureScreens[i])
10556 ASMBitSet(&data.u64VideoCaptureScreens, i);
10557 else
10558 ASMBitClear(&data.u64VideoCaptureScreens, i);
10559 }
10560 /* store relative video capture file if possible */
10561 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10562
10563 /* VRDEServer settings (optional) */
10564 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10565 if (FAILED(rc)) throw rc;
10566
10567 /* BIOS (required) */
10568 rc = mBIOSSettings->saveSettings(data.biosSettings);
10569 if (FAILED(rc)) throw rc;
10570
10571 /* USB Controller (required) */
10572 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10573 it != mUSBControllers->end();
10574 ++it)
10575 {
10576 ComObjPtr<USBController> ctrl = *it;
10577 settings::USBController settingsCtrl;
10578
10579 settingsCtrl.strName = ctrl->getName();
10580 settingsCtrl.enmType = ctrl->getControllerType();
10581
10582 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10583 }
10584
10585 /* USB device filters (required) */
10586 rc = mUSBDeviceFilters->saveSettings(data.usbSettings);
10587 if (FAILED(rc)) throw rc;
10588
10589 /* Network adapters (required) */
10590 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10591 data.llNetworkAdapters.clear();
10592 /* Write out only the nominal number of network adapters for this
10593 * chipset type. Since Machine::commit() hasn't been called there
10594 * may be extra NIC settings in the vector. */
10595 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10596 {
10597 settings::NetworkAdapter nic;
10598 nic.ulSlot = slot;
10599 /* paranoia check... must not be NULL, but must not crash either. */
10600 if (mNetworkAdapters[slot])
10601 {
10602 rc = mNetworkAdapters[slot]->saveSettings(nic);
10603 if (FAILED(rc)) throw rc;
10604
10605 data.llNetworkAdapters.push_back(nic);
10606 }
10607 }
10608
10609 /* Serial ports */
10610 data.llSerialPorts.clear();
10611 for (ULONG slot = 0;
10612 slot < RT_ELEMENTS(mSerialPorts);
10613 ++slot)
10614 {
10615 settings::SerialPort s;
10616 s.ulSlot = slot;
10617 rc = mSerialPorts[slot]->saveSettings(s);
10618 if (FAILED(rc)) return rc;
10619
10620 data.llSerialPorts.push_back(s);
10621 }
10622
10623 /* Parallel ports */
10624 data.llParallelPorts.clear();
10625 for (ULONG slot = 0;
10626 slot < RT_ELEMENTS(mParallelPorts);
10627 ++slot)
10628 {
10629 settings::ParallelPort p;
10630 p.ulSlot = slot;
10631 rc = mParallelPorts[slot]->saveSettings(p);
10632 if (FAILED(rc)) return rc;
10633
10634 data.llParallelPorts.push_back(p);
10635 }
10636
10637 /* Audio adapter */
10638 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10639 if (FAILED(rc)) return rc;
10640
10641 /* Shared folders */
10642 data.llSharedFolders.clear();
10643 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10644 it != mHWData->mSharedFolders.end();
10645 ++it)
10646 {
10647 SharedFolder *pSF = *it;
10648 AutoCaller sfCaller(pSF);
10649 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10650 settings::SharedFolder sf;
10651 sf.strName = pSF->getName();
10652 sf.strHostPath = pSF->getHostPath();
10653 sf.fWritable = !!pSF->isWritable();
10654 sf.fAutoMount = !!pSF->isAutoMounted();
10655
10656 data.llSharedFolders.push_back(sf);
10657 }
10658
10659 // clipboard
10660 data.clipboardMode = mHWData->mClipboardMode;
10661
10662 // drag'n'drop
10663 data.dragAndDropMode = mHWData->mDragAndDropMode;
10664
10665 /* Guest */
10666 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10667
10668 // IO settings
10669 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10670 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10671
10672 /* BandwidthControl (required) */
10673 rc = mBandwidthControl->saveSettings(data.ioSettings);
10674 if (FAILED(rc)) throw rc;
10675
10676 /* Host PCI devices */
10677 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10678 it != mHWData->mPCIDeviceAssignments.end();
10679 ++it)
10680 {
10681 ComObjPtr<PCIDeviceAttachment> pda = *it;
10682 settings::HostPCIDeviceAttachment hpda;
10683
10684 rc = pda->saveSettings(hpda);
10685 if (FAILED(rc)) throw rc;
10686
10687 data.pciAttachments.push_back(hpda);
10688 }
10689
10690
10691 // guest properties
10692 data.llGuestProperties.clear();
10693#ifdef VBOX_WITH_GUEST_PROPS
10694 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10695 it != mHWData->mGuestProperties.end();
10696 ++it)
10697 {
10698 HWData::GuestProperty property = it->second;
10699
10700 /* Remove transient guest properties at shutdown unless we
10701 * are saving state */
10702 if ( ( mData->mMachineState == MachineState_PoweredOff
10703 || mData->mMachineState == MachineState_Aborted
10704 || mData->mMachineState == MachineState_Teleported)
10705 && ( property.mFlags & guestProp::TRANSIENT
10706 || property.mFlags & guestProp::TRANSRESET))
10707 continue;
10708 settings::GuestProperty prop;
10709 prop.strName = it->first;
10710 prop.strValue = property.strValue;
10711 prop.timestamp = property.mTimestamp;
10712 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10713 guestProp::writeFlags(property.mFlags, szFlags);
10714 prop.strFlags = szFlags;
10715
10716 data.llGuestProperties.push_back(prop);
10717 }
10718
10719 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10720 /* I presume this doesn't require a backup(). */
10721 mData->mGuestPropertiesModified = FALSE;
10722#endif /* VBOX_WITH_GUEST_PROPS defined */
10723
10724 *pDbg = mHWData->mDebugging;
10725 *pAutostart = mHWData->mAutostart;
10726
10727 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10728 }
10729 catch(std::bad_alloc &)
10730 {
10731 return E_OUTOFMEMORY;
10732 }
10733
10734 AssertComRC(rc);
10735 return rc;
10736}
10737
10738/**
10739 * Saves the storage controller configuration.
10740 *
10741 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10742 */
10743HRESULT Machine::saveStorageControllers(settings::Storage &data)
10744{
10745 data.llStorageControllers.clear();
10746
10747 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10748 it != mStorageControllers->end();
10749 ++it)
10750 {
10751 HRESULT rc;
10752 ComObjPtr<StorageController> pCtl = *it;
10753
10754 settings::StorageController ctl;
10755 ctl.strName = pCtl->getName();
10756 ctl.controllerType = pCtl->getControllerType();
10757 ctl.storageBus = pCtl->getStorageBus();
10758 ctl.ulInstance = pCtl->getInstance();
10759 ctl.fBootable = pCtl->getBootable();
10760
10761 /* Save the port count. */
10762 ULONG portCount;
10763 rc = pCtl->COMGETTER(PortCount)(&portCount);
10764 ComAssertComRCRet(rc, rc);
10765 ctl.ulPortCount = portCount;
10766
10767 /* Save fUseHostIOCache */
10768 BOOL fUseHostIOCache;
10769 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10770 ComAssertComRCRet(rc, rc);
10771 ctl.fUseHostIOCache = !!fUseHostIOCache;
10772
10773 /* Save IDE emulation settings. */
10774 if (ctl.controllerType == StorageControllerType_IntelAhci)
10775 {
10776 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10777 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10778 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10779 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10780 )
10781 ComAssertComRCRet(rc, rc);
10782 }
10783
10784 /* save the devices now. */
10785 rc = saveStorageDevices(pCtl, ctl);
10786 ComAssertComRCRet(rc, rc);
10787
10788 data.llStorageControllers.push_back(ctl);
10789 }
10790
10791 return S_OK;
10792}
10793
10794/**
10795 * Saves the hard disk configuration.
10796 */
10797HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10798 settings::StorageController &data)
10799{
10800 MediaData::AttachmentList atts;
10801
10802 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10803 if (FAILED(rc)) return rc;
10804
10805 data.llAttachedDevices.clear();
10806 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10807 it != atts.end();
10808 ++it)
10809 {
10810 settings::AttachedDevice dev;
10811
10812 MediumAttachment *pAttach = *it;
10813 Medium *pMedium = pAttach->getMedium();
10814
10815 dev.deviceType = pAttach->getType();
10816 dev.lPort = pAttach->getPort();
10817 dev.lDevice = pAttach->getDevice();
10818 if (pMedium)
10819 {
10820 if (pMedium->isHostDrive())
10821 dev.strHostDriveSrc = pMedium->getLocationFull();
10822 else
10823 dev.uuid = pMedium->getId();
10824 dev.fPassThrough = pAttach->getPassthrough();
10825 dev.fTempEject = pAttach->getTempEject();
10826 dev.fNonRotational = pAttach->getNonRotational();
10827 dev.fDiscard = pAttach->getDiscard();
10828 }
10829
10830 dev.strBwGroup = pAttach->getBandwidthGroup();
10831
10832 data.llAttachedDevices.push_back(dev);
10833 }
10834
10835 return S_OK;
10836}
10837
10838/**
10839 * Saves machine state settings as defined by aFlags
10840 * (SaveSTS_* values).
10841 *
10842 * @param aFlags Combination of SaveSTS_* flags.
10843 *
10844 * @note Locks objects for writing.
10845 */
10846HRESULT Machine::saveStateSettings(int aFlags)
10847{
10848 if (aFlags == 0)
10849 return S_OK;
10850
10851 AutoCaller autoCaller(this);
10852 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10853
10854 /* This object's write lock is also necessary to serialize file access
10855 * (prevent concurrent reads and writes) */
10856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10857
10858 HRESULT rc = S_OK;
10859
10860 Assert(mData->pMachineConfigFile);
10861
10862 try
10863 {
10864 if (aFlags & SaveSTS_CurStateModified)
10865 mData->pMachineConfigFile->fCurrentStateModified = true;
10866
10867 if (aFlags & SaveSTS_StateFilePath)
10868 {
10869 if (!mSSData->strStateFilePath.isEmpty())
10870 /* try to make the file name relative to the settings file dir */
10871 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10872 else
10873 mData->pMachineConfigFile->strStateFile.setNull();
10874 }
10875
10876 if (aFlags & SaveSTS_StateTimeStamp)
10877 {
10878 Assert( mData->mMachineState != MachineState_Aborted
10879 || mSSData->strStateFilePath.isEmpty());
10880
10881 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10882
10883 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10884//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10885 }
10886
10887 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10888 }
10889 catch (...)
10890 {
10891 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10892 }
10893
10894 return rc;
10895}
10896
10897/**
10898 * Ensures that the given medium is added to a media registry. If this machine
10899 * was created with 4.0 or later, then the machine registry is used. Otherwise
10900 * the global VirtualBox media registry is used.
10901 *
10902 * Caller must NOT hold machine lock, media tree or any medium locks!
10903 *
10904 * @param pMedium
10905 */
10906void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10907{
10908 /* Paranoia checks: do not hold machine or media tree locks. */
10909 AssertReturnVoid(!isWriteLockOnCurrentThread());
10910 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10911
10912 ComObjPtr<Medium> pBase;
10913 {
10914 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10915 pBase = pMedium->getBase();
10916 }
10917
10918 /* Paranoia checks: do not hold medium locks. */
10919 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10920 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10921
10922 // decide which medium registry to use now that the medium is attached:
10923 Guid uuid;
10924 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10925 // machine XML is VirtualBox 4.0 or higher:
10926 uuid = getId(); // machine UUID
10927 else
10928 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10929
10930 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10931 mParent->markRegistryModified(uuid);
10932
10933 /* For more complex hard disk structures it can happen that the base
10934 * medium isn't yet associated with any medium registry. Do that now. */
10935 if (pMedium != pBase)
10936 {
10937 if (pBase->addRegistry(uuid, true /* fRecurse */))
10938 mParent->markRegistryModified(uuid);
10939 }
10940}
10941
10942/**
10943 * Creates differencing hard disks for all normal hard disks attached to this
10944 * machine and a new set of attachments to refer to created disks.
10945 *
10946 * Used when taking a snapshot or when deleting the current state. Gets called
10947 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10948 *
10949 * This method assumes that mMediaData contains the original hard disk attachments
10950 * it needs to create diffs for. On success, these attachments will be replaced
10951 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10952 * called to delete created diffs which will also rollback mMediaData and restore
10953 * whatever was backed up before calling this method.
10954 *
10955 * Attachments with non-normal hard disks are left as is.
10956 *
10957 * If @a aOnline is @c false then the original hard disks that require implicit
10958 * diffs will be locked for reading. Otherwise it is assumed that they are
10959 * already locked for writing (when the VM was started). Note that in the latter
10960 * case it is responsibility of the caller to lock the newly created diffs for
10961 * writing if this method succeeds.
10962 *
10963 * @param aProgress Progress object to run (must contain at least as
10964 * many operations left as the number of hard disks
10965 * attached).
10966 * @param aOnline Whether the VM was online prior to this operation.
10967 *
10968 * @note The progress object is not marked as completed, neither on success nor
10969 * on failure. This is a responsibility of the caller.
10970 *
10971 * @note Locks this object and the media tree for writing.
10972 */
10973HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10974 ULONG aWeight,
10975 bool aOnline)
10976{
10977 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10978
10979 AutoCaller autoCaller(this);
10980 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10981
10982 AutoMultiWriteLock2 alock(this->lockHandle(),
10983 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10984
10985 /* must be in a protective state because we release the lock below */
10986 AssertReturn( mData->mMachineState == MachineState_Saving
10987 || mData->mMachineState == MachineState_LiveSnapshotting
10988 || mData->mMachineState == MachineState_RestoringSnapshot
10989 || mData->mMachineState == MachineState_DeletingSnapshot
10990 , E_FAIL);
10991
10992 HRESULT rc = S_OK;
10993
10994 // use appropriate locked media map (online or offline)
10995 MediumLockListMap lockedMediaOffline;
10996 MediumLockListMap *lockedMediaMap;
10997 if (aOnline)
10998 lockedMediaMap = &mData->mSession.mLockedMedia;
10999 else
11000 lockedMediaMap = &lockedMediaOffline;
11001
11002 try
11003 {
11004 if (!aOnline)
11005 {
11006 /* lock all attached hard disks early to detect "in use"
11007 * situations before creating actual diffs */
11008 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11009 it != mMediaData->mAttachments.end();
11010 ++it)
11011 {
11012 MediumAttachment* pAtt = *it;
11013 if (pAtt->getType() == DeviceType_HardDisk)
11014 {
11015 Medium* pMedium = pAtt->getMedium();
11016 Assert(pMedium);
11017
11018 MediumLockList *pMediumLockList(new MediumLockList());
11019 alock.release();
11020 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11021 false /* fMediumLockWrite */,
11022 NULL,
11023 *pMediumLockList);
11024 alock.acquire();
11025 if (FAILED(rc))
11026 {
11027 delete pMediumLockList;
11028 throw rc;
11029 }
11030 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11031 if (FAILED(rc))
11032 {
11033 throw setError(rc,
11034 tr("Collecting locking information for all attached media failed"));
11035 }
11036 }
11037 }
11038
11039 /* Now lock all media. If this fails, nothing is locked. */
11040 alock.release();
11041 rc = lockedMediaMap->Lock();
11042 alock.acquire();
11043 if (FAILED(rc))
11044 {
11045 throw setError(rc,
11046 tr("Locking of attached media failed"));
11047 }
11048 }
11049
11050 /* remember the current list (note that we don't use backup() since
11051 * mMediaData may be already backed up) */
11052 MediaData::AttachmentList atts = mMediaData->mAttachments;
11053
11054 /* start from scratch */
11055 mMediaData->mAttachments.clear();
11056
11057 /* go through remembered attachments and create diffs for normal hard
11058 * disks and attach them */
11059 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11060 it != atts.end();
11061 ++it)
11062 {
11063 MediumAttachment* pAtt = *it;
11064
11065 DeviceType_T devType = pAtt->getType();
11066 Medium* pMedium = pAtt->getMedium();
11067
11068 if ( devType != DeviceType_HardDisk
11069 || pMedium == NULL
11070 || pMedium->getType() != MediumType_Normal)
11071 {
11072 /* copy the attachment as is */
11073
11074 /** @todo the progress object created in Console::TakeSnaphot
11075 * only expects operations for hard disks. Later other
11076 * device types need to show up in the progress as well. */
11077 if (devType == DeviceType_HardDisk)
11078 {
11079 if (pMedium == NULL)
11080 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11081 aWeight); // weight
11082 else
11083 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11084 pMedium->getBase()->getName().c_str()).raw(),
11085 aWeight); // weight
11086 }
11087
11088 mMediaData->mAttachments.push_back(pAtt);
11089 continue;
11090 }
11091
11092 /* need a diff */
11093 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11094 pMedium->getBase()->getName().c_str()).raw(),
11095 aWeight); // weight
11096
11097 Utf8Str strFullSnapshotFolder;
11098 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11099
11100 ComObjPtr<Medium> diff;
11101 diff.createObject();
11102 // store the diff in the same registry as the parent
11103 // (this cannot fail here because we can't create implicit diffs for
11104 // unregistered images)
11105 Guid uuidRegistryParent;
11106 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
11107 Assert(fInRegistry); NOREF(fInRegistry);
11108 rc = diff->init(mParent,
11109 pMedium->getPreferredDiffFormat(),
11110 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11111 uuidRegistryParent);
11112 if (FAILED(rc)) throw rc;
11113
11114 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11115 * the push_back? Looks like we're going to release medium with the
11116 * wrong kind of lock (general issue with if we fail anywhere at all)
11117 * and an orphaned VDI in the snapshots folder. */
11118
11119 /* update the appropriate lock list */
11120 MediumLockList *pMediumLockList;
11121 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11122 AssertComRCThrowRC(rc);
11123 if (aOnline)
11124 {
11125 alock.release();
11126 /* The currently attached medium will be read-only, change
11127 * the lock type to read. */
11128 rc = pMediumLockList->Update(pMedium, false);
11129 alock.acquire();
11130 AssertComRCThrowRC(rc);
11131 }
11132
11133 /* release the locks before the potentially lengthy operation */
11134 alock.release();
11135 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
11136 pMediumLockList,
11137 NULL /* aProgress */,
11138 true /* aWait */);
11139 alock.acquire();
11140 if (FAILED(rc)) throw rc;
11141
11142 /* actual lock list update is done in Medium::commitMedia */
11143
11144 rc = diff->addBackReference(mData->mUuid);
11145 AssertComRCThrowRC(rc);
11146
11147 /* add a new attachment */
11148 ComObjPtr<MediumAttachment> attachment;
11149 attachment.createObject();
11150 rc = attachment->init(this,
11151 diff,
11152 pAtt->getControllerName(),
11153 pAtt->getPort(),
11154 pAtt->getDevice(),
11155 DeviceType_HardDisk,
11156 true /* aImplicit */,
11157 false /* aPassthrough */,
11158 false /* aTempEject */,
11159 pAtt->getNonRotational(),
11160 pAtt->getDiscard(),
11161 pAtt->getBandwidthGroup());
11162 if (FAILED(rc)) throw rc;
11163
11164 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11165 AssertComRCThrowRC(rc);
11166 mMediaData->mAttachments.push_back(attachment);
11167 }
11168 }
11169 catch (HRESULT aRC) { rc = aRC; }
11170
11171 /* unlock all hard disks we locked when there is no VM */
11172 if (!aOnline)
11173 {
11174 ErrorInfoKeeper eik;
11175
11176 HRESULT rc1 = lockedMediaMap->Clear();
11177 AssertComRC(rc1);
11178 }
11179
11180 return rc;
11181}
11182
11183/**
11184 * Deletes implicit differencing hard disks created either by
11185 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11186 *
11187 * Note that to delete hard disks created by #AttachDevice() this method is
11188 * called from #fixupMedia() when the changes are rolled back.
11189 *
11190 * @note Locks this object and the media tree for writing.
11191 */
11192HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11193{
11194 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11195
11196 AutoCaller autoCaller(this);
11197 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11198
11199 AutoMultiWriteLock2 alock(this->lockHandle(),
11200 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11201
11202 /* We absolutely must have backed up state. */
11203 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11204
11205 /* Check if there are any implicitly created diff images. */
11206 bool fImplicitDiffs = false;
11207 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11208 it != mMediaData->mAttachments.end();
11209 ++it)
11210 {
11211 const ComObjPtr<MediumAttachment> &pAtt = *it;
11212 if (pAtt->isImplicit())
11213 {
11214 fImplicitDiffs = true;
11215 break;
11216 }
11217 }
11218 /* If there is nothing to do, leave early. This saves lots of image locking
11219 * effort. It also avoids a MachineStateChanged event without real reason.
11220 * This is important e.g. when loading a VM config, because there should be
11221 * no events. Otherwise API clients can become thoroughly confused for
11222 * inaccessible VMs (the code for loading VM configs uses this method for
11223 * cleanup if the config makes no sense), as they take such events as an
11224 * indication that the VM is alive, and they would force the VM config to
11225 * be reread, leading to an endless loop. */
11226 if (!fImplicitDiffs)
11227 return S_OK;
11228
11229 HRESULT rc = S_OK;
11230 MachineState_T oldState = mData->mMachineState;
11231
11232 /* will release the lock before the potentially lengthy operation,
11233 * so protect with the special state (unless already protected) */
11234 if ( oldState != MachineState_Saving
11235 && oldState != MachineState_LiveSnapshotting
11236 && oldState != MachineState_RestoringSnapshot
11237 && oldState != MachineState_DeletingSnapshot
11238 && oldState != MachineState_DeletingSnapshotOnline
11239 && oldState != MachineState_DeletingSnapshotPaused
11240 )
11241 setMachineState(MachineState_SettingUp);
11242
11243 // use appropriate locked media map (online or offline)
11244 MediumLockListMap lockedMediaOffline;
11245 MediumLockListMap *lockedMediaMap;
11246 if (aOnline)
11247 lockedMediaMap = &mData->mSession.mLockedMedia;
11248 else
11249 lockedMediaMap = &lockedMediaOffline;
11250
11251 try
11252 {
11253 if (!aOnline)
11254 {
11255 /* lock all attached hard disks early to detect "in use"
11256 * situations before deleting actual diffs */
11257 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11258 it != mMediaData->mAttachments.end();
11259 ++it)
11260 {
11261 MediumAttachment* pAtt = *it;
11262 if (pAtt->getType() == DeviceType_HardDisk)
11263 {
11264 Medium* pMedium = pAtt->getMedium();
11265 Assert(pMedium);
11266
11267 MediumLockList *pMediumLockList(new MediumLockList());
11268 alock.release();
11269 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11270 false /* fMediumLockWrite */,
11271 NULL,
11272 *pMediumLockList);
11273 alock.acquire();
11274
11275 if (FAILED(rc))
11276 {
11277 delete pMediumLockList;
11278 throw rc;
11279 }
11280
11281 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11282 if (FAILED(rc))
11283 throw rc;
11284 }
11285 }
11286
11287 if (FAILED(rc))
11288 throw rc;
11289 } // end of offline
11290
11291 /* Lock lists are now up to date and include implicitly created media */
11292
11293 /* Go through remembered attachments and delete all implicitly created
11294 * diffs and fix up the attachment information */
11295 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11296 MediaData::AttachmentList implicitAtts;
11297 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11298 it != mMediaData->mAttachments.end();
11299 ++it)
11300 {
11301 ComObjPtr<MediumAttachment> pAtt = *it;
11302 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11303 if (pMedium.isNull())
11304 continue;
11305
11306 // Implicit attachments go on the list for deletion and back references are removed.
11307 if (pAtt->isImplicit())
11308 {
11309 /* Deassociate and mark for deletion */
11310 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11311 rc = pMedium->removeBackReference(mData->mUuid);
11312 if (FAILED(rc))
11313 throw rc;
11314 implicitAtts.push_back(pAtt);
11315 continue;
11316 }
11317
11318 /* Was this medium attached before? */
11319 if (!findAttachment(oldAtts, pMedium))
11320 {
11321 /* no: de-associate */
11322 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11323 rc = pMedium->removeBackReference(mData->mUuid);
11324 if (FAILED(rc))
11325 throw rc;
11326 continue;
11327 }
11328 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11329 }
11330
11331 /* If there are implicit attachments to delete, throw away the lock
11332 * map contents (which will unlock all media) since the medium
11333 * attachments will be rolled back. Below we need to completely
11334 * recreate the lock map anyway since it is infinitely complex to
11335 * do this incrementally (would need reconstructing each attachment
11336 * change, which would be extremely hairy). */
11337 if (implicitAtts.size() != 0)
11338 {
11339 ErrorInfoKeeper eik;
11340
11341 HRESULT rc1 = lockedMediaMap->Clear();
11342 AssertComRC(rc1);
11343 }
11344
11345 /* rollback hard disk changes */
11346 mMediaData.rollback();
11347
11348 MultiResult mrc(S_OK);
11349
11350 // Delete unused implicit diffs.
11351 if (implicitAtts.size() != 0)
11352 {
11353 alock.release();
11354
11355 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11356 it != implicitAtts.end();
11357 ++it)
11358 {
11359 // Remove medium associated with this attachment.
11360 ComObjPtr<MediumAttachment> pAtt = *it;
11361 Assert(pAtt);
11362 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11363 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11364 Assert(pMedium);
11365
11366 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11367 // continue on delete failure, just collect error messages
11368 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11369 mrc = rc;
11370 }
11371
11372 alock.acquire();
11373
11374 /* if there is a VM recreate media lock map as mentioned above,
11375 * otherwise it is a waste of time and we leave things unlocked */
11376 if (aOnline)
11377 {
11378 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11379 /* must never be NULL, but better safe than sorry */
11380 if (!pMachine.isNull())
11381 {
11382 alock.release();
11383 rc = mData->mSession.mMachine->lockMedia();
11384 alock.acquire();
11385 if (FAILED(rc))
11386 throw rc;
11387 }
11388 }
11389 }
11390 }
11391 catch (HRESULT aRC) {rc = aRC;}
11392
11393 if (mData->mMachineState == MachineState_SettingUp)
11394 setMachineState(oldState);
11395
11396 /* unlock all hard disks we locked when there is no VM */
11397 if (!aOnline)
11398 {
11399 ErrorInfoKeeper eik;
11400
11401 HRESULT rc1 = lockedMediaMap->Clear();
11402 AssertComRC(rc1);
11403 }
11404
11405 return rc;
11406}
11407
11408
11409/**
11410 * Looks through the given list of media attachments for one with the given parameters
11411 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11412 * can be searched as well if needed.
11413 *
11414 * @param list
11415 * @param aControllerName
11416 * @param aControllerPort
11417 * @param aDevice
11418 * @return
11419 */
11420MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11421 IN_BSTR aControllerName,
11422 LONG aControllerPort,
11423 LONG aDevice)
11424{
11425 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11426 it != ll.end();
11427 ++it)
11428 {
11429 MediumAttachment *pAttach = *it;
11430 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11431 return pAttach;
11432 }
11433
11434 return NULL;
11435}
11436
11437/**
11438 * Looks through the given list of media attachments for one with the given parameters
11439 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11440 * can be searched as well if needed.
11441 *
11442 * @param list
11443 * @param aControllerName
11444 * @param aControllerPort
11445 * @param aDevice
11446 * @return
11447 */
11448MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11449 ComObjPtr<Medium> pMedium)
11450{
11451 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11452 it != ll.end();
11453 ++it)
11454 {
11455 MediumAttachment *pAttach = *it;
11456 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11457 if (pMediumThis == pMedium)
11458 return pAttach;
11459 }
11460
11461 return NULL;
11462}
11463
11464/**
11465 * Looks through the given list of media attachments for one with the given parameters
11466 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11467 * can be searched as well if needed.
11468 *
11469 * @param list
11470 * @param aControllerName
11471 * @param aControllerPort
11472 * @param aDevice
11473 * @return
11474 */
11475MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11476 Guid &id)
11477{
11478 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11479 it != ll.end();
11480 ++it)
11481 {
11482 MediumAttachment *pAttach = *it;
11483 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11484 if (pMediumThis->getId() == id)
11485 return pAttach;
11486 }
11487
11488 return NULL;
11489}
11490
11491/**
11492 * Main implementation for Machine::DetachDevice. This also gets called
11493 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11494 *
11495 * @param pAttach Medium attachment to detach.
11496 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11497 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11498 * @return
11499 */
11500HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11501 AutoWriteLock &writeLock,
11502 Snapshot *pSnapshot)
11503{
11504 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11505 DeviceType_T mediumType = pAttach->getType();
11506
11507 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11508
11509 if (pAttach->isImplicit())
11510 {
11511 /* attempt to implicitly delete the implicitly created diff */
11512
11513 /// @todo move the implicit flag from MediumAttachment to Medium
11514 /// and forbid any hard disk operation when it is implicit. Or maybe
11515 /// a special media state for it to make it even more simple.
11516
11517 Assert(mMediaData.isBackedUp());
11518
11519 /* will release the lock before the potentially lengthy operation, so
11520 * protect with the special state */
11521 MachineState_T oldState = mData->mMachineState;
11522 setMachineState(MachineState_SettingUp);
11523
11524 writeLock.release();
11525
11526 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11527 true /*aWait*/);
11528
11529 writeLock.acquire();
11530
11531 setMachineState(oldState);
11532
11533 if (FAILED(rc)) return rc;
11534 }
11535
11536 setModified(IsModified_Storage);
11537 mMediaData.backup();
11538 mMediaData->mAttachments.remove(pAttach);
11539
11540 if (!oldmedium.isNull())
11541 {
11542 // if this is from a snapshot, do not defer detachment to commitMedia()
11543 if (pSnapshot)
11544 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11545 // else if non-hard disk media, do not defer detachment to commitMedia() either
11546 else if (mediumType != DeviceType_HardDisk)
11547 oldmedium->removeBackReference(mData->mUuid);
11548 }
11549
11550 return S_OK;
11551}
11552
11553/**
11554 * Goes thru all media of the given list and
11555 *
11556 * 1) calls detachDevice() on each of them for this machine and
11557 * 2) adds all Medium objects found in the process to the given list,
11558 * depending on cleanupMode.
11559 *
11560 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11561 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11562 * media to the list.
11563 *
11564 * This gets called from Machine::Unregister, both for the actual Machine and
11565 * the SnapshotMachine objects that might be found in the snapshots.
11566 *
11567 * Requires caller and locking. The machine lock must be passed in because it
11568 * will be passed on to detachDevice which needs it for temporary unlocking.
11569 *
11570 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11571 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11572 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11573 * otherwise no media get added.
11574 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11575 * @return
11576 */
11577HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11578 Snapshot *pSnapshot,
11579 CleanupMode_T cleanupMode,
11580 MediaList &llMedia)
11581{
11582 Assert(isWriteLockOnCurrentThread());
11583
11584 HRESULT rc;
11585
11586 // make a temporary list because detachDevice invalidates iterators into
11587 // mMediaData->mAttachments
11588 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11589
11590 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11591 it != llAttachments2.end();
11592 ++it)
11593 {
11594 ComObjPtr<MediumAttachment> &pAttach = *it;
11595 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11596
11597 if (!pMedium.isNull())
11598 {
11599 AutoCaller mac(pMedium);
11600 if (FAILED(mac.rc())) return mac.rc();
11601 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11602 DeviceType_T devType = pMedium->getDeviceType();
11603 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11604 && devType == DeviceType_HardDisk)
11605 || (cleanupMode == CleanupMode_Full)
11606 )
11607 {
11608 llMedia.push_back(pMedium);
11609 ComObjPtr<Medium> pParent = pMedium->getParent();
11610 /*
11611 * Search for medias which are not attached to any machine, but
11612 * in the chain to an attached disk. Mediums are only consided
11613 * if they are:
11614 * - have only one child
11615 * - no references to any machines
11616 * - are of normal medium type
11617 */
11618 while (!pParent.isNull())
11619 {
11620 AutoCaller mac1(pParent);
11621 if (FAILED(mac1.rc())) return mac1.rc();
11622 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11623 if (pParent->getChildren().size() == 1)
11624 {
11625 if ( pParent->getMachineBackRefCount() == 0
11626 && pParent->getType() == MediumType_Normal
11627 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11628 llMedia.push_back(pParent);
11629 }
11630 else
11631 break;
11632 pParent = pParent->getParent();
11633 }
11634 }
11635 }
11636
11637 // real machine: then we need to use the proper method
11638 rc = detachDevice(pAttach, writeLock, pSnapshot);
11639
11640 if (FAILED(rc))
11641 return rc;
11642 }
11643
11644 return S_OK;
11645}
11646
11647/**
11648 * Perform deferred hard disk detachments.
11649 *
11650 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11651 * backed up).
11652 *
11653 * If @a aOnline is @c true then this method will also unlock the old hard disks
11654 * for which the new implicit diffs were created and will lock these new diffs for
11655 * writing.
11656 *
11657 * @param aOnline Whether the VM was online prior to this operation.
11658 *
11659 * @note Locks this object for writing!
11660 */
11661void Machine::commitMedia(bool aOnline /*= false*/)
11662{
11663 AutoCaller autoCaller(this);
11664 AssertComRCReturnVoid(autoCaller.rc());
11665
11666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11667
11668 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11669
11670 HRESULT rc = S_OK;
11671
11672 /* no attach/detach operations -- nothing to do */
11673 if (!mMediaData.isBackedUp())
11674 return;
11675
11676 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11677 bool fMediaNeedsLocking = false;
11678
11679 /* enumerate new attachments */
11680 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11681 it != mMediaData->mAttachments.end();
11682 ++it)
11683 {
11684 MediumAttachment *pAttach = *it;
11685
11686 pAttach->commit();
11687
11688 Medium* pMedium = pAttach->getMedium();
11689 bool fImplicit = pAttach->isImplicit();
11690
11691 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11692 (pMedium) ? pMedium->getName().c_str() : "NULL",
11693 fImplicit));
11694
11695 /** @todo convert all this Machine-based voodoo to MediumAttachment
11696 * based commit logic. */
11697 if (fImplicit)
11698 {
11699 /* convert implicit attachment to normal */
11700 pAttach->setImplicit(false);
11701
11702 if ( aOnline
11703 && pMedium
11704 && pAttach->getType() == DeviceType_HardDisk
11705 )
11706 {
11707 ComObjPtr<Medium> parent = pMedium->getParent();
11708 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11709
11710 /* update the appropriate lock list */
11711 MediumLockList *pMediumLockList;
11712 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11713 AssertComRC(rc);
11714 if (pMediumLockList)
11715 {
11716 /* unlock if there's a need to change the locking */
11717 if (!fMediaNeedsLocking)
11718 {
11719 rc = mData->mSession.mLockedMedia.Unlock();
11720 AssertComRC(rc);
11721 fMediaNeedsLocking = true;
11722 }
11723 rc = pMediumLockList->Update(parent, false);
11724 AssertComRC(rc);
11725 rc = pMediumLockList->Append(pMedium, true);
11726 AssertComRC(rc);
11727 }
11728 }
11729
11730 continue;
11731 }
11732
11733 if (pMedium)
11734 {
11735 /* was this medium attached before? */
11736 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11737 oldIt != oldAtts.end();
11738 ++oldIt)
11739 {
11740 MediumAttachment *pOldAttach = *oldIt;
11741 if (pOldAttach->getMedium() == pMedium)
11742 {
11743 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11744
11745 /* yes: remove from old to avoid de-association */
11746 oldAtts.erase(oldIt);
11747 break;
11748 }
11749 }
11750 }
11751 }
11752
11753 /* enumerate remaining old attachments and de-associate from the
11754 * current machine state */
11755 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11756 it != oldAtts.end();
11757 ++it)
11758 {
11759 MediumAttachment *pAttach = *it;
11760 Medium* pMedium = pAttach->getMedium();
11761
11762 /* Detach only hard disks, since DVD/floppy media is detached
11763 * instantly in MountMedium. */
11764 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11765 {
11766 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11767
11768 /* now de-associate from the current machine state */
11769 rc = pMedium->removeBackReference(mData->mUuid);
11770 AssertComRC(rc);
11771
11772 if (aOnline)
11773 {
11774 /* unlock since medium is not used anymore */
11775 MediumLockList *pMediumLockList;
11776 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11777 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11778 {
11779 /* this happens for online snapshots, there the attachment
11780 * is changing, but only to a diff image created under
11781 * the old one, so there is no separate lock list */
11782 Assert(!pMediumLockList);
11783 }
11784 else
11785 {
11786 AssertComRC(rc);
11787 if (pMediumLockList)
11788 {
11789 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11790 AssertComRC(rc);
11791 }
11792 }
11793 }
11794 }
11795 }
11796
11797 /* take media locks again so that the locking state is consistent */
11798 if (fMediaNeedsLocking)
11799 {
11800 Assert(aOnline);
11801 rc = mData->mSession.mLockedMedia.Lock();
11802 AssertComRC(rc);
11803 }
11804
11805 /* commit the hard disk changes */
11806 mMediaData.commit();
11807
11808 if (isSessionMachine())
11809 {
11810 /*
11811 * Update the parent machine to point to the new owner.
11812 * This is necessary because the stored parent will point to the
11813 * session machine otherwise and cause crashes or errors later
11814 * when the session machine gets invalid.
11815 */
11816 /** @todo Change the MediumAttachment class to behave like any other
11817 * class in this regard by creating peer MediumAttachment
11818 * objects for session machines and share the data with the peer
11819 * machine.
11820 */
11821 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11822 it != mMediaData->mAttachments.end();
11823 ++it)
11824 {
11825 (*it)->updateParentMachine(mPeer);
11826 }
11827
11828 /* attach new data to the primary machine and reshare it */
11829 mPeer->mMediaData.attach(mMediaData);
11830 }
11831
11832 return;
11833}
11834
11835/**
11836 * Perform deferred deletion of implicitly created diffs.
11837 *
11838 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11839 * backed up).
11840 *
11841 * @note Locks this object for writing!
11842 */
11843void Machine::rollbackMedia()
11844{
11845 AutoCaller autoCaller(this);
11846 AssertComRCReturnVoid(autoCaller.rc());
11847
11848 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11849 LogFlowThisFunc(("Entering rollbackMedia\n"));
11850
11851 HRESULT rc = S_OK;
11852
11853 /* no attach/detach operations -- nothing to do */
11854 if (!mMediaData.isBackedUp())
11855 return;
11856
11857 /* enumerate new attachments */
11858 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11859 it != mMediaData->mAttachments.end();
11860 ++it)
11861 {
11862 MediumAttachment *pAttach = *it;
11863 /* Fix up the backrefs for DVD/floppy media. */
11864 if (pAttach->getType() != DeviceType_HardDisk)
11865 {
11866 Medium* pMedium = pAttach->getMedium();
11867 if (pMedium)
11868 {
11869 rc = pMedium->removeBackReference(mData->mUuid);
11870 AssertComRC(rc);
11871 }
11872 }
11873
11874 (*it)->rollback();
11875
11876 pAttach = *it;
11877 /* Fix up the backrefs for DVD/floppy media. */
11878 if (pAttach->getType() != DeviceType_HardDisk)
11879 {
11880 Medium* pMedium = pAttach->getMedium();
11881 if (pMedium)
11882 {
11883 rc = pMedium->addBackReference(mData->mUuid);
11884 AssertComRC(rc);
11885 }
11886 }
11887 }
11888
11889 /** @todo convert all this Machine-based voodoo to MediumAttachment
11890 * based rollback logic. */
11891 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11892
11893 return;
11894}
11895
11896/**
11897 * Returns true if the settings file is located in the directory named exactly
11898 * as the machine; this means, among other things, that the machine directory
11899 * should be auto-renamed.
11900 *
11901 * @param aSettingsDir if not NULL, the full machine settings file directory
11902 * name will be assigned there.
11903 *
11904 * @note Doesn't lock anything.
11905 * @note Not thread safe (must be called from this object's lock).
11906 */
11907bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11908{
11909 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11910 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11911 if (aSettingsDir)
11912 *aSettingsDir = strMachineDirName;
11913 strMachineDirName.stripPath(); // vmname
11914 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11915 strConfigFileOnly.stripPath() // vmname.vbox
11916 .stripExt(); // vmname
11917 /** @todo hack, make somehow use of ComposeMachineFilename */
11918 if (mUserData->s.fDirectoryIncludesUUID)
11919 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11920
11921 AssertReturn(!strMachineDirName.isEmpty(), false);
11922 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11923
11924 return strMachineDirName == strConfigFileOnly;
11925}
11926
11927/**
11928 * Discards all changes to machine settings.
11929 *
11930 * @param aNotify Whether to notify the direct session about changes or not.
11931 *
11932 * @note Locks objects for writing!
11933 */
11934void Machine::rollback(bool aNotify)
11935{
11936 AutoCaller autoCaller(this);
11937 AssertComRCReturn(autoCaller.rc(), (void)0);
11938
11939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11940
11941 if (!mStorageControllers.isNull())
11942 {
11943 if (mStorageControllers.isBackedUp())
11944 {
11945 /* unitialize all new devices (absent in the backed up list). */
11946 StorageControllerList::const_iterator it = mStorageControllers->begin();
11947 StorageControllerList *backedList = mStorageControllers.backedUpData();
11948 while (it != mStorageControllers->end())
11949 {
11950 if ( std::find(backedList->begin(), backedList->end(), *it)
11951 == backedList->end()
11952 )
11953 {
11954 (*it)->uninit();
11955 }
11956 ++it;
11957 }
11958
11959 /* restore the list */
11960 mStorageControllers.rollback();
11961 }
11962
11963 /* rollback any changes to devices after restoring the list */
11964 if (mData->flModifications & IsModified_Storage)
11965 {
11966 StorageControllerList::const_iterator it = mStorageControllers->begin();
11967 while (it != mStorageControllers->end())
11968 {
11969 (*it)->rollback();
11970 ++it;
11971 }
11972 }
11973 }
11974
11975 if (!mUSBControllers.isNull())
11976 {
11977 if (mUSBControllers.isBackedUp())
11978 {
11979 /* unitialize all new devices (absent in the backed up list). */
11980 USBControllerList::const_iterator it = mUSBControllers->begin();
11981 USBControllerList *backedList = mUSBControllers.backedUpData();
11982 while (it != mUSBControllers->end())
11983 {
11984 if ( std::find(backedList->begin(), backedList->end(), *it)
11985 == backedList->end()
11986 )
11987 {
11988 (*it)->uninit();
11989 }
11990 ++it;
11991 }
11992
11993 /* restore the list */
11994 mUSBControllers.rollback();
11995 }
11996
11997 /* rollback any changes to devices after restoring the list */
11998 if (mData->flModifications & IsModified_USB)
11999 {
12000 USBControllerList::const_iterator it = mUSBControllers->begin();
12001 while (it != mUSBControllers->end())
12002 {
12003 (*it)->rollback();
12004 ++it;
12005 }
12006 }
12007 }
12008
12009 mUserData.rollback();
12010
12011 mHWData.rollback();
12012
12013 if (mData->flModifications & IsModified_Storage)
12014 rollbackMedia();
12015
12016 if (mBIOSSettings)
12017 mBIOSSettings->rollback();
12018
12019 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12020 mVRDEServer->rollback();
12021
12022 if (mAudioAdapter)
12023 mAudioAdapter->rollback();
12024
12025 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12026 mUSBDeviceFilters->rollback();
12027
12028 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12029 mBandwidthControl->rollback();
12030
12031 if (!mHWData.isNull())
12032 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12033 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12034 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12035 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12036
12037 if (mData->flModifications & IsModified_NetworkAdapters)
12038 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12039 if ( mNetworkAdapters[slot]
12040 && mNetworkAdapters[slot]->isModified())
12041 {
12042 mNetworkAdapters[slot]->rollback();
12043 networkAdapters[slot] = mNetworkAdapters[slot];
12044 }
12045
12046 if (mData->flModifications & IsModified_SerialPorts)
12047 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12048 if ( mSerialPorts[slot]
12049 && mSerialPorts[slot]->isModified())
12050 {
12051 mSerialPorts[slot]->rollback();
12052 serialPorts[slot] = mSerialPorts[slot];
12053 }
12054
12055 if (mData->flModifications & IsModified_ParallelPorts)
12056 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12057 if ( mParallelPorts[slot]
12058 && mParallelPorts[slot]->isModified())
12059 {
12060 mParallelPorts[slot]->rollback();
12061 parallelPorts[slot] = mParallelPorts[slot];
12062 }
12063
12064 if (aNotify)
12065 {
12066 /* inform the direct session about changes */
12067
12068 ComObjPtr<Machine> that = this;
12069 uint32_t flModifications = mData->flModifications;
12070 alock.release();
12071
12072 if (flModifications & IsModified_SharedFolders)
12073 that->onSharedFolderChange();
12074
12075 if (flModifications & IsModified_VRDEServer)
12076 that->onVRDEServerChange(/* aRestart */ TRUE);
12077 if (flModifications & IsModified_USB)
12078 that->onUSBControllerChange();
12079
12080 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12081 if (networkAdapters[slot])
12082 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12083 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12084 if (serialPorts[slot])
12085 that->onSerialPortChange(serialPorts[slot]);
12086 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12087 if (parallelPorts[slot])
12088 that->onParallelPortChange(parallelPorts[slot]);
12089
12090 if (flModifications & IsModified_Storage)
12091 that->onStorageControllerChange();
12092
12093#if 0
12094 if (flModifications & IsModified_BandwidthControl)
12095 that->onBandwidthControlChange();
12096#endif
12097 }
12098}
12099
12100/**
12101 * Commits all the changes to machine settings.
12102 *
12103 * Note that this operation is supposed to never fail.
12104 *
12105 * @note Locks this object and children for writing.
12106 */
12107void Machine::commit()
12108{
12109 AutoCaller autoCaller(this);
12110 AssertComRCReturnVoid(autoCaller.rc());
12111
12112 AutoCaller peerCaller(mPeer);
12113 AssertComRCReturnVoid(peerCaller.rc());
12114
12115 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12116
12117 /*
12118 * use safe commit to ensure Snapshot machines (that share mUserData)
12119 * will still refer to a valid memory location
12120 */
12121 mUserData.commitCopy();
12122
12123 mHWData.commit();
12124
12125 if (mMediaData.isBackedUp())
12126 commitMedia(Global::IsOnline(mData->mMachineState));
12127
12128 mBIOSSettings->commit();
12129 mVRDEServer->commit();
12130 mAudioAdapter->commit();
12131 mUSBDeviceFilters->commit();
12132 mBandwidthControl->commit();
12133
12134 /* Since mNetworkAdapters is a list which might have been changed (resized)
12135 * without using the Backupable<> template we need to handle the copying
12136 * of the list entries manually, including the creation of peers for the
12137 * new objects. */
12138 bool commitNetworkAdapters = false;
12139 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12140 if (mPeer)
12141 {
12142 /* commit everything, even the ones which will go away */
12143 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12144 mNetworkAdapters[slot]->commit();
12145 /* copy over the new entries, creating a peer and uninit the original */
12146 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12147 for (size_t slot = 0; slot < newSize; slot++)
12148 {
12149 /* look if this adapter has a peer device */
12150 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
12151 if (!peer)
12152 {
12153 /* no peer means the adapter is a newly created one;
12154 * create a peer owning data this data share it with */
12155 peer.createObject();
12156 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12157 }
12158 mPeer->mNetworkAdapters[slot] = peer;
12159 }
12160 /* uninit any no longer needed network adapters */
12161 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12162 mNetworkAdapters[slot]->uninit();
12163 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12164 {
12165 if (mPeer->mNetworkAdapters[slot])
12166 mPeer->mNetworkAdapters[slot]->uninit();
12167 }
12168 /* Keep the original network adapter count until this point, so that
12169 * discarding a chipset type change will not lose settings. */
12170 mNetworkAdapters.resize(newSize);
12171 mPeer->mNetworkAdapters.resize(newSize);
12172 }
12173 else
12174 {
12175 /* we have no peer (our parent is the newly created machine);
12176 * just commit changes to the network adapters */
12177 commitNetworkAdapters = true;
12178 }
12179 if (commitNetworkAdapters)
12180 {
12181 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12182 mNetworkAdapters[slot]->commit();
12183 }
12184
12185 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12186 mSerialPorts[slot]->commit();
12187 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12188 mParallelPorts[slot]->commit();
12189
12190 bool commitStorageControllers = false;
12191
12192 if (mStorageControllers.isBackedUp())
12193 {
12194 mStorageControllers.commit();
12195
12196 if (mPeer)
12197 {
12198 /* Commit all changes to new controllers (this will reshare data with
12199 * peers for those who have peers) */
12200 StorageControllerList *newList = new StorageControllerList();
12201 StorageControllerList::const_iterator it = mStorageControllers->begin();
12202 while (it != mStorageControllers->end())
12203 {
12204 (*it)->commit();
12205
12206 /* look if this controller has a peer device */
12207 ComObjPtr<StorageController> peer = (*it)->getPeer();
12208 if (!peer)
12209 {
12210 /* no peer means the device is a newly created one;
12211 * create a peer owning data this device share it with */
12212 peer.createObject();
12213 peer->init(mPeer, *it, true /* aReshare */);
12214 }
12215 else
12216 {
12217 /* remove peer from the old list */
12218 mPeer->mStorageControllers->remove(peer);
12219 }
12220 /* and add it to the new list */
12221 newList->push_back(peer);
12222
12223 ++it;
12224 }
12225
12226 /* uninit old peer's controllers that are left */
12227 it = mPeer->mStorageControllers->begin();
12228 while (it != mPeer->mStorageControllers->end())
12229 {
12230 (*it)->uninit();
12231 ++it;
12232 }
12233
12234 /* attach new list of controllers to our peer */
12235 mPeer->mStorageControllers.attach(newList);
12236 }
12237 else
12238 {
12239 /* we have no peer (our parent is the newly created machine);
12240 * just commit changes to devices */
12241 commitStorageControllers = true;
12242 }
12243 }
12244 else
12245 {
12246 /* the list of controllers itself is not changed,
12247 * just commit changes to controllers themselves */
12248 commitStorageControllers = true;
12249 }
12250
12251 if (commitStorageControllers)
12252 {
12253 StorageControllerList::const_iterator it = mStorageControllers->begin();
12254 while (it != mStorageControllers->end())
12255 {
12256 (*it)->commit();
12257 ++it;
12258 }
12259 }
12260
12261 bool commitUSBControllers = false;
12262
12263 if (mUSBControllers.isBackedUp())
12264 {
12265 mUSBControllers.commit();
12266
12267 if (mPeer)
12268 {
12269 /* Commit all changes to new controllers (this will reshare data with
12270 * peers for those who have peers) */
12271 USBControllerList *newList = new USBControllerList();
12272 USBControllerList::const_iterator it = mUSBControllers->begin();
12273 while (it != mUSBControllers->end())
12274 {
12275 (*it)->commit();
12276
12277 /* look if this controller has a peer device */
12278 ComObjPtr<USBController> peer = (*it)->getPeer();
12279 if (!peer)
12280 {
12281 /* no peer means the device is a newly created one;
12282 * create a peer owning data this device share it with */
12283 peer.createObject();
12284 peer->init(mPeer, *it, true /* aReshare */);
12285 }
12286 else
12287 {
12288 /* remove peer from the old list */
12289 mPeer->mUSBControllers->remove(peer);
12290 }
12291 /* and add it to the new list */
12292 newList->push_back(peer);
12293
12294 ++it;
12295 }
12296
12297 /* uninit old peer's controllers that are left */
12298 it = mPeer->mUSBControllers->begin();
12299 while (it != mPeer->mUSBControllers->end())
12300 {
12301 (*it)->uninit();
12302 ++it;
12303 }
12304
12305 /* attach new list of controllers to our peer */
12306 mPeer->mUSBControllers.attach(newList);
12307 }
12308 else
12309 {
12310 /* we have no peer (our parent is the newly created machine);
12311 * just commit changes to devices */
12312 commitUSBControllers = true;
12313 }
12314 }
12315 else
12316 {
12317 /* the list of controllers itself is not changed,
12318 * just commit changes to controllers themselves */
12319 commitUSBControllers = true;
12320 }
12321
12322 if (commitUSBControllers)
12323 {
12324 USBControllerList::const_iterator it = mUSBControllers->begin();
12325 while (it != mUSBControllers->end())
12326 {
12327 (*it)->commit();
12328 ++it;
12329 }
12330 }
12331
12332 if (isSessionMachine())
12333 {
12334 /* attach new data to the primary machine and reshare it */
12335 mPeer->mUserData.attach(mUserData);
12336 mPeer->mHWData.attach(mHWData);
12337 /* mMediaData is reshared by fixupMedia */
12338 // mPeer->mMediaData.attach(mMediaData);
12339 Assert(mPeer->mMediaData.data() == mMediaData.data());
12340 }
12341}
12342
12343/**
12344 * Copies all the hardware data from the given machine.
12345 *
12346 * Currently, only called when the VM is being restored from a snapshot. In
12347 * particular, this implies that the VM is not running during this method's
12348 * call.
12349 *
12350 * @note This method must be called from under this object's lock.
12351 *
12352 * @note This method doesn't call #commit(), so all data remains backed up and
12353 * unsaved.
12354 */
12355void Machine::copyFrom(Machine *aThat)
12356{
12357 AssertReturnVoid(!isSnapshotMachine());
12358 AssertReturnVoid(aThat->isSnapshotMachine());
12359
12360 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12361
12362 mHWData.assignCopy(aThat->mHWData);
12363
12364 // create copies of all shared folders (mHWData after attaching a copy
12365 // contains just references to original objects)
12366 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12367 it != mHWData->mSharedFolders.end();
12368 ++it)
12369 {
12370 ComObjPtr<SharedFolder> folder;
12371 folder.createObject();
12372 HRESULT rc = folder->initCopy(getMachine(), *it);
12373 AssertComRC(rc);
12374 *it = folder;
12375 }
12376
12377 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12378 mVRDEServer->copyFrom(aThat->mVRDEServer);
12379 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12380 mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters);
12381 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12382
12383 /* create private copies of all controllers */
12384 mStorageControllers.backup();
12385 mStorageControllers->clear();
12386 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12387 it != aThat->mStorageControllers->end();
12388 ++it)
12389 {
12390 ComObjPtr<StorageController> ctrl;
12391 ctrl.createObject();
12392 ctrl->initCopy(this, *it);
12393 mStorageControllers->push_back(ctrl);
12394 }
12395
12396 /* create private copies of all USB controllers */
12397 mUSBControllers.backup();
12398 mUSBControllers->clear();
12399 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12400 it != aThat->mUSBControllers->end();
12401 ++it)
12402 {
12403 ComObjPtr<USBController> ctrl;
12404 ctrl.createObject();
12405 ctrl->initCopy(this, *it);
12406 mUSBControllers->push_back(ctrl);
12407 }
12408
12409 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12410 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12411 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12412 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12413 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12414 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12415 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12416}
12417
12418/**
12419 * Returns whether the given storage controller is hotplug capable.
12420 *
12421 * @returns true if the controller supports hotplugging
12422 * false otherwise.
12423 * @param enmCtrlType The controller type to check for.
12424 */
12425bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12426{
12427 switch (enmCtrlType)
12428 {
12429 case StorageControllerType_IntelAhci:
12430 return true;
12431 case StorageControllerType_LsiLogic:
12432 case StorageControllerType_LsiLogicSas:
12433 case StorageControllerType_BusLogic:
12434 case StorageControllerType_PIIX3:
12435 case StorageControllerType_PIIX4:
12436 case StorageControllerType_ICH6:
12437 case StorageControllerType_I82078:
12438 default:
12439 return false;
12440 }
12441}
12442
12443#ifdef VBOX_WITH_RESOURCE_USAGE_API
12444
12445void Machine::getDiskList(MediaList &list)
12446{
12447 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12448 it != mMediaData->mAttachments.end();
12449 ++it)
12450 {
12451 MediumAttachment* pAttach = *it;
12452 /* just in case */
12453 AssertStmt(pAttach, continue);
12454
12455 AutoCaller localAutoCallerA(pAttach);
12456 if (FAILED(localAutoCallerA.rc())) continue;
12457
12458 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12459
12460 if (pAttach->getType() == DeviceType_HardDisk)
12461 list.push_back(pAttach->getMedium());
12462 }
12463}
12464
12465void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12466{
12467 AssertReturnVoid(isWriteLockOnCurrentThread());
12468 AssertPtrReturnVoid(aCollector);
12469
12470 pm::CollectorHAL *hal = aCollector->getHAL();
12471 /* Create sub metrics */
12472 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12473 "Percentage of processor time spent in user mode by the VM process.");
12474 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12475 "Percentage of processor time spent in kernel mode by the VM process.");
12476 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12477 "Size of resident portion of VM process in memory.");
12478 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12479 "Actual size of all VM disks combined.");
12480 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12481 "Network receive rate.");
12482 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12483 "Network transmit rate.");
12484 /* Create and register base metrics */
12485 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12486 cpuLoadUser, cpuLoadKernel);
12487 aCollector->registerBaseMetric(cpuLoad);
12488 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12489 ramUsageUsed);
12490 aCollector->registerBaseMetric(ramUsage);
12491 MediaList disks;
12492 getDiskList(disks);
12493 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12494 diskUsageUsed);
12495 aCollector->registerBaseMetric(diskUsage);
12496
12497 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12498 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12499 new pm::AggregateAvg()));
12500 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12501 new pm::AggregateMin()));
12502 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12503 new pm::AggregateMax()));
12504 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12505 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12506 new pm::AggregateAvg()));
12507 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12508 new pm::AggregateMin()));
12509 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12510 new pm::AggregateMax()));
12511
12512 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12513 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12514 new pm::AggregateAvg()));
12515 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12516 new pm::AggregateMin()));
12517 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12518 new pm::AggregateMax()));
12519
12520 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12521 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12522 new pm::AggregateAvg()));
12523 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12524 new pm::AggregateMin()));
12525 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12526 new pm::AggregateMax()));
12527
12528
12529 /* Guest metrics collector */
12530 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12531 aCollector->registerGuest(mCollectorGuest);
12532 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12533 this, __PRETTY_FUNCTION__, mCollectorGuest));
12534
12535 /* Create sub metrics */
12536 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12537 "Percentage of processor time spent in user mode as seen by the guest.");
12538 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12539 "Percentage of processor time spent in kernel mode as seen by the guest.");
12540 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12541 "Percentage of processor time spent idling as seen by the guest.");
12542
12543 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12544 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12545 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12546 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12547 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12548 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12549
12550 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12551
12552 /* Create and register base metrics */
12553 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12554 machineNetRx, machineNetTx);
12555 aCollector->registerBaseMetric(machineNetRate);
12556
12557 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12558 guestLoadUser, guestLoadKernel, guestLoadIdle);
12559 aCollector->registerBaseMetric(guestCpuLoad);
12560
12561 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12562 guestMemTotal, guestMemFree,
12563 guestMemBalloon, guestMemShared,
12564 guestMemCache, guestPagedTotal);
12565 aCollector->registerBaseMetric(guestCpuMem);
12566
12567 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12568 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12569 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12570 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12571
12572 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12573 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12574 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12575 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12576
12577 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12578 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12579 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12580 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12581
12582 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12583 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12584 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12585 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12586
12587 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12588 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12589 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12590 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12591
12592 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12593 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12594 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12595 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12596
12597 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12598 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12599 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12600 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12601
12602 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12603 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12604 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12605 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12606
12607 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12608 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12609 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12610 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12611
12612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12613 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12614 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12616
12617 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12618 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12619 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12620 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12621}
12622
12623void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12624{
12625 AssertReturnVoid(isWriteLockOnCurrentThread());
12626
12627 if (aCollector)
12628 {
12629 aCollector->unregisterMetricsFor(aMachine);
12630 aCollector->unregisterBaseMetricsFor(aMachine);
12631 }
12632}
12633
12634#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12635
12636
12637////////////////////////////////////////////////////////////////////////////////
12638
12639DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12640
12641HRESULT SessionMachine::FinalConstruct()
12642{
12643 LogFlowThisFunc(("\n"));
12644
12645 mClientToken = NULL;
12646
12647 return BaseFinalConstruct();
12648}
12649
12650void SessionMachine::FinalRelease()
12651{
12652 LogFlowThisFunc(("\n"));
12653
12654 Assert(!mClientToken);
12655 /* paranoia, should not hang around any more */
12656 if (mClientToken)
12657 {
12658 delete mClientToken;
12659 mClientToken = NULL;
12660 }
12661
12662 uninit(Uninit::Unexpected);
12663
12664 BaseFinalRelease();
12665}
12666
12667/**
12668 * @note Must be called only by Machine::LockMachine() from its own write lock.
12669 */
12670HRESULT SessionMachine::init(Machine *aMachine)
12671{
12672 LogFlowThisFuncEnter();
12673 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12674
12675 AssertReturn(aMachine, E_INVALIDARG);
12676
12677 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12678
12679 /* Enclose the state transition NotReady->InInit->Ready */
12680 AutoInitSpan autoInitSpan(this);
12681 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12682
12683 HRESULT rc = S_OK;
12684
12685 /* create the machine client token */
12686 try
12687 {
12688 mClientToken = new ClientToken(aMachine, this);
12689 if (!mClientToken->isReady())
12690 {
12691 delete mClientToken;
12692 mClientToken = NULL;
12693 rc = E_FAIL;
12694 }
12695 }
12696 catch (std::bad_alloc &)
12697 {
12698 rc = E_OUTOFMEMORY;
12699 }
12700 if (FAILED(rc))
12701 return rc;
12702
12703 /* memorize the peer Machine */
12704 unconst(mPeer) = aMachine;
12705 /* share the parent pointer */
12706 unconst(mParent) = aMachine->mParent;
12707
12708 /* take the pointers to data to share */
12709 mData.share(aMachine->mData);
12710 mSSData.share(aMachine->mSSData);
12711
12712 mUserData.share(aMachine->mUserData);
12713 mHWData.share(aMachine->mHWData);
12714 mMediaData.share(aMachine->mMediaData);
12715
12716 mStorageControllers.allocate();
12717 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12718 it != aMachine->mStorageControllers->end();
12719 ++it)
12720 {
12721 ComObjPtr<StorageController> ctl;
12722 ctl.createObject();
12723 ctl->init(this, *it);
12724 mStorageControllers->push_back(ctl);
12725 }
12726
12727 mUSBControllers.allocate();
12728 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12729 it != aMachine->mUSBControllers->end();
12730 ++it)
12731 {
12732 ComObjPtr<USBController> ctl;
12733 ctl.createObject();
12734 ctl->init(this, *it);
12735 mUSBControllers->push_back(ctl);
12736 }
12737
12738 unconst(mBIOSSettings).createObject();
12739 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12740 /* create another VRDEServer object that will be mutable */
12741 unconst(mVRDEServer).createObject();
12742 mVRDEServer->init(this, aMachine->mVRDEServer);
12743 /* create another audio adapter object that will be mutable */
12744 unconst(mAudioAdapter).createObject();
12745 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12746 /* create a list of serial ports that will be mutable */
12747 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12748 {
12749 unconst(mSerialPorts[slot]).createObject();
12750 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12751 }
12752 /* create a list of parallel ports that will be mutable */
12753 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12754 {
12755 unconst(mParallelPorts[slot]).createObject();
12756 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12757 }
12758
12759 /* create another USB device filters object that will be mutable */
12760 unconst(mUSBDeviceFilters).createObject();
12761 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12762
12763 /* create a list of network adapters that will be mutable */
12764 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12765 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12766 {
12767 unconst(mNetworkAdapters[slot]).createObject();
12768 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12769 }
12770
12771 /* create another bandwidth control object that will be mutable */
12772 unconst(mBandwidthControl).createObject();
12773 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12774
12775 /* default is to delete saved state on Saved -> PoweredOff transition */
12776 mRemoveSavedState = true;
12777
12778 /* Confirm a successful initialization when it's the case */
12779 autoInitSpan.setSucceeded();
12780
12781 LogFlowThisFuncLeave();
12782 return rc;
12783}
12784
12785/**
12786 * Uninitializes this session object. If the reason is other than
12787 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12788 * or the client watcher code.
12789 *
12790 * @param aReason uninitialization reason
12791 *
12792 * @note Locks mParent + this object for writing.
12793 */
12794void SessionMachine::uninit(Uninit::Reason aReason)
12795{
12796 LogFlowThisFuncEnter();
12797 LogFlowThisFunc(("reason=%d\n", aReason));
12798
12799 /*
12800 * Strongly reference ourselves to prevent this object deletion after
12801 * mData->mSession.mMachine.setNull() below (which can release the last
12802 * reference and call the destructor). Important: this must be done before
12803 * accessing any members (and before AutoUninitSpan that does it as well).
12804 * This self reference will be released as the very last step on return.
12805 */
12806 ComObjPtr<SessionMachine> selfRef = this;
12807
12808 /* Enclose the state transition Ready->InUninit->NotReady */
12809 AutoUninitSpan autoUninitSpan(this);
12810 if (autoUninitSpan.uninitDone())
12811 {
12812 LogFlowThisFunc(("Already uninitialized\n"));
12813 LogFlowThisFuncLeave();
12814 return;
12815 }
12816
12817 if (autoUninitSpan.initFailed())
12818 {
12819 /* We've been called by init() because it's failed. It's not really
12820 * necessary (nor it's safe) to perform the regular uninit sequence
12821 * below, the following is enough.
12822 */
12823 LogFlowThisFunc(("Initialization failed.\n"));
12824 /* destroy the machine client token */
12825 if (mClientToken)
12826 {
12827 delete mClientToken;
12828 mClientToken = NULL;
12829 }
12830 uninitDataAndChildObjects();
12831 mData.free();
12832 unconst(mParent) = NULL;
12833 unconst(mPeer) = NULL;
12834 LogFlowThisFuncLeave();
12835 return;
12836 }
12837
12838 MachineState_T lastState;
12839 {
12840 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12841 lastState = mData->mMachineState;
12842 }
12843 NOREF(lastState);
12844
12845#ifdef VBOX_WITH_USB
12846 // release all captured USB devices, but do this before requesting the locks below
12847 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12848 {
12849 /* Console::captureUSBDevices() is called in the VM process only after
12850 * setting the machine state to Starting or Restoring.
12851 * Console::detachAllUSBDevices() will be called upon successful
12852 * termination. So, we need to release USB devices only if there was
12853 * an abnormal termination of a running VM.
12854 *
12855 * This is identical to SessionMachine::DetachAllUSBDevices except
12856 * for the aAbnormal argument. */
12857 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
12858 AssertComRC(rc);
12859 NOREF(rc);
12860
12861 USBProxyService *service = mParent->host()->usbProxyService();
12862 if (service)
12863 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12864 }
12865#endif /* VBOX_WITH_USB */
12866
12867 // we need to lock this object in uninit() because the lock is shared
12868 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12869 // and others need mParent lock, and USB needs host lock.
12870 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12871
12872#ifdef VBOX_WITH_RESOURCE_USAGE_API
12873 /*
12874 * It is safe to call Machine::unregisterMetrics() here because
12875 * PerformanceCollector::samplerCallback no longer accesses guest methods
12876 * holding the lock.
12877 */
12878 unregisterMetrics(mParent->performanceCollector(), mPeer);
12879 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12880 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12881 this, __PRETTY_FUNCTION__, mCollectorGuest));
12882 if (mCollectorGuest)
12883 {
12884 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12885 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12886 mCollectorGuest = NULL;
12887 }
12888#endif
12889
12890 if (aReason == Uninit::Abnormal)
12891 {
12892 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12893 Global::IsOnlineOrTransient(lastState)));
12894
12895 /* reset the state to Aborted */
12896 if (mData->mMachineState != MachineState_Aborted)
12897 setMachineState(MachineState_Aborted);
12898 }
12899
12900 // any machine settings modified?
12901 if (mData->flModifications)
12902 {
12903 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12904 rollback(false /* aNotify */);
12905 }
12906
12907 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12908 || !mConsoleTaskData.mSnapshot);
12909 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12910 {
12911 LogWarningThisFunc(("canceling failed save state request!\n"));
12912 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12913 }
12914 else if (!mConsoleTaskData.mSnapshot.isNull())
12915 {
12916 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12917
12918 /* delete all differencing hard disks created (this will also attach
12919 * their parents back by rolling back mMediaData) */
12920 rollbackMedia();
12921
12922 // delete the saved state file (it might have been already created)
12923 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12924 // think it's still in use
12925 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12926 mConsoleTaskData.mSnapshot->uninit();
12927 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12928 }
12929
12930 if (!mData->mSession.mType.isEmpty())
12931 {
12932 /* mType is not null when this machine's process has been started by
12933 * Machine::LaunchVMProcess(), therefore it is our child. We
12934 * need to queue the PID to reap the process (and avoid zombies on
12935 * Linux). */
12936 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12937 mParent->addProcessToReap(mData->mSession.mPID);
12938 }
12939
12940 mData->mSession.mPID = NIL_RTPROCESS;
12941
12942 if (aReason == Uninit::Unexpected)
12943 {
12944 /* Uninitialization didn't come from #checkForDeath(), so tell the
12945 * client watcher thread to update the set of machines that have open
12946 * sessions. */
12947 mParent->updateClientWatcher();
12948 }
12949
12950 /* uninitialize all remote controls */
12951 if (mData->mSession.mRemoteControls.size())
12952 {
12953 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12954 mData->mSession.mRemoteControls.size()));
12955
12956 Data::Session::RemoteControlList::iterator it =
12957 mData->mSession.mRemoteControls.begin();
12958 while (it != mData->mSession.mRemoteControls.end())
12959 {
12960 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12961 HRESULT rc = (*it)->Uninitialize();
12962 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12963 if (FAILED(rc))
12964 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12965 ++it;
12966 }
12967 mData->mSession.mRemoteControls.clear();
12968 }
12969
12970 /*
12971 * An expected uninitialization can come only from #checkForDeath().
12972 * Otherwise it means that something's gone really wrong (for example,
12973 * the Session implementation has released the VirtualBox reference
12974 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12975 * etc). However, it's also possible, that the client releases the IPC
12976 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12977 * but the VirtualBox release event comes first to the server process.
12978 * This case is practically possible, so we should not assert on an
12979 * unexpected uninit, just log a warning.
12980 */
12981
12982 if ((aReason == Uninit::Unexpected))
12983 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12984
12985 if (aReason != Uninit::Normal)
12986 {
12987 mData->mSession.mDirectControl.setNull();
12988 }
12989 else
12990 {
12991 /* this must be null here (see #OnSessionEnd()) */
12992 Assert(mData->mSession.mDirectControl.isNull());
12993 Assert(mData->mSession.mState == SessionState_Unlocking);
12994 Assert(!mData->mSession.mProgress.isNull());
12995 }
12996 if (mData->mSession.mProgress)
12997 {
12998 if (aReason == Uninit::Normal)
12999 mData->mSession.mProgress->notifyComplete(S_OK);
13000 else
13001 mData->mSession.mProgress->notifyComplete(E_FAIL,
13002 COM_IIDOF(ISession),
13003 getComponentName(),
13004 tr("The VM session was aborted"));
13005 mData->mSession.mProgress.setNull();
13006 }
13007
13008 /* remove the association between the peer machine and this session machine */
13009 Assert( (SessionMachine*)mData->mSession.mMachine == this
13010 || aReason == Uninit::Unexpected);
13011
13012 /* reset the rest of session data */
13013 mData->mSession.mMachine.setNull();
13014 mData->mSession.mState = SessionState_Unlocked;
13015 mData->mSession.mType.setNull();
13016
13017 /* destroy the machine client token before leaving the exclusive lock */
13018 if (mClientToken)
13019 {
13020 delete mClientToken;
13021 mClientToken = NULL;
13022 }
13023
13024 /* fire an event */
13025 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13026
13027 uninitDataAndChildObjects();
13028
13029 /* free the essential data structure last */
13030 mData.free();
13031
13032 /* release the exclusive lock before setting the below two to NULL */
13033 multilock.release();
13034
13035 unconst(mParent) = NULL;
13036 unconst(mPeer) = NULL;
13037
13038 LogFlowThisFuncLeave();
13039}
13040
13041// util::Lockable interface
13042////////////////////////////////////////////////////////////////////////////////
13043
13044/**
13045 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13046 * with the primary Machine instance (mPeer).
13047 */
13048RWLockHandle *SessionMachine::lockHandle() const
13049{
13050 AssertReturn(mPeer != NULL, NULL);
13051 return mPeer->lockHandle();
13052}
13053
13054// IInternalMachineControl methods
13055////////////////////////////////////////////////////////////////////////////////
13056
13057/**
13058 * Passes collected guest statistics to performance collector object
13059 */
13060STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13061 ULONG aCpuKernel, ULONG aCpuIdle,
13062 ULONG aMemTotal, ULONG aMemFree,
13063 ULONG aMemBalloon, ULONG aMemShared,
13064 ULONG aMemCache, ULONG aPageTotal,
13065 ULONG aAllocVMM, ULONG aFreeVMM,
13066 ULONG aBalloonedVMM, ULONG aSharedVMM,
13067 ULONG aVmNetRx, ULONG aVmNetTx)
13068{
13069#ifdef VBOX_WITH_RESOURCE_USAGE_API
13070 if (mCollectorGuest)
13071 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13072 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13073 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13074 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13075
13076 return S_OK;
13077#else
13078 NOREF(aValidStats);
13079 NOREF(aCpuUser);
13080 NOREF(aCpuKernel);
13081 NOREF(aCpuIdle);
13082 NOREF(aMemTotal);
13083 NOREF(aMemFree);
13084 NOREF(aMemBalloon);
13085 NOREF(aMemShared);
13086 NOREF(aMemCache);
13087 NOREF(aPageTotal);
13088 NOREF(aAllocVMM);
13089 NOREF(aFreeVMM);
13090 NOREF(aBalloonedVMM);
13091 NOREF(aSharedVMM);
13092 NOREF(aVmNetRx);
13093 NOREF(aVmNetTx);
13094 return E_NOTIMPL;
13095#endif
13096}
13097
13098/**
13099 * @note Locks this object for writing.
13100 */
13101STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13102{
13103 AutoCaller autoCaller(this);
13104 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13105
13106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13107
13108 mRemoveSavedState = aRemove;
13109
13110 return S_OK;
13111}
13112
13113/**
13114 * @note Locks the same as #setMachineState() does.
13115 */
13116STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13117{
13118 return setMachineState(aMachineState);
13119}
13120
13121/**
13122 * @note Locks this object for writing.
13123 */
13124STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13125{
13126 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13127 AutoCaller autoCaller(this);
13128 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13129
13130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13131
13132 if (mData->mSession.mState != SessionState_Locked)
13133 return VBOX_E_INVALID_OBJECT_STATE;
13134
13135 if (!mData->mSession.mProgress.isNull())
13136 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13137
13138 LogFlowThisFunc(("returns S_OK.\n"));
13139 return S_OK;
13140}
13141
13142/**
13143 * @note Locks this object for writing.
13144 */
13145STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13146{
13147 AutoCaller autoCaller(this);
13148 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13149
13150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13151
13152 if (mData->mSession.mState != SessionState_Locked)
13153 return VBOX_E_INVALID_OBJECT_STATE;
13154
13155 /* Finalize the LaunchVMProcess progress object. */
13156 if (mData->mSession.mProgress)
13157 {
13158 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13159 mData->mSession.mProgress.setNull();
13160 }
13161
13162 if (SUCCEEDED((HRESULT)iResult))
13163 {
13164#ifdef VBOX_WITH_RESOURCE_USAGE_API
13165 /* The VM has been powered up successfully, so it makes sense
13166 * now to offer the performance metrics for a running machine
13167 * object. Doing it earlier wouldn't be safe. */
13168 registerMetrics(mParent->performanceCollector(), mPeer,
13169 mData->mSession.mPID);
13170#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13171 }
13172
13173 return S_OK;
13174}
13175
13176/**
13177 * @note Locks this object for writing.
13178 */
13179STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13180{
13181 LogFlowThisFuncEnter();
13182
13183 CheckComArgOutPointerValid(aProgress);
13184
13185 AutoCaller autoCaller(this);
13186 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13187
13188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13189
13190 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13191 E_FAIL);
13192
13193 /* create a progress object to track operation completion */
13194 ComObjPtr<Progress> pProgress;
13195 pProgress.createObject();
13196 pProgress->init(getVirtualBox(),
13197 static_cast<IMachine *>(this) /* aInitiator */,
13198 Bstr(tr("Stopping the virtual machine")).raw(),
13199 FALSE /* aCancelable */);
13200
13201 /* fill in the console task data */
13202 mConsoleTaskData.mLastState = mData->mMachineState;
13203 mConsoleTaskData.mProgress = pProgress;
13204
13205 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13206 setMachineState(MachineState_Stopping);
13207
13208 pProgress.queryInterfaceTo(aProgress);
13209
13210 return S_OK;
13211}
13212
13213/**
13214 * @note Locks this object for writing.
13215 */
13216STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13217{
13218 LogFlowThisFuncEnter();
13219
13220 AutoCaller autoCaller(this);
13221 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13222
13223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13224
13225 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13226 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13227 && mConsoleTaskData.mLastState != MachineState_Null,
13228 E_FAIL);
13229
13230 /*
13231 * On failure, set the state to the state we had when BeginPoweringDown()
13232 * was called (this is expected by Console::PowerDown() and the associated
13233 * task). On success the VM process already changed the state to
13234 * MachineState_PoweredOff, so no need to do anything.
13235 */
13236 if (FAILED(iResult))
13237 setMachineState(mConsoleTaskData.mLastState);
13238
13239 /* notify the progress object about operation completion */
13240 Assert(mConsoleTaskData.mProgress);
13241 if (SUCCEEDED(iResult))
13242 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13243 else
13244 {
13245 Utf8Str strErrMsg(aErrMsg);
13246 if (strErrMsg.length())
13247 mConsoleTaskData.mProgress->notifyComplete(iResult,
13248 COM_IIDOF(ISession),
13249 getComponentName(),
13250 strErrMsg.c_str());
13251 else
13252 mConsoleTaskData.mProgress->notifyComplete(iResult);
13253 }
13254
13255 /* clear out the temporary saved state data */
13256 mConsoleTaskData.mLastState = MachineState_Null;
13257 mConsoleTaskData.mProgress.setNull();
13258
13259 LogFlowThisFuncLeave();
13260 return S_OK;
13261}
13262
13263
13264/**
13265 * Goes through the USB filters of the given machine to see if the given
13266 * device matches any filter or not.
13267 *
13268 * @note Locks the same as USBController::hasMatchingFilter() does.
13269 */
13270STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13271 BOOL *aMatched,
13272 ULONG *aMaskedIfs)
13273{
13274 LogFlowThisFunc(("\n"));
13275
13276 CheckComArgNotNull(aUSBDevice);
13277 CheckComArgOutPointerValid(aMatched);
13278
13279 AutoCaller autoCaller(this);
13280 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13281
13282#ifdef VBOX_WITH_USB
13283 *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13284#else
13285 NOREF(aUSBDevice);
13286 NOREF(aMaskedIfs);
13287 *aMatched = FALSE;
13288#endif
13289
13290 return S_OK;
13291}
13292
13293/**
13294 * @note Locks the same as Host::captureUSBDevice() does.
13295 */
13296STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13297{
13298 LogFlowThisFunc(("\n"));
13299
13300 AutoCaller autoCaller(this);
13301 AssertComRCReturnRC(autoCaller.rc());
13302
13303#ifdef VBOX_WITH_USB
13304 /* if captureDeviceForVM() fails, it must have set extended error info */
13305 clearError();
13306 MultiResult rc = mParent->host()->checkUSBProxyService();
13307 if (FAILED(rc)) return rc;
13308
13309 USBProxyService *service = mParent->host()->usbProxyService();
13310 AssertReturn(service, E_FAIL);
13311 return service->captureDeviceForVM(this, Guid(aId).ref());
13312#else
13313 NOREF(aId);
13314 return E_NOTIMPL;
13315#endif
13316}
13317
13318/**
13319 * @note Locks the same as Host::detachUSBDevice() does.
13320 */
13321STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13322{
13323 LogFlowThisFunc(("\n"));
13324
13325 AutoCaller autoCaller(this);
13326 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13327
13328#ifdef VBOX_WITH_USB
13329 USBProxyService *service = mParent->host()->usbProxyService();
13330 AssertReturn(service, E_FAIL);
13331 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13332#else
13333 NOREF(aId);
13334 NOREF(aDone);
13335 return E_NOTIMPL;
13336#endif
13337}
13338
13339/**
13340 * Inserts all machine filters to the USB proxy service and then calls
13341 * Host::autoCaptureUSBDevices().
13342 *
13343 * Called by Console from the VM process upon VM startup.
13344 *
13345 * @note Locks what called methods lock.
13346 */
13347STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13348{
13349 LogFlowThisFunc(("\n"));
13350
13351 AutoCaller autoCaller(this);
13352 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13353
13354#ifdef VBOX_WITH_USB
13355 HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);
13356 AssertComRC(rc);
13357 NOREF(rc);
13358
13359 USBProxyService *service = mParent->host()->usbProxyService();
13360 AssertReturn(service, E_FAIL);
13361 return service->autoCaptureDevicesForVM(this);
13362#else
13363 return S_OK;
13364#endif
13365}
13366
13367/**
13368 * Removes all machine filters from the USB proxy service and then calls
13369 * Host::detachAllUSBDevices().
13370 *
13371 * Called by Console from the VM process upon normal VM termination or by
13372 * SessionMachine::uninit() upon abnormal VM termination (from under the
13373 * Machine/SessionMachine lock).
13374 *
13375 * @note Locks what called methods lock.
13376 */
13377STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13378{
13379 LogFlowThisFunc(("\n"));
13380
13381 AutoCaller autoCaller(this);
13382 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13383
13384#ifdef VBOX_WITH_USB
13385 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
13386 AssertComRC(rc);
13387 NOREF(rc);
13388
13389 USBProxyService *service = mParent->host()->usbProxyService();
13390 AssertReturn(service, E_FAIL);
13391 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13392#else
13393 NOREF(aDone);
13394 return S_OK;
13395#endif
13396}
13397
13398/**
13399 * @note Locks this object for writing.
13400 */
13401STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13402 IProgress **aProgress)
13403{
13404 LogFlowThisFuncEnter();
13405
13406 AssertReturn(aSession, E_INVALIDARG);
13407 AssertReturn(aProgress, E_INVALIDARG);
13408
13409 AutoCaller autoCaller(this);
13410
13411 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13412 /*
13413 * We don't assert below because it might happen that a non-direct session
13414 * informs us it is closed right after we've been uninitialized -- it's ok.
13415 */
13416 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13417
13418 /* get IInternalSessionControl interface */
13419 ComPtr<IInternalSessionControl> control(aSession);
13420
13421 ComAssertRet(!control.isNull(), E_INVALIDARG);
13422
13423 /* Creating a Progress object requires the VirtualBox lock, and
13424 * thus locking it here is required by the lock order rules. */
13425 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13426
13427 if (control == mData->mSession.mDirectControl)
13428 {
13429 ComAssertRet(aProgress, E_POINTER);
13430
13431 /* The direct session is being normally closed by the client process
13432 * ----------------------------------------------------------------- */
13433
13434 /* go to the closing state (essential for all open*Session() calls and
13435 * for #checkForDeath()) */
13436 Assert(mData->mSession.mState == SessionState_Locked);
13437 mData->mSession.mState = SessionState_Unlocking;
13438
13439 /* set direct control to NULL to release the remote instance */
13440 mData->mSession.mDirectControl.setNull();
13441 LogFlowThisFunc(("Direct control is set to NULL\n"));
13442
13443 if (mData->mSession.mProgress)
13444 {
13445 /* finalize the progress, someone might wait if a frontend
13446 * closes the session before powering on the VM. */
13447 mData->mSession.mProgress->notifyComplete(E_FAIL,
13448 COM_IIDOF(ISession),
13449 getComponentName(),
13450 tr("The VM session was closed before any attempt to power it on"));
13451 mData->mSession.mProgress.setNull();
13452 }
13453
13454 /* Create the progress object the client will use to wait until
13455 * #checkForDeath() is called to uninitialize this session object after
13456 * it releases the IPC semaphore.
13457 * Note! Because we're "reusing" mProgress here, this must be a proxy
13458 * object just like for LaunchVMProcess. */
13459 Assert(mData->mSession.mProgress.isNull());
13460 ComObjPtr<ProgressProxy> progress;
13461 progress.createObject();
13462 ComPtr<IUnknown> pPeer(mPeer);
13463 progress->init(mParent, pPeer,
13464 Bstr(tr("Closing session")).raw(),
13465 FALSE /* aCancelable */);
13466 progress.queryInterfaceTo(aProgress);
13467 mData->mSession.mProgress = progress;
13468 }
13469 else
13470 {
13471 /* the remote session is being normally closed */
13472 Data::Session::RemoteControlList::iterator it =
13473 mData->mSession.mRemoteControls.begin();
13474 while (it != mData->mSession.mRemoteControls.end())
13475 {
13476 if (control == *it)
13477 break;
13478 ++it;
13479 }
13480 BOOL found = it != mData->mSession.mRemoteControls.end();
13481 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13482 E_INVALIDARG);
13483 // This MUST be erase(it), not remove(*it) as the latter triggers a
13484 // very nasty use after free due to the place where the value "lives".
13485 mData->mSession.mRemoteControls.erase(it);
13486 }
13487
13488 /* signal the client watcher thread, because the client is going away */
13489 mParent->updateClientWatcher();
13490
13491 LogFlowThisFuncLeave();
13492 return S_OK;
13493}
13494
13495/**
13496 * @note Locks this object for writing.
13497 */
13498STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13499{
13500 LogFlowThisFuncEnter();
13501
13502 CheckComArgOutPointerValid(aProgress);
13503 CheckComArgOutPointerValid(aStateFilePath);
13504
13505 AutoCaller autoCaller(this);
13506 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13507
13508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13509
13510 AssertReturn( mData->mMachineState == MachineState_Paused
13511 && mConsoleTaskData.mLastState == MachineState_Null
13512 && mConsoleTaskData.strStateFilePath.isEmpty(),
13513 E_FAIL);
13514
13515 /* create a progress object to track operation completion */
13516 ComObjPtr<Progress> pProgress;
13517 pProgress.createObject();
13518 pProgress->init(getVirtualBox(),
13519 static_cast<IMachine *>(this) /* aInitiator */,
13520 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13521 FALSE /* aCancelable */);
13522
13523 Utf8Str strStateFilePath;
13524 /* stateFilePath is null when the machine is not running */
13525 if (mData->mMachineState == MachineState_Paused)
13526 composeSavedStateFilename(strStateFilePath);
13527
13528 /* fill in the console task data */
13529 mConsoleTaskData.mLastState = mData->mMachineState;
13530 mConsoleTaskData.strStateFilePath = strStateFilePath;
13531 mConsoleTaskData.mProgress = pProgress;
13532
13533 /* set the state to Saving (this is expected by Console::SaveState()) */
13534 setMachineState(MachineState_Saving);
13535
13536 strStateFilePath.cloneTo(aStateFilePath);
13537 pProgress.queryInterfaceTo(aProgress);
13538
13539 return S_OK;
13540}
13541
13542/**
13543 * @note Locks mParent + this object for writing.
13544 */
13545STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13546{
13547 LogFlowThisFunc(("\n"));
13548
13549 AutoCaller autoCaller(this);
13550 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13551
13552 /* endSavingState() need mParent lock */
13553 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13554
13555 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13556 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13557 && mConsoleTaskData.mLastState != MachineState_Null
13558 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13559 E_FAIL);
13560
13561 /*
13562 * On failure, set the state to the state we had when BeginSavingState()
13563 * was called (this is expected by Console::SaveState() and the associated
13564 * task). On success the VM process already changed the state to
13565 * MachineState_Saved, so no need to do anything.
13566 */
13567 if (FAILED(iResult))
13568 setMachineState(mConsoleTaskData.mLastState);
13569
13570 return endSavingState(iResult, aErrMsg);
13571}
13572
13573/**
13574 * @note Locks this object for writing.
13575 */
13576STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13577{
13578 LogFlowThisFunc(("\n"));
13579
13580 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13581
13582 AutoCaller autoCaller(this);
13583 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13584
13585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13586
13587 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13588 || mData->mMachineState == MachineState_Teleported
13589 || mData->mMachineState == MachineState_Aborted
13590 , E_FAIL); /** @todo setError. */
13591
13592 Utf8Str stateFilePathFull = aSavedStateFile;
13593 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13594 if (RT_FAILURE(vrc))
13595 return setError(VBOX_E_FILE_ERROR,
13596 tr("Invalid saved state file path '%ls' (%Rrc)"),
13597 aSavedStateFile,
13598 vrc);
13599
13600 mSSData->strStateFilePath = stateFilePathFull;
13601
13602 /* The below setMachineState() will detect the state transition and will
13603 * update the settings file */
13604
13605 return setMachineState(MachineState_Saved);
13606}
13607
13608STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13609 ComSafeArrayOut(BSTR, aValues),
13610 ComSafeArrayOut(LONG64, aTimestamps),
13611 ComSafeArrayOut(BSTR, aFlags))
13612{
13613 LogFlowThisFunc(("\n"));
13614
13615#ifdef VBOX_WITH_GUEST_PROPS
13616 using namespace guestProp;
13617
13618 AutoCaller autoCaller(this);
13619 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13620
13621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13622
13623 CheckComArgOutSafeArrayPointerValid(aNames);
13624 CheckComArgOutSafeArrayPointerValid(aValues);
13625 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13626 CheckComArgOutSafeArrayPointerValid(aFlags);
13627
13628 size_t cEntries = mHWData->mGuestProperties.size();
13629 com::SafeArray<BSTR> names(cEntries);
13630 com::SafeArray<BSTR> values(cEntries);
13631 com::SafeArray<LONG64> timestamps(cEntries);
13632 com::SafeArray<BSTR> flags(cEntries);
13633 unsigned i = 0;
13634 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13635 it != mHWData->mGuestProperties.end();
13636 ++it)
13637 {
13638 char szFlags[MAX_FLAGS_LEN + 1];
13639 it->first.cloneTo(&names[i]);
13640 it->second.strValue.cloneTo(&values[i]);
13641 timestamps[i] = it->second.mTimestamp;
13642 /* If it is NULL, keep it NULL. */
13643 if (it->second.mFlags)
13644 {
13645 writeFlags(it->second.mFlags, szFlags);
13646 Bstr(szFlags).cloneTo(&flags[i]);
13647 }
13648 else
13649 flags[i] = NULL;
13650 ++i;
13651 }
13652 names.detachTo(ComSafeArrayOutArg(aNames));
13653 values.detachTo(ComSafeArrayOutArg(aValues));
13654 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13655 flags.detachTo(ComSafeArrayOutArg(aFlags));
13656 return S_OK;
13657#else
13658 ReturnComNotImplemented();
13659#endif
13660}
13661
13662STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13663 IN_BSTR aValue,
13664 LONG64 aTimestamp,
13665 IN_BSTR aFlags)
13666{
13667 LogFlowThisFunc(("\n"));
13668
13669#ifdef VBOX_WITH_GUEST_PROPS
13670 using namespace guestProp;
13671
13672 CheckComArgStrNotEmptyOrNull(aName);
13673 CheckComArgNotNull(aValue);
13674 CheckComArgNotNull(aFlags);
13675
13676 try
13677 {
13678 /*
13679 * Convert input up front.
13680 */
13681 Utf8Str utf8Name(aName);
13682 uint32_t fFlags = NILFLAG;
13683 if (aFlags)
13684 {
13685 Utf8Str utf8Flags(aFlags);
13686 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13687 AssertRCReturn(vrc, E_INVALIDARG);
13688 }
13689
13690 /*
13691 * Now grab the object lock, validate the state and do the update.
13692 */
13693 AutoCaller autoCaller(this);
13694 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13695
13696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13697
13698 switch (mData->mMachineState)
13699 {
13700 case MachineState_Paused:
13701 case MachineState_Running:
13702 case MachineState_Teleporting:
13703 case MachineState_TeleportingPausedVM:
13704 case MachineState_LiveSnapshotting:
13705 case MachineState_DeletingSnapshotOnline:
13706 case MachineState_DeletingSnapshotPaused:
13707 case MachineState_Saving:
13708 case MachineState_Stopping:
13709 break;
13710
13711 default:
13712 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13713 VBOX_E_INVALID_VM_STATE);
13714 }
13715
13716 setModified(IsModified_MachineData);
13717 mHWData.backup();
13718
13719 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13720 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13721 if (it != mHWData->mGuestProperties.end())
13722 {
13723 if (!fDelete)
13724 {
13725 it->second.strValue = aValue;
13726 it->second.mTimestamp = aTimestamp;
13727 it->second.mFlags = fFlags;
13728 }
13729 else
13730 mHWData->mGuestProperties.erase(it);
13731
13732 mData->mGuestPropertiesModified = TRUE;
13733 }
13734 else if (!fDelete)
13735 {
13736 HWData::GuestProperty prop;
13737 prop.strValue = aValue;
13738 prop.mTimestamp = aTimestamp;
13739 prop.mFlags = fFlags;
13740
13741 mHWData->mGuestProperties[utf8Name] = prop;
13742 mData->mGuestPropertiesModified = TRUE;
13743 }
13744
13745 /*
13746 * Send a callback notification if appropriate
13747 */
13748 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13749 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13750 RTSTR_MAX,
13751 utf8Name.c_str(),
13752 RTSTR_MAX, NULL)
13753 )
13754 {
13755 alock.release();
13756
13757 mParent->onGuestPropertyChange(mData->mUuid,
13758 aName,
13759 aValue,
13760 aFlags);
13761 }
13762 }
13763 catch (...)
13764 {
13765 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13766 }
13767 return S_OK;
13768#else
13769 ReturnComNotImplemented();
13770#endif
13771}
13772
13773STDMETHODIMP SessionMachine::LockMedia()
13774{
13775 AutoCaller autoCaller(this);
13776 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13777
13778 AutoMultiWriteLock2 alock(this->lockHandle(),
13779 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13780
13781 AssertReturn( mData->mMachineState == MachineState_Starting
13782 || mData->mMachineState == MachineState_Restoring
13783 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13784
13785 clearError();
13786 alock.release();
13787 return lockMedia();
13788}
13789
13790STDMETHODIMP SessionMachine::UnlockMedia()
13791{
13792 unlockMedia();
13793 return S_OK;
13794}
13795
13796STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13797 IMediumAttachment **aNewAttachment)
13798{
13799 CheckComArgNotNull(aAttachment);
13800 CheckComArgOutPointerValid(aNewAttachment);
13801
13802 AutoCaller autoCaller(this);
13803 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13804
13805 // request the host lock first, since might be calling Host methods for getting host drives;
13806 // next, protect the media tree all the while we're in here, as well as our member variables
13807 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13808 this->lockHandle(),
13809 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13810
13811 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13812
13813 Bstr ctrlName;
13814 LONG lPort;
13815 LONG lDevice;
13816 bool fTempEject;
13817 {
13818 AutoCaller autoAttachCaller(this);
13819 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13820
13821 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13822
13823 /* Need to query the details first, as the IMediumAttachment reference
13824 * might be to the original settings, which we are going to change. */
13825 ctrlName = pAttach->getControllerName();
13826 lPort = pAttach->getPort();
13827 lDevice = pAttach->getDevice();
13828 fTempEject = pAttach->getTempEject();
13829 }
13830
13831 if (!fTempEject)
13832 {
13833 /* Remember previously mounted medium. The medium before taking the
13834 * backup is not necessarily the same thing. */
13835 ComObjPtr<Medium> oldmedium;
13836 oldmedium = pAttach->getMedium();
13837
13838 setModified(IsModified_Storage);
13839 mMediaData.backup();
13840
13841 // The backup operation makes the pAttach reference point to the
13842 // old settings. Re-get the correct reference.
13843 pAttach = findAttachment(mMediaData->mAttachments,
13844 ctrlName.raw(),
13845 lPort,
13846 lDevice);
13847
13848 {
13849 AutoCaller autoAttachCaller(this);
13850 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13851
13852 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13853 if (!oldmedium.isNull())
13854 oldmedium->removeBackReference(mData->mUuid);
13855
13856 pAttach->updateMedium(NULL);
13857 pAttach->updateEjected();
13858 }
13859
13860 setModified(IsModified_Storage);
13861 }
13862 else
13863 {
13864 {
13865 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13866 pAttach->updateEjected();
13867 }
13868 }
13869
13870 pAttach.queryInterfaceTo(aNewAttachment);
13871
13872 return S_OK;
13873}
13874
13875// public methods only for internal purposes
13876/////////////////////////////////////////////////////////////////////////////
13877
13878#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13879/**
13880 * Called from the client watcher thread to check for expected or unexpected
13881 * death of the client process that has a direct session to this machine.
13882 *
13883 * On Win32 and on OS/2, this method is called only when we've got the
13884 * mutex (i.e. the client has either died or terminated normally) so it always
13885 * returns @c true (the client is terminated, the session machine is
13886 * uninitialized).
13887 *
13888 * On other platforms, the method returns @c true if the client process has
13889 * terminated normally or abnormally and the session machine was uninitialized,
13890 * and @c false if the client process is still alive.
13891 *
13892 * @note Locks this object for writing.
13893 */
13894bool SessionMachine::checkForDeath()
13895{
13896 Uninit::Reason reason;
13897 bool terminated = false;
13898
13899 /* Enclose autoCaller with a block because calling uninit() from under it
13900 * will deadlock. */
13901 {
13902 AutoCaller autoCaller(this);
13903 if (!autoCaller.isOk())
13904 {
13905 /* return true if not ready, to cause the client watcher to exclude
13906 * the corresponding session from watching */
13907 LogFlowThisFunc(("Already uninitialized!\n"));
13908 return true;
13909 }
13910
13911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13912
13913 /* Determine the reason of death: if the session state is Closing here,
13914 * everything is fine. Otherwise it means that the client did not call
13915 * OnSessionEnd() before it released the IPC semaphore. This may happen
13916 * either because the client process has abnormally terminated, or
13917 * because it simply forgot to call ISession::Close() before exiting. We
13918 * threat the latter also as an abnormal termination (see
13919 * Session::uninit() for details). */
13920 reason = mData->mSession.mState == SessionState_Unlocking ?
13921 Uninit::Normal :
13922 Uninit::Abnormal;
13923
13924 if (mClientToken)
13925 terminated = mClientToken->release();
13926 } /* AutoCaller block */
13927
13928 if (terminated)
13929 uninit(reason);
13930
13931 return terminated;
13932}
13933
13934void SessionMachine::getTokenId(Utf8Str &strTokenId)
13935{
13936 LogFlowThisFunc(("\n"));
13937
13938 strTokenId.setNull();
13939
13940 AutoCaller autoCaller(this);
13941 AssertComRCReturnVoid(autoCaller.rc());
13942
13943 Assert(mClientToken);
13944 if (mClientToken)
13945 mClientToken->getId(strTokenId);
13946}
13947#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13948IToken *SessionMachine::getToken()
13949{
13950 LogFlowThisFunc(("\n"));
13951
13952 AutoCaller autoCaller(this);
13953 AssertComRCReturn(autoCaller.rc(), NULL);
13954
13955 Assert(mClientToken);
13956 if (mClientToken)
13957 return mClientToken->getToken();
13958 else
13959 return NULL;
13960}
13961#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13962
13963Machine::ClientToken *SessionMachine::getClientToken()
13964{
13965 LogFlowThisFunc(("\n"));
13966
13967 AutoCaller autoCaller(this);
13968 AssertComRCReturn(autoCaller.rc(), NULL);
13969
13970 return mClientToken;
13971}
13972
13973
13974/**
13975 * @note Locks this object for reading.
13976 */
13977HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13978{
13979 LogFlowThisFunc(("\n"));
13980
13981 AutoCaller autoCaller(this);
13982 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13983
13984 ComPtr<IInternalSessionControl> directControl;
13985 {
13986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13987 directControl = mData->mSession.mDirectControl;
13988 }
13989
13990 /* ignore notifications sent after #OnSessionEnd() is called */
13991 if (!directControl)
13992 return S_OK;
13993
13994 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13995}
13996
13997/**
13998 * @note Locks this object for reading.
13999 */
14000HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14001 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
14002{
14003 LogFlowThisFunc(("\n"));
14004
14005 AutoCaller autoCaller(this);
14006 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14007
14008 ComPtr<IInternalSessionControl> directControl;
14009 {
14010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14011 directControl = mData->mSession.mDirectControl;
14012 }
14013
14014 /* ignore notifications sent after #OnSessionEnd() is called */
14015 if (!directControl)
14016 return S_OK;
14017 /*
14018 * instead acting like callback we ask IVirtualBox deliver corresponding event
14019 */
14020
14021 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14022 return S_OK;
14023}
14024
14025/**
14026 * @note Locks this object for reading.
14027 */
14028HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14029{
14030 LogFlowThisFunc(("\n"));
14031
14032 AutoCaller autoCaller(this);
14033 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14034
14035 ComPtr<IInternalSessionControl> directControl;
14036 {
14037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14038 directControl = mData->mSession.mDirectControl;
14039 }
14040
14041 /* ignore notifications sent after #OnSessionEnd() is called */
14042 if (!directControl)
14043 return S_OK;
14044
14045 return directControl->OnSerialPortChange(serialPort);
14046}
14047
14048/**
14049 * @note Locks this object for reading.
14050 */
14051HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14052{
14053 LogFlowThisFunc(("\n"));
14054
14055 AutoCaller autoCaller(this);
14056 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14057
14058 ComPtr<IInternalSessionControl> directControl;
14059 {
14060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14061 directControl = mData->mSession.mDirectControl;
14062 }
14063
14064 /* ignore notifications sent after #OnSessionEnd() is called */
14065 if (!directControl)
14066 return S_OK;
14067
14068 return directControl->OnParallelPortChange(parallelPort);
14069}
14070
14071/**
14072 * @note Locks this object for reading.
14073 */
14074HRESULT SessionMachine::onStorageControllerChange()
14075{
14076 LogFlowThisFunc(("\n"));
14077
14078 AutoCaller autoCaller(this);
14079 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14080
14081 ComPtr<IInternalSessionControl> directControl;
14082 {
14083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14084 directControl = mData->mSession.mDirectControl;
14085 }
14086
14087 /* ignore notifications sent after #OnSessionEnd() is called */
14088 if (!directControl)
14089 return S_OK;
14090
14091 return directControl->OnStorageControllerChange();
14092}
14093
14094/**
14095 * @note Locks this object for reading.
14096 */
14097HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14098{
14099 LogFlowThisFunc(("\n"));
14100
14101 AutoCaller autoCaller(this);
14102 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14103
14104 ComPtr<IInternalSessionControl> directControl;
14105 {
14106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14107 directControl = mData->mSession.mDirectControl;
14108 }
14109
14110 /* ignore notifications sent after #OnSessionEnd() is called */
14111 if (!directControl)
14112 return S_OK;
14113
14114 return directControl->OnMediumChange(aAttachment, aForce);
14115}
14116
14117/**
14118 * @note Locks this object for reading.
14119 */
14120HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14121{
14122 LogFlowThisFunc(("\n"));
14123
14124 AutoCaller autoCaller(this);
14125 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14126
14127 ComPtr<IInternalSessionControl> directControl;
14128 {
14129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14130 directControl = mData->mSession.mDirectControl;
14131 }
14132
14133 /* ignore notifications sent after #OnSessionEnd() is called */
14134 if (!directControl)
14135 return S_OK;
14136
14137 return directControl->OnCPUChange(aCPU, aRemove);
14138}
14139
14140HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14141{
14142 LogFlowThisFunc(("\n"));
14143
14144 AutoCaller autoCaller(this);
14145 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14146
14147 ComPtr<IInternalSessionControl> directControl;
14148 {
14149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14150 directControl = mData->mSession.mDirectControl;
14151 }
14152
14153 /* ignore notifications sent after #OnSessionEnd() is called */
14154 if (!directControl)
14155 return S_OK;
14156
14157 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14158}
14159
14160/**
14161 * @note Locks this object for reading.
14162 */
14163HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14164{
14165 LogFlowThisFunc(("\n"));
14166
14167 AutoCaller autoCaller(this);
14168 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14169
14170 ComPtr<IInternalSessionControl> directControl;
14171 {
14172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14173 directControl = mData->mSession.mDirectControl;
14174 }
14175
14176 /* ignore notifications sent after #OnSessionEnd() is called */
14177 if (!directControl)
14178 return S_OK;
14179
14180 return directControl->OnVRDEServerChange(aRestart);
14181}
14182
14183/**
14184 * @note Locks this object for reading.
14185 */
14186HRESULT SessionMachine::onVideoCaptureChange()
14187{
14188 LogFlowThisFunc(("\n"));
14189
14190 AutoCaller autoCaller(this);
14191 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14192
14193 ComPtr<IInternalSessionControl> directControl;
14194 {
14195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14196 directControl = mData->mSession.mDirectControl;
14197 }
14198
14199 /* ignore notifications sent after #OnSessionEnd() is called */
14200 if (!directControl)
14201 return S_OK;
14202
14203 return directControl->OnVideoCaptureChange();
14204}
14205
14206/**
14207 * @note Locks this object for reading.
14208 */
14209HRESULT SessionMachine::onUSBControllerChange()
14210{
14211 LogFlowThisFunc(("\n"));
14212
14213 AutoCaller autoCaller(this);
14214 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14215
14216 ComPtr<IInternalSessionControl> directControl;
14217 {
14218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14219 directControl = mData->mSession.mDirectControl;
14220 }
14221
14222 /* ignore notifications sent after #OnSessionEnd() is called */
14223 if (!directControl)
14224 return S_OK;
14225
14226 return directControl->OnUSBControllerChange();
14227}
14228
14229/**
14230 * @note Locks this object for reading.
14231 */
14232HRESULT SessionMachine::onSharedFolderChange()
14233{
14234 LogFlowThisFunc(("\n"));
14235
14236 AutoCaller autoCaller(this);
14237 AssertComRCReturnRC(autoCaller.rc());
14238
14239 ComPtr<IInternalSessionControl> directControl;
14240 {
14241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14242 directControl = mData->mSession.mDirectControl;
14243 }
14244
14245 /* ignore notifications sent after #OnSessionEnd() is called */
14246 if (!directControl)
14247 return S_OK;
14248
14249 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14250}
14251
14252/**
14253 * @note Locks this object for reading.
14254 */
14255HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14256{
14257 LogFlowThisFunc(("\n"));
14258
14259 AutoCaller autoCaller(this);
14260 AssertComRCReturnRC(autoCaller.rc());
14261
14262 ComPtr<IInternalSessionControl> directControl;
14263 {
14264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14265 directControl = mData->mSession.mDirectControl;
14266 }
14267
14268 /* ignore notifications sent after #OnSessionEnd() is called */
14269 if (!directControl)
14270 return S_OK;
14271
14272 return directControl->OnClipboardModeChange(aClipboardMode);
14273}
14274
14275/**
14276 * @note Locks this object for reading.
14277 */
14278HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14279{
14280 LogFlowThisFunc(("\n"));
14281
14282 AutoCaller autoCaller(this);
14283 AssertComRCReturnRC(autoCaller.rc());
14284
14285 ComPtr<IInternalSessionControl> directControl;
14286 {
14287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14288 directControl = mData->mSession.mDirectControl;
14289 }
14290
14291 /* ignore notifications sent after #OnSessionEnd() is called */
14292 if (!directControl)
14293 return S_OK;
14294
14295 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14296}
14297
14298/**
14299 * @note Locks this object for reading.
14300 */
14301HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14302{
14303 LogFlowThisFunc(("\n"));
14304
14305 AutoCaller autoCaller(this);
14306 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14307
14308 ComPtr<IInternalSessionControl> directControl;
14309 {
14310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14311 directControl = mData->mSession.mDirectControl;
14312 }
14313
14314 /* ignore notifications sent after #OnSessionEnd() is called */
14315 if (!directControl)
14316 return S_OK;
14317
14318 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14319}
14320
14321/**
14322 * @note Locks this object for reading.
14323 */
14324HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14325{
14326 LogFlowThisFunc(("\n"));
14327
14328 AutoCaller autoCaller(this);
14329 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14330
14331 ComPtr<IInternalSessionControl> directControl;
14332 {
14333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14334 directControl = mData->mSession.mDirectControl;
14335 }
14336
14337 /* ignore notifications sent after #OnSessionEnd() is called */
14338 if (!directControl)
14339 return S_OK;
14340
14341 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14342}
14343
14344/**
14345 * Returns @c true if this machine's USB controller reports it has a matching
14346 * filter for the given USB device and @c false otherwise.
14347 *
14348 * @note locks this object for reading.
14349 */
14350bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14351{
14352 AutoCaller autoCaller(this);
14353 /* silently return if not ready -- this method may be called after the
14354 * direct machine session has been called */
14355 if (!autoCaller.isOk())
14356 return false;
14357
14358#ifdef VBOX_WITH_USB
14359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14360
14361 switch (mData->mMachineState)
14362 {
14363 case MachineState_Starting:
14364 case MachineState_Restoring:
14365 case MachineState_TeleportingIn:
14366 case MachineState_Paused:
14367 case MachineState_Running:
14368 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14369 * elsewhere... */
14370 alock.release();
14371 return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs);
14372 default: break;
14373 }
14374#else
14375 NOREF(aDevice);
14376 NOREF(aMaskedIfs);
14377#endif
14378 return false;
14379}
14380
14381/**
14382 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14383 */
14384HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14385 IVirtualBoxErrorInfo *aError,
14386 ULONG aMaskedIfs)
14387{
14388 LogFlowThisFunc(("\n"));
14389
14390 AutoCaller autoCaller(this);
14391
14392 /* This notification may happen after the machine object has been
14393 * uninitialized (the session was closed), so don't assert. */
14394 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14395
14396 ComPtr<IInternalSessionControl> directControl;
14397 {
14398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14399 directControl = mData->mSession.mDirectControl;
14400 }
14401
14402 /* fail on notifications sent after #OnSessionEnd() is called, it is
14403 * expected by the caller */
14404 if (!directControl)
14405 return E_FAIL;
14406
14407 /* No locks should be held at this point. */
14408 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14409 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14410
14411 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14412}
14413
14414/**
14415 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14416 */
14417HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14418 IVirtualBoxErrorInfo *aError)
14419{
14420 LogFlowThisFunc(("\n"));
14421
14422 AutoCaller autoCaller(this);
14423
14424 /* This notification may happen after the machine object has been
14425 * uninitialized (the session was closed), so don't assert. */
14426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14427
14428 ComPtr<IInternalSessionControl> directControl;
14429 {
14430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14431 directControl = mData->mSession.mDirectControl;
14432 }
14433
14434 /* fail on notifications sent after #OnSessionEnd() is called, it is
14435 * expected by the caller */
14436 if (!directControl)
14437 return E_FAIL;
14438
14439 /* No locks should be held at this point. */
14440 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14441 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14442
14443 return directControl->OnUSBDeviceDetach(aId, aError);
14444}
14445
14446// protected methods
14447/////////////////////////////////////////////////////////////////////////////
14448
14449/**
14450 * Helper method to finalize saving the state.
14451 *
14452 * @note Must be called from under this object's lock.
14453 *
14454 * @param aRc S_OK if the snapshot has been taken successfully
14455 * @param aErrMsg human readable error message for failure
14456 *
14457 * @note Locks mParent + this objects for writing.
14458 */
14459HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14460{
14461 LogFlowThisFuncEnter();
14462
14463 AutoCaller autoCaller(this);
14464 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14465
14466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14467
14468 HRESULT rc = S_OK;
14469
14470 if (SUCCEEDED(aRc))
14471 {
14472 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14473
14474 /* save all VM settings */
14475 rc = saveSettings(NULL);
14476 // no need to check whether VirtualBox.xml needs saving also since
14477 // we can't have a name change pending at this point
14478 }
14479 else
14480 {
14481 // delete the saved state file (it might have been already created);
14482 // we need not check whether this is shared with a snapshot here because
14483 // we certainly created this saved state file here anew
14484 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14485 }
14486
14487 /* notify the progress object about operation completion */
14488 Assert(mConsoleTaskData.mProgress);
14489 if (SUCCEEDED(aRc))
14490 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14491 else
14492 {
14493 if (aErrMsg.length())
14494 mConsoleTaskData.mProgress->notifyComplete(aRc,
14495 COM_IIDOF(ISession),
14496 getComponentName(),
14497 aErrMsg.c_str());
14498 else
14499 mConsoleTaskData.mProgress->notifyComplete(aRc);
14500 }
14501
14502 /* clear out the temporary saved state data */
14503 mConsoleTaskData.mLastState = MachineState_Null;
14504 mConsoleTaskData.strStateFilePath.setNull();
14505 mConsoleTaskData.mProgress.setNull();
14506
14507 LogFlowThisFuncLeave();
14508 return rc;
14509}
14510
14511/**
14512 * Deletes the given file if it is no longer in use by either the current machine state
14513 * (if the machine is "saved") or any of the machine's snapshots.
14514 *
14515 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14516 * but is different for each SnapshotMachine. When calling this, the order of calling this
14517 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14518 * is therefore critical. I know, it's all rather messy.
14519 *
14520 * @param strStateFile
14521 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14522 */
14523void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14524 Snapshot *pSnapshotToIgnore)
14525{
14526 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14527 if ( (strStateFile.isNotEmpty())
14528 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14529 )
14530 // ... and it must also not be shared with other snapshots
14531 if ( !mData->mFirstSnapshot
14532 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14533 // this checks the SnapshotMachine's state file paths
14534 )
14535 RTFileDelete(strStateFile.c_str());
14536}
14537
14538/**
14539 * Locks the attached media.
14540 *
14541 * All attached hard disks are locked for writing and DVD/floppy are locked for
14542 * reading. Parents of attached hard disks (if any) are locked for reading.
14543 *
14544 * This method also performs accessibility check of all media it locks: if some
14545 * media is inaccessible, the method will return a failure and a bunch of
14546 * extended error info objects per each inaccessible medium.
14547 *
14548 * Note that this method is atomic: if it returns a success, all media are
14549 * locked as described above; on failure no media is locked at all (all
14550 * succeeded individual locks will be undone).
14551 *
14552 * The caller is responsible for doing the necessary state sanity checks.
14553 *
14554 * The locks made by this method must be undone by calling #unlockMedia() when
14555 * no more needed.
14556 */
14557HRESULT SessionMachine::lockMedia()
14558{
14559 AutoCaller autoCaller(this);
14560 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14561
14562 AutoMultiWriteLock2 alock(this->lockHandle(),
14563 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14564
14565 /* bail out if trying to lock things with already set up locking */
14566 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14567
14568 MultiResult mrc(S_OK);
14569
14570 /* Collect locking information for all medium objects attached to the VM. */
14571 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14572 it != mMediaData->mAttachments.end();
14573 ++it)
14574 {
14575 MediumAttachment* pAtt = *it;
14576 DeviceType_T devType = pAtt->getType();
14577 Medium *pMedium = pAtt->getMedium();
14578
14579 MediumLockList *pMediumLockList(new MediumLockList());
14580 // There can be attachments without a medium (floppy/dvd), and thus
14581 // it's impossible to create a medium lock list. It still makes sense
14582 // to have the empty medium lock list in the map in case a medium is
14583 // attached later.
14584 if (pMedium != NULL)
14585 {
14586 MediumType_T mediumType = pMedium->getType();
14587 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14588 || mediumType == MediumType_Shareable;
14589 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14590
14591 alock.release();
14592 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14593 !fIsReadOnlyLock /* fMediumLockWrite */,
14594 NULL,
14595 *pMediumLockList);
14596 alock.acquire();
14597 if (FAILED(mrc))
14598 {
14599 delete pMediumLockList;
14600 mData->mSession.mLockedMedia.Clear();
14601 break;
14602 }
14603 }
14604
14605 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14606 if (FAILED(rc))
14607 {
14608 mData->mSession.mLockedMedia.Clear();
14609 mrc = setError(rc,
14610 tr("Collecting locking information for all attached media failed"));
14611 break;
14612 }
14613 }
14614
14615 if (SUCCEEDED(mrc))
14616 {
14617 /* Now lock all media. If this fails, nothing is locked. */
14618 alock.release();
14619 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14620 alock.acquire();
14621 if (FAILED(rc))
14622 {
14623 mrc = setError(rc,
14624 tr("Locking of attached media failed"));
14625 }
14626 }
14627
14628 return mrc;
14629}
14630
14631/**
14632 * Undoes the locks made by by #lockMedia().
14633 */
14634void SessionMachine::unlockMedia()
14635{
14636 AutoCaller autoCaller(this);
14637 AssertComRCReturnVoid(autoCaller.rc());
14638
14639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14640
14641 /* we may be holding important error info on the current thread;
14642 * preserve it */
14643 ErrorInfoKeeper eik;
14644
14645 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14646 AssertComRC(rc);
14647}
14648
14649/**
14650 * Helper to change the machine state (reimplementation).
14651 *
14652 * @note Locks this object for writing.
14653 * @note This method must not call saveSettings or SaveSettings, otherwise
14654 * it can cause crashes in random places due to unexpectedly committing
14655 * the current settings. The caller is responsible for that. The call
14656 * to saveStateSettings is fine, because this method does not commit.
14657 */
14658HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14659{
14660 LogFlowThisFuncEnter();
14661 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14662
14663 AutoCaller autoCaller(this);
14664 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14665
14666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14667
14668 MachineState_T oldMachineState = mData->mMachineState;
14669
14670 AssertMsgReturn(oldMachineState != aMachineState,
14671 ("oldMachineState=%s, aMachineState=%s\n",
14672 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14673 E_FAIL);
14674
14675 HRESULT rc = S_OK;
14676
14677 int stsFlags = 0;
14678 bool deleteSavedState = false;
14679
14680 /* detect some state transitions */
14681
14682 if ( ( oldMachineState == MachineState_Saved
14683 && aMachineState == MachineState_Restoring)
14684 || ( ( oldMachineState == MachineState_PoweredOff
14685 || oldMachineState == MachineState_Teleported
14686 || oldMachineState == MachineState_Aborted
14687 )
14688 && ( aMachineState == MachineState_TeleportingIn
14689 || aMachineState == MachineState_Starting
14690 )
14691 )
14692 )
14693 {
14694 /* The EMT thread is about to start */
14695
14696 /* Nothing to do here for now... */
14697
14698 /// @todo NEWMEDIA don't let mDVDDrive and other children
14699 /// change anything when in the Starting/Restoring state
14700 }
14701 else if ( ( oldMachineState == MachineState_Running
14702 || oldMachineState == MachineState_Paused
14703 || oldMachineState == MachineState_Teleporting
14704 || oldMachineState == MachineState_LiveSnapshotting
14705 || oldMachineState == MachineState_Stuck
14706 || oldMachineState == MachineState_Starting
14707 || oldMachineState == MachineState_Stopping
14708 || oldMachineState == MachineState_Saving
14709 || oldMachineState == MachineState_Restoring
14710 || oldMachineState == MachineState_TeleportingPausedVM
14711 || oldMachineState == MachineState_TeleportingIn
14712 )
14713 && ( aMachineState == MachineState_PoweredOff
14714 || aMachineState == MachineState_Saved
14715 || aMachineState == MachineState_Teleported
14716 || aMachineState == MachineState_Aborted
14717 )
14718 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14719 * snapshot */
14720 && ( mConsoleTaskData.mSnapshot.isNull()
14721 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14722 )
14723 )
14724 {
14725 /* The EMT thread has just stopped, unlock attached media. Note that as
14726 * opposed to locking that is done from Console, we do unlocking here
14727 * because the VM process may have aborted before having a chance to
14728 * properly unlock all media it locked. */
14729
14730 unlockMedia();
14731 }
14732
14733 if (oldMachineState == MachineState_Restoring)
14734 {
14735 if (aMachineState != MachineState_Saved)
14736 {
14737 /*
14738 * delete the saved state file once the machine has finished
14739 * restoring from it (note that Console sets the state from
14740 * Restoring to Saved if the VM couldn't restore successfully,
14741 * to give the user an ability to fix an error and retry --
14742 * we keep the saved state file in this case)
14743 */
14744 deleteSavedState = true;
14745 }
14746 }
14747 else if ( oldMachineState == MachineState_Saved
14748 && ( aMachineState == MachineState_PoweredOff
14749 || aMachineState == MachineState_Aborted
14750 || aMachineState == MachineState_Teleported
14751 )
14752 )
14753 {
14754 /*
14755 * delete the saved state after Console::ForgetSavedState() is called
14756 * or if the VM process (owning a direct VM session) crashed while the
14757 * VM was Saved
14758 */
14759
14760 /// @todo (dmik)
14761 // Not sure that deleting the saved state file just because of the
14762 // client death before it attempted to restore the VM is a good
14763 // thing. But when it crashes we need to go to the Aborted state
14764 // which cannot have the saved state file associated... The only
14765 // way to fix this is to make the Aborted condition not a VM state
14766 // but a bool flag: i.e., when a crash occurs, set it to true and
14767 // change the state to PoweredOff or Saved depending on the
14768 // saved state presence.
14769
14770 deleteSavedState = true;
14771 mData->mCurrentStateModified = TRUE;
14772 stsFlags |= SaveSTS_CurStateModified;
14773 }
14774
14775 if ( aMachineState == MachineState_Starting
14776 || aMachineState == MachineState_Restoring
14777 || aMachineState == MachineState_TeleportingIn
14778 )
14779 {
14780 /* set the current state modified flag to indicate that the current
14781 * state is no more identical to the state in the
14782 * current snapshot */
14783 if (!mData->mCurrentSnapshot.isNull())
14784 {
14785 mData->mCurrentStateModified = TRUE;
14786 stsFlags |= SaveSTS_CurStateModified;
14787 }
14788 }
14789
14790 if (deleteSavedState)
14791 {
14792 if (mRemoveSavedState)
14793 {
14794 Assert(!mSSData->strStateFilePath.isEmpty());
14795
14796 // it is safe to delete the saved state file if ...
14797 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14798 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14799 // ... none of the snapshots share the saved state file
14800 )
14801 RTFileDelete(mSSData->strStateFilePath.c_str());
14802 }
14803
14804 mSSData->strStateFilePath.setNull();
14805 stsFlags |= SaveSTS_StateFilePath;
14806 }
14807
14808 /* redirect to the underlying peer machine */
14809 mPeer->setMachineState(aMachineState);
14810
14811 if ( aMachineState == MachineState_PoweredOff
14812 || aMachineState == MachineState_Teleported
14813 || aMachineState == MachineState_Aborted
14814 || aMachineState == MachineState_Saved)
14815 {
14816 /* the machine has stopped execution
14817 * (or the saved state file was adopted) */
14818 stsFlags |= SaveSTS_StateTimeStamp;
14819 }
14820
14821 if ( ( oldMachineState == MachineState_PoweredOff
14822 || oldMachineState == MachineState_Aborted
14823 || oldMachineState == MachineState_Teleported
14824 )
14825 && aMachineState == MachineState_Saved)
14826 {
14827 /* the saved state file was adopted */
14828 Assert(!mSSData->strStateFilePath.isEmpty());
14829 stsFlags |= SaveSTS_StateFilePath;
14830 }
14831
14832#ifdef VBOX_WITH_GUEST_PROPS
14833 if ( aMachineState == MachineState_PoweredOff
14834 || aMachineState == MachineState_Aborted
14835 || aMachineState == MachineState_Teleported)
14836 {
14837 /* Make sure any transient guest properties get removed from the
14838 * property store on shutdown. */
14839
14840 HWData::GuestPropertyMap::const_iterator it;
14841 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14842 if (!fNeedsSaving)
14843 for (it = mHWData->mGuestProperties.begin();
14844 it != mHWData->mGuestProperties.end(); ++it)
14845 if ( (it->second.mFlags & guestProp::TRANSIENT)
14846 || (it->second.mFlags & guestProp::TRANSRESET))
14847 {
14848 fNeedsSaving = true;
14849 break;
14850 }
14851 if (fNeedsSaving)
14852 {
14853 mData->mCurrentStateModified = TRUE;
14854 stsFlags |= SaveSTS_CurStateModified;
14855 }
14856 }
14857#endif
14858
14859 rc = saveStateSettings(stsFlags);
14860
14861 if ( ( oldMachineState != MachineState_PoweredOff
14862 && oldMachineState != MachineState_Aborted
14863 && oldMachineState != MachineState_Teleported
14864 )
14865 && ( aMachineState == MachineState_PoweredOff
14866 || aMachineState == MachineState_Aborted
14867 || aMachineState == MachineState_Teleported
14868 )
14869 )
14870 {
14871 /* we've been shut down for any reason */
14872 /* no special action so far */
14873 }
14874
14875 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14876 LogFlowThisFuncLeave();
14877 return rc;
14878}
14879
14880/**
14881 * Sends the current machine state value to the VM process.
14882 *
14883 * @note Locks this object for reading, then calls a client process.
14884 */
14885HRESULT SessionMachine::updateMachineStateOnClient()
14886{
14887 AutoCaller autoCaller(this);
14888 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14889
14890 ComPtr<IInternalSessionControl> directControl;
14891 {
14892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14893 AssertReturn(!!mData, E_FAIL);
14894 directControl = mData->mSession.mDirectControl;
14895
14896 /* directControl may be already set to NULL here in #OnSessionEnd()
14897 * called too early by the direct session process while there is still
14898 * some operation (like deleting the snapshot) in progress. The client
14899 * process in this case is waiting inside Session::close() for the
14900 * "end session" process object to complete, while #uninit() called by
14901 * #checkForDeath() on the Watcher thread is waiting for the pending
14902 * operation to complete. For now, we accept this inconsistent behavior
14903 * and simply do nothing here. */
14904
14905 if (mData->mSession.mState == SessionState_Unlocking)
14906 return S_OK;
14907
14908 AssertReturn(!directControl.isNull(), E_FAIL);
14909 }
14910
14911 return directControl->UpdateMachineState(mData->mMachineState);
14912}
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