VirtualBox

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

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

Main,VBoxManage: Implemented IConsole::EmulatedUSB. Removed IMachine::emulatedUSBWebcameraEnabled.

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

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