VirtualBox

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

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

Main/Machine+Session: New generic client session watcher implementation based on token objects, works on all platforms and is used for now on XPCOM. Additionally a better error message when several API clients are racing for a lock, previously it could be quite confusing.

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