VirtualBox

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

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

typo in prev change

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 506.3 KB
Line 
1/* $Id: MachineImpl.cpp 49293 2013-10-25 16:00:53Z 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 mTripleFaultReset = false;
191 mHPETEnabled = false;
192
193 /* default boot order: floppy - DVD - HDD */
194 mBootOrder[0] = DeviceType_Floppy;
195 mBootOrder[1] = DeviceType_DVD;
196 mBootOrder[2] = DeviceType_HardDisk;
197 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
198 mBootOrder[i] = DeviceType_Null;
199
200 mClipboardMode = ClipboardMode_Disabled;
201 mDragAndDropMode = DragAndDropMode_Disabled;
202 mGuestPropertyNotificationPatterns = "";
203
204 mFirmwareType = FirmwareType_BIOS;
205 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
206 mPointingHIDType = PointingHIDType_PS2Mouse;
207 mChipsetType = ChipsetType_PIIX3;
208 mEmulatedUSBCardReaderEnabled = FALSE;
209
210 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
211 mCPUAttached[i] = false;
212
213 mIOCacheEnabled = true;
214 mIOCacheSize = 5; /* 5MB */
215
216 /* Maximum CPU execution cap by default. */
217 mCpuExecutionCap = 100;
218}
219
220Machine::HWData::~HWData()
221{
222}
223
224/////////////////////////////////////////////////////////////////////////////
225// Machine::HDData structure
226/////////////////////////////////////////////////////////////////////////////
227
228Machine::MediaData::MediaData()
229{
230}
231
232Machine::MediaData::~MediaData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine class
238/////////////////////////////////////////////////////////////////////////////
239
240// constructor / destructor
241/////////////////////////////////////////////////////////////////////////////
242
243Machine::Machine() :
244#ifdef VBOX_WITH_RESOURCE_USAGE_API
245 mCollectorGuest(NULL),
246#endif
247 mPeer(NULL),
248 mParent(NULL),
249 mSerialPorts(),
250 mParallelPorts(),
251 uRegistryNeedsSaving(0)
252{}
253
254Machine::~Machine()
255{}
256
257HRESULT Machine::FinalConstruct()
258{
259 LogFlowThisFunc(("\n"));
260 return BaseFinalConstruct();
261}
262
263void Machine::FinalRelease()
264{
265 LogFlowThisFunc(("\n"));
266 uninit();
267 BaseFinalRelease();
268}
269
270/**
271 * Initializes a new machine instance; this init() variant creates a new, empty machine.
272 * This gets called from VirtualBox::CreateMachine().
273 *
274 * @param aParent Associated parent object
275 * @param strConfigFile Local file system path to the VM settings file (can
276 * be relative to the VirtualBox config directory).
277 * @param strName name for the machine
278 * @param llGroups list of groups for the machine
279 * @param aOsType OS Type of this machine or NULL.
280 * @param aId UUID for the new machine.
281 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
282 *
283 * @return Success indicator. if not S_OK, the machine object is invalid
284 */
285HRESULT Machine::init(VirtualBox *aParent,
286 const Utf8Str &strConfigFile,
287 const Utf8Str &strName,
288 const StringsList &llGroups,
289 GuestOSType *aOsType,
290 const Guid &aId,
291 bool fForceOverwrite,
292 bool fDirectoryIncludesUUID)
293{
294 LogFlowThisFuncEnter();
295 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
296
297 /* Enclose the state transition NotReady->InInit->Ready */
298 AutoInitSpan autoInitSpan(this);
299 AssertReturn(autoInitSpan.isOk(), E_FAIL);
300
301 HRESULT rc = initImpl(aParent, strConfigFile);
302 if (FAILED(rc)) return rc;
303
304 rc = tryCreateMachineConfigFile(fForceOverwrite);
305 if (FAILED(rc)) return rc;
306
307 if (SUCCEEDED(rc))
308 {
309 // create an empty machine config
310 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
311
312 rc = initDataAndChildObjects();
313 }
314
315 if (SUCCEEDED(rc))
316 {
317 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
318 mData->mAccessible = TRUE;
319
320 unconst(mData->mUuid) = aId;
321
322 mUserData->s.strName = strName;
323
324 mUserData->s.llGroups = llGroups;
325
326 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
327 // the "name sync" flag determines whether the machine directory gets renamed along
328 // with the machine file; say so if the settings file name is the same as the
329 // settings file parent directory (machine directory)
330 mUserData->s.fNameSync = isInOwnDir();
331
332 // initialize the default snapshots folder
333 rc = COMSETTER(SnapshotFolder)(NULL);
334 AssertComRC(rc);
335
336 if (aOsType)
337 {
338 /* Store OS type */
339 mUserData->s.strOsType = aOsType->id();
340
341 /* Apply BIOS defaults */
342 mBIOSSettings->applyDefaults(aOsType);
343
344 /* Apply network adapters defaults */
345 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
346 mNetworkAdapters[slot]->applyDefaults(aOsType);
347
348 /* Apply serial port defaults */
349 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
350 mSerialPorts[slot]->applyDefaults(aOsType);
351
352 /* Let the OS type select 64-bit ness. */
353 mHWData->mLongMode = aOsType->is64Bit()
354 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
355 }
356
357 /* At this point the changing of the current state modification
358 * flag is allowed. */
359 allowStateModification();
360
361 /* commit all changes made during the initialization */
362 commit();
363 }
364
365 /* Confirm a successful initialization when it's the case */
366 if (SUCCEEDED(rc))
367 {
368 if (mData->mAccessible)
369 autoInitSpan.setSucceeded();
370 else
371 autoInitSpan.setLimited();
372 }
373
374 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
375 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
376 mData->mRegistered,
377 mData->mAccessible,
378 rc));
379
380 LogFlowThisFuncLeave();
381
382 return rc;
383}
384
385/**
386 * Initializes a new instance with data from machine XML (formerly Init_Registered).
387 * Gets called in two modes:
388 *
389 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
390 * UUID is specified and we mark the machine as "registered";
391 *
392 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
393 * and the machine remains unregistered until RegisterMachine() is called.
394 *
395 * @param aParent Associated parent object
396 * @param aConfigFile Local file system path to the VM settings file (can
397 * be relative to the VirtualBox config directory).
398 * @param aId UUID of the machine or NULL (see above).
399 *
400 * @return Success indicator. if not S_OK, the machine object is invalid
401 */
402HRESULT Machine::initFromSettings(VirtualBox *aParent,
403 const Utf8Str &strConfigFile,
404 const Guid *aId)
405{
406 LogFlowThisFuncEnter();
407 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
408
409 /* Enclose the state transition NotReady->InInit->Ready */
410 AutoInitSpan autoInitSpan(this);
411 AssertReturn(autoInitSpan.isOk(), E_FAIL);
412
413 HRESULT rc = initImpl(aParent, strConfigFile);
414 if (FAILED(rc)) return rc;
415
416 if (aId)
417 {
418 // loading a registered VM:
419 unconst(mData->mUuid) = *aId;
420 mData->mRegistered = TRUE;
421 // now load the settings from XML:
422 rc = registeredInit();
423 // this calls initDataAndChildObjects() and loadSettings()
424 }
425 else
426 {
427 // opening an unregistered VM (VirtualBox::OpenMachine()):
428 rc = initDataAndChildObjects();
429
430 if (SUCCEEDED(rc))
431 {
432 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
433 mData->mAccessible = TRUE;
434
435 try
436 {
437 // load and parse machine XML; this will throw on XML or logic errors
438 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
439
440 // reject VM UUID duplicates, they can happen if someone
441 // tries to register an already known VM config again
442 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
443 true /* fPermitInaccessible */,
444 false /* aDoSetError */,
445 NULL) != VBOX_E_OBJECT_NOT_FOUND)
446 {
447 throw setError(E_FAIL,
448 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
449 mData->m_strConfigFile.c_str());
450 }
451
452 // use UUID from machine config
453 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
454
455 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
456 NULL /* puuidRegistry */);
457 if (FAILED(rc)) throw rc;
458
459 /* At this point the changing of the current state modification
460 * flag is allowed. */
461 allowStateModification();
462
463 commit();
464 }
465 catch (HRESULT err)
466 {
467 /* we assume that error info is set by the thrower */
468 rc = err;
469 }
470 catch (...)
471 {
472 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
473 }
474 }
475 }
476
477 /* Confirm a successful initialization when it's the case */
478 if (SUCCEEDED(rc))
479 {
480 if (mData->mAccessible)
481 autoInitSpan.setSucceeded();
482 else
483 {
484 autoInitSpan.setLimited();
485
486 // uninit media from this machine's media registry, or else
487 // reloading the settings will fail
488 mParent->unregisterMachineMedia(getId());
489 }
490 }
491
492 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
493 "rc=%08X\n",
494 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
495 mData->mRegistered, mData->mAccessible, rc));
496
497 LogFlowThisFuncLeave();
498
499 return rc;
500}
501
502/**
503 * Initializes a new instance from a machine config that is already in memory
504 * (import OVF case). Since we are importing, the UUID in the machine
505 * config is ignored and we always generate a fresh one.
506 *
507 * @param strName Name for the new machine; this overrides what is specified in config and is used
508 * for the settings file as well.
509 * @param config Machine configuration loaded and parsed from XML.
510 *
511 * @return Success indicator. if not S_OK, the machine object is invalid
512 */
513HRESULT Machine::init(VirtualBox *aParent,
514 const Utf8Str &strName,
515 const settings::MachineConfigFile &config)
516{
517 LogFlowThisFuncEnter();
518
519 /* Enclose the state transition NotReady->InInit->Ready */
520 AutoInitSpan autoInitSpan(this);
521 AssertReturn(autoInitSpan.isOk(), E_FAIL);
522
523 Utf8Str strConfigFile;
524 aParent->getDefaultMachineFolder(strConfigFile);
525 strConfigFile.append(RTPATH_DELIMITER);
526 strConfigFile.append(strName);
527 strConfigFile.append(RTPATH_DELIMITER);
528 strConfigFile.append(strName);
529 strConfigFile.append(".vbox");
530
531 HRESULT rc = initImpl(aParent, strConfigFile);
532 if (FAILED(rc)) return rc;
533
534 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
535 if (FAILED(rc)) return rc;
536
537 rc = initDataAndChildObjects();
538
539 if (SUCCEEDED(rc))
540 {
541 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
542 mData->mAccessible = TRUE;
543
544 // create empty machine config for instance data
545 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
546
547 // generate fresh UUID, ignore machine config
548 unconst(mData->mUuid).create();
549
550 rc = loadMachineDataFromSettings(config,
551 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
552
553 // override VM name as well, it may be different
554 mUserData->s.strName = strName;
555
556 if (SUCCEEDED(rc))
557 {
558 /* At this point the changing of the current state modification
559 * flag is allowed. */
560 allowStateModification();
561
562 /* commit all changes made during the initialization */
563 commit();
564 }
565 }
566
567 /* Confirm a successful initialization when it's the case */
568 if (SUCCEEDED(rc))
569 {
570 if (mData->mAccessible)
571 autoInitSpan.setSucceeded();
572 else
573 {
574 autoInitSpan.setLimited();
575
576 // uninit media from this machine's media registry, or else
577 // reloading the settings will fail
578 mParent->unregisterMachineMedia(getId());
579 }
580 }
581
582 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
583 "rc=%08X\n",
584 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
585 mData->mRegistered, mData->mAccessible, rc));
586
587 LogFlowThisFuncLeave();
588
589 return rc;
590}
591
592/**
593 * Shared code between the various init() implementations.
594 * @param aParent
595 * @return
596 */
597HRESULT Machine::initImpl(VirtualBox *aParent,
598 const Utf8Str &strConfigFile)
599{
600 LogFlowThisFuncEnter();
601
602 AssertReturn(aParent, E_INVALIDARG);
603 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
604
605 HRESULT rc = S_OK;
606
607 /* share the parent weakly */
608 unconst(mParent) = aParent;
609
610 /* allocate the essential machine data structure (the rest will be
611 * allocated later by initDataAndChildObjects() */
612 mData.allocate();
613
614 /* memorize the config file name (as provided) */
615 mData->m_strConfigFile = strConfigFile;
616
617 /* get the full file name */
618 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
619 if (RT_FAILURE(vrc1))
620 return setError(VBOX_E_FILE_ERROR,
621 tr("Invalid machine settings file name '%s' (%Rrc)"),
622 strConfigFile.c_str(),
623 vrc1);
624
625 LogFlowThisFuncLeave();
626
627 return rc;
628}
629
630/**
631 * Tries to create a machine settings file in the path stored in the machine
632 * instance data. Used when a new machine is created to fail gracefully if
633 * the settings file could not be written (e.g. because machine dir is read-only).
634 * @return
635 */
636HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
637{
638 HRESULT rc = S_OK;
639
640 // when we create a new machine, we must be able to create the settings file
641 RTFILE f = NIL_RTFILE;
642 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
643 if ( RT_SUCCESS(vrc)
644 || vrc == VERR_SHARING_VIOLATION
645 )
646 {
647 if (RT_SUCCESS(vrc))
648 RTFileClose(f);
649 if (!fForceOverwrite)
650 rc = setError(VBOX_E_FILE_ERROR,
651 tr("Machine settings file '%s' already exists"),
652 mData->m_strConfigFileFull.c_str());
653 else
654 {
655 /* try to delete the config file, as otherwise the creation
656 * of a new settings file will fail. */
657 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
658 if (RT_FAILURE(vrc2))
659 rc = setError(VBOX_E_FILE_ERROR,
660 tr("Could not delete the existing settings file '%s' (%Rrc)"),
661 mData->m_strConfigFileFull.c_str(), vrc2);
662 }
663 }
664 else if ( vrc != VERR_FILE_NOT_FOUND
665 && vrc != VERR_PATH_NOT_FOUND
666 )
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Invalid machine settings file name '%s' (%Rrc)"),
669 mData->m_strConfigFileFull.c_str(),
670 vrc);
671 return rc;
672}
673
674/**
675 * Initializes the registered machine by loading the settings file.
676 * This method is separated from #init() in order to make it possible to
677 * retry the operation after VirtualBox startup instead of refusing to
678 * startup the whole VirtualBox server in case if the settings file of some
679 * registered VM is invalid or inaccessible.
680 *
681 * @note Must be always called from this object's write lock
682 * (unless called from #init() that doesn't need any locking).
683 * @note Locks the mUSBController method for writing.
684 * @note Subclasses must not call this method.
685 */
686HRESULT Machine::registeredInit()
687{
688 AssertReturn(!isSessionMachine(), E_FAIL);
689 AssertReturn(!isSnapshotMachine(), E_FAIL);
690 AssertReturn(mData->mUuid.isValid(), E_FAIL);
691 AssertReturn(!mData->mAccessible, E_FAIL);
692
693 HRESULT rc = initDataAndChildObjects();
694
695 if (SUCCEEDED(rc))
696 {
697 /* Temporarily reset the registered flag in order to let setters
698 * potentially called from loadSettings() succeed (isMutable() used in
699 * all setters will return FALSE for a Machine instance if mRegistered
700 * is TRUE). */
701 mData->mRegistered = FALSE;
702
703 try
704 {
705 // load and parse machine XML; this will throw on XML or logic errors
706 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
707
708 if (mData->mUuid != mData->pMachineConfigFile->uuid)
709 throw setError(E_FAIL,
710 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
711 mData->pMachineConfigFile->uuid.raw(),
712 mData->m_strConfigFileFull.c_str(),
713 mData->mUuid.toString().c_str(),
714 mParent->settingsFilePath().c_str());
715
716 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
717 NULL /* const Guid *puuidRegistry */);
718 if (FAILED(rc)) throw rc;
719 }
720 catch (HRESULT err)
721 {
722 /* we assume that error info is set by the thrower */
723 rc = err;
724 }
725 catch (...)
726 {
727 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
728 }
729
730 /* Restore the registered flag (even on failure) */
731 mData->mRegistered = TRUE;
732 }
733
734 if (SUCCEEDED(rc))
735 {
736 /* Set mAccessible to TRUE only if we successfully locked and loaded
737 * the settings file */
738 mData->mAccessible = TRUE;
739
740 /* commit all changes made during loading the settings file */
741 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
742 /// @todo r=klaus for some reason the settings loading logic backs up
743 // the settings, and therefore a commit is needed. Should probably be changed.
744 }
745 else
746 {
747 /* If the machine is registered, then, instead of returning a
748 * failure, we mark it as inaccessible and set the result to
749 * success to give it a try later */
750
751 /* fetch the current error info */
752 mData->mAccessError = com::ErrorInfo();
753 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
754 mData->mUuid.raw(),
755 mData->mAccessError.getText().raw()));
756
757 /* rollback all changes */
758 rollback(false /* aNotify */);
759
760 // uninit media from this machine's media registry, or else
761 // reloading the settings will fail
762 mParent->unregisterMachineMedia(getId());
763
764 /* uninitialize the common part to make sure all data is reset to
765 * default (null) values */
766 uninitDataAndChildObjects();
767
768 rc = S_OK;
769 }
770
771 return rc;
772}
773
774/**
775 * Uninitializes the instance.
776 * Called either from FinalRelease() or by the parent when it gets destroyed.
777 *
778 * @note The caller of this method must make sure that this object
779 * a) doesn't have active callers on the current thread and b) is not locked
780 * by the current thread; otherwise uninit() will hang either a) due to
781 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
782 * a dead-lock caused by this thread waiting for all callers on the other
783 * threads are done but preventing them from doing so by holding a lock.
784 */
785void Machine::uninit()
786{
787 LogFlowThisFuncEnter();
788
789 Assert(!isWriteLockOnCurrentThread());
790
791 Assert(!uRegistryNeedsSaving);
792 if (uRegistryNeedsSaving)
793 {
794 AutoCaller autoCaller(this);
795 if (SUCCEEDED(autoCaller.rc()))
796 {
797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
798 saveSettings(NULL, Machine::SaveS_Force);
799 }
800 }
801
802 /* Enclose the state transition Ready->InUninit->NotReady */
803 AutoUninitSpan autoUninitSpan(this);
804 if (autoUninitSpan.uninitDone())
805 return;
806
807 Assert(!isSnapshotMachine());
808 Assert(!isSessionMachine());
809 Assert(!!mData);
810
811 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
812 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
813
814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
815
816 if (!mData->mSession.mMachine.isNull())
817 {
818 /* Theoretically, this can only happen if the VirtualBox server has been
819 * terminated while there were clients running that owned open direct
820 * sessions. Since in this case we are definitely called by
821 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
822 * won't happen on the client watcher thread (because it does
823 * VirtualBox::addCaller() for the duration of the
824 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
825 * cannot happen until the VirtualBox caller is released). This is
826 * important, because SessionMachine::uninit() cannot correctly operate
827 * after we return from this method (it expects the Machine instance is
828 * still valid). We'll call it ourselves below.
829 */
830 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
831 (SessionMachine*)mData->mSession.mMachine));
832
833 if (Global::IsOnlineOrTransient(mData->mMachineState))
834 {
835 LogWarningThisFunc(("Setting state to Aborted!\n"));
836 /* set machine state using SessionMachine reimplementation */
837 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
838 }
839
840 /*
841 * Uninitialize SessionMachine using public uninit() to indicate
842 * an unexpected uninitialization.
843 */
844 mData->mSession.mMachine->uninit();
845 /* SessionMachine::uninit() must set mSession.mMachine to null */
846 Assert(mData->mSession.mMachine.isNull());
847 }
848
849 // uninit media from this machine's media registry, if they're still there
850 Guid uuidMachine(getId());
851
852 /* the lock is no more necessary (SessionMachine is uninitialized) */
853 alock.release();
854
855 /* XXX This will fail with
856 * "cannot be closed because it is still attached to 1 virtual machines"
857 * because at this point we did not call uninitDataAndChildObjects() yet
858 * and therefore also removeBackReference() for all these mediums was not called! */
859
860 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
861 mParent->unregisterMachineMedia(uuidMachine);
862
863 // has machine been modified?
864 if (mData->flModifications)
865 {
866 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
867 rollback(false /* aNotify */);
868 }
869
870 if (mData->mAccessible)
871 uninitDataAndChildObjects();
872
873 /* free the essential data structure last */
874 mData.free();
875
876 LogFlowThisFuncLeave();
877}
878
879// IMachine properties
880/////////////////////////////////////////////////////////////////////////////
881
882STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
883{
884 CheckComArgOutPointerValid(aParent);
885
886 AutoLimitedCaller autoCaller(this);
887 if (FAILED(autoCaller.rc())) return autoCaller.rc();
888
889 /* mParent is constant during life time, no need to lock */
890 ComObjPtr<VirtualBox> pVirtualBox(mParent);
891 pVirtualBox.queryInterfaceTo(aParent);
892
893 return S_OK;
894}
895
896STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
897{
898 CheckComArgOutPointerValid(aAccessible);
899
900 AutoLimitedCaller autoCaller(this);
901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
902
903 LogFlowThisFunc(("ENTER\n"));
904
905 /* In some cases (medium registry related), it is necessary to be able to
906 * go through the list of all machines. Happens when an inaccessible VM
907 * has a sensible medium registry. */
908 AutoReadLock mllock(mParent->getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
955{
956 CheckComArgOutPointerValid(aAccessError);
957
958 AutoLimitedCaller autoCaller(this);
959 if (FAILED(autoCaller.rc())) return autoCaller.rc();
960
961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
962
963 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
964 {
965 /* return shortly */
966 aAccessError = NULL;
967 return S_OK;
968 }
969
970 HRESULT rc = S_OK;
971
972 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
973 rc = errorInfo.createObject();
974 if (SUCCEEDED(rc))
975 {
976 errorInfo->init(mData->mAccessError.getResultCode(),
977 mData->mAccessError.getInterfaceID().ref(),
978 Utf8Str(mData->mAccessError.getComponent()).c_str(),
979 Utf8Str(mData->mAccessError.getText()));
980 rc = errorInfo.queryInterfaceTo(aAccessError);
981 }
982
983 return rc;
984}
985
986STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
987{
988 CheckComArgOutPointerValid(aName);
989
990 AutoCaller autoCaller(this);
991 if (FAILED(autoCaller.rc())) return autoCaller.rc();
992
993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 mUserData->s.strName.cloneTo(aName);
996
997 return S_OK;
998}
999
1000STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1001{
1002 CheckComArgStrNotEmptyOrNull(aName);
1003
1004 AutoCaller autoCaller(this);
1005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1006
1007 // prohibit setting a UUID only as the machine name, or else it can
1008 // never be found by findMachine()
1009 Guid test(aName);
1010
1011 if (test.isValid())
1012 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1013
1014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1015
1016 HRESULT rc = checkStateDependency(MutableStateDep);
1017 if (FAILED(rc)) return rc;
1018
1019 setModified(IsModified_MachineData);
1020 mUserData.backup();
1021 mUserData->s.strName = aName;
1022
1023 return S_OK;
1024}
1025
1026STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1027{
1028 CheckComArgOutPointerValid(aDescription);
1029
1030 AutoCaller autoCaller(this);
1031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1032
1033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 mUserData->s.strDescription.cloneTo(aDescription);
1036
1037 return S_OK;
1038}
1039
1040STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1041{
1042 AutoCaller autoCaller(this);
1043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1044
1045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1046
1047 // this can be done in principle in any state as it doesn't affect the VM
1048 // significantly, but play safe by not messing around while complex
1049 // activities are going on
1050 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1051 if (FAILED(rc)) return rc;
1052
1053 setModified(IsModified_MachineData);
1054 mUserData.backup();
1055 mUserData->s.strDescription = aDescription;
1056
1057 return S_OK;
1058}
1059
1060STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1061{
1062 CheckComArgOutPointerValid(aId);
1063
1064 AutoLimitedCaller autoCaller(this);
1065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1066
1067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1068
1069 mData->mUuid.toUtf16().cloneTo(aId);
1070
1071 return S_OK;
1072}
1073
1074STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1075{
1076 CheckComArgOutSafeArrayPointerValid(aGroups);
1077
1078 AutoCaller autoCaller(this);
1079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1080
1081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1082 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1083 size_t i = 0;
1084 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1085 it != mUserData->s.llGroups.end();
1086 ++it, i++)
1087 {
1088 Bstr tmp = *it;
1089 tmp.cloneTo(&groups[i]);
1090 }
1091 groups.detachTo(ComSafeArrayOutArg(aGroups));
1092
1093 return S_OK;
1094}
1095
1096STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1097{
1098 AutoCaller autoCaller(this);
1099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1100
1101 StringsList llGroups;
1102 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1103 if (FAILED(rc))
1104 return rc;
1105
1106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1107
1108 // changing machine groups is possible while the VM is offline
1109 rc = checkStateDependency(OfflineStateDep);
1110 if (FAILED(rc)) return rc;
1111
1112 setModified(IsModified_MachineData);
1113 mUserData.backup();
1114 mUserData->s.llGroups = llGroups;
1115
1116 return S_OK;
1117}
1118
1119STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1120{
1121 CheckComArgOutPointerValid(aOSTypeId);
1122
1123 AutoCaller autoCaller(this);
1124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1125
1126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1127
1128 mUserData->s.strOsType.cloneTo(aOSTypeId);
1129
1130 return S_OK;
1131}
1132
1133STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1134{
1135 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1136
1137 AutoCaller autoCaller(this);
1138 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1139
1140 /* look up the object by Id to check it is valid */
1141 ComPtr<IGuestOSType> guestOSType;
1142 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1143 if (FAILED(rc)) return rc;
1144
1145 /* when setting, always use the "etalon" value for consistency -- lookup
1146 * by ID is case-insensitive and the input value may have different case */
1147 Bstr osTypeId;
1148 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1149 if (FAILED(rc)) return rc;
1150
1151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1152
1153 rc = checkStateDependency(MutableStateDep);
1154 if (FAILED(rc)) return rc;
1155
1156 setModified(IsModified_MachineData);
1157 mUserData.backup();
1158 mUserData->s.strOsType = osTypeId;
1159
1160 return S_OK;
1161}
1162
1163
1164STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1165{
1166 CheckComArgOutPointerValid(aFirmwareType);
1167
1168 AutoCaller autoCaller(this);
1169 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1170
1171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1172
1173 *aFirmwareType = mHWData->mFirmwareType;
1174
1175 return S_OK;
1176}
1177
1178STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1179{
1180 AutoCaller autoCaller(this);
1181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 HRESULT rc = checkStateDependency(MutableStateDep);
1185 if (FAILED(rc)) return rc;
1186
1187 setModified(IsModified_MachineData);
1188 mHWData.backup();
1189 mHWData->mFirmwareType = aFirmwareType;
1190
1191 return S_OK;
1192}
1193
1194STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1195{
1196 CheckComArgOutPointerValid(aKeyboardHIDType);
1197
1198 AutoCaller autoCaller(this);
1199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1200
1201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1204
1205 return S_OK;
1206}
1207
1208STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1209{
1210 AutoCaller autoCaller(this);
1211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1213
1214 HRESULT rc = checkStateDependency(MutableStateDep);
1215 if (FAILED(rc)) return rc;
1216
1217 setModified(IsModified_MachineData);
1218 mHWData.backup();
1219 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1220
1221 return S_OK;
1222}
1223
1224STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1225{
1226 CheckComArgOutPointerValid(aPointingHIDType);
1227
1228 AutoCaller autoCaller(this);
1229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1230
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 *aPointingHIDType = mHWData->mPointingHIDType;
1234
1235 return S_OK;
1236}
1237
1238STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1239{
1240 AutoCaller autoCaller(this);
1241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1243
1244 HRESULT rc = checkStateDependency(MutableStateDep);
1245 if (FAILED(rc)) return rc;
1246
1247 setModified(IsModified_MachineData);
1248 mHWData.backup();
1249 mHWData->mPointingHIDType = aPointingHIDType;
1250
1251 return S_OK;
1252}
1253
1254STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1255{
1256 CheckComArgOutPointerValid(aChipsetType);
1257
1258 AutoCaller autoCaller(this);
1259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1260
1261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 *aChipsetType = mHWData->mChipsetType;
1264
1265 return S_OK;
1266}
1267
1268STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1269{
1270 AutoCaller autoCaller(this);
1271 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1273
1274 HRESULT rc = checkStateDependency(MutableStateDep);
1275 if (FAILED(rc)) return rc;
1276
1277 if (aChipsetType != mHWData->mChipsetType)
1278 {
1279 setModified(IsModified_MachineData);
1280 mHWData.backup();
1281 mHWData->mChipsetType = aChipsetType;
1282
1283 // Resize network adapter array, to be finalized on commit/rollback.
1284 // We must not throw away entries yet, otherwise settings are lost
1285 // without a way to roll back.
1286 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1287 size_t oldCount = mNetworkAdapters.size();
1288 if (newCount > oldCount)
1289 {
1290 mNetworkAdapters.resize(newCount);
1291 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1292 {
1293 unconst(mNetworkAdapters[slot]).createObject();
1294 mNetworkAdapters[slot]->init(this, slot);
1295 }
1296 }
1297 }
1298
1299 return S_OK;
1300}
1301
1302STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1303{
1304 CheckComArgOutPointerValid(aHWVersion);
1305
1306 AutoCaller autoCaller(this);
1307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1308
1309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1310
1311 mHWData->mHWVersion.cloneTo(aHWVersion);
1312
1313 return S_OK;
1314}
1315
1316STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1317{
1318 /* check known version */
1319 Utf8Str hwVersion = aHWVersion;
1320 if ( hwVersion.compare("1") != 0
1321 && hwVersion.compare("2") != 0)
1322 return setError(E_INVALIDARG,
1323 tr("Invalid hardware version: %ls\n"), aHWVersion);
1324
1325 AutoCaller autoCaller(this);
1326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1327
1328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1329
1330 HRESULT rc = checkStateDependency(MutableStateDep);
1331 if (FAILED(rc)) return rc;
1332
1333 setModified(IsModified_MachineData);
1334 mHWData.backup();
1335 mHWData->mHWVersion = hwVersion;
1336
1337 return S_OK;
1338}
1339
1340STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1341{
1342 CheckComArgOutPointerValid(aUUID);
1343
1344 AutoCaller autoCaller(this);
1345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1346
1347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1348
1349 if (!mHWData->mHardwareUUID.isZero())
1350 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1351 else
1352 mData->mUuid.toUtf16().cloneTo(aUUID);
1353
1354 return S_OK;
1355}
1356
1357STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1358{
1359 Guid hardwareUUID(aUUID);
1360 if (!hardwareUUID.isValid())
1361 return E_INVALIDARG;
1362
1363 AutoCaller autoCaller(this);
1364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1365
1366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1367
1368 HRESULT rc = checkStateDependency(MutableStateDep);
1369 if (FAILED(rc)) return rc;
1370
1371 setModified(IsModified_MachineData);
1372 mHWData.backup();
1373 if (hardwareUUID == mData->mUuid)
1374 mHWData->mHardwareUUID.clear();
1375 else
1376 mHWData->mHardwareUUID = hardwareUUID;
1377
1378 return S_OK;
1379}
1380
1381STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1382{
1383 CheckComArgOutPointerValid(memorySize);
1384
1385 AutoCaller autoCaller(this);
1386 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1387
1388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1389
1390 *memorySize = mHWData->mMemorySize;
1391
1392 return S_OK;
1393}
1394
1395STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1396{
1397 /* check RAM limits */
1398 if ( memorySize < MM_RAM_MIN_IN_MB
1399 || memorySize > MM_RAM_MAX_IN_MB
1400 )
1401 return setError(E_INVALIDARG,
1402 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1403 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1404
1405 AutoCaller autoCaller(this);
1406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1407
1408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 HRESULT rc = checkStateDependency(MutableStateDep);
1411 if (FAILED(rc)) return rc;
1412
1413 setModified(IsModified_MachineData);
1414 mHWData.backup();
1415 mHWData->mMemorySize = memorySize;
1416
1417 return S_OK;
1418}
1419
1420STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1421{
1422 CheckComArgOutPointerValid(CPUCount);
1423
1424 AutoCaller autoCaller(this);
1425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1426
1427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1428
1429 *CPUCount = mHWData->mCPUCount;
1430
1431 return S_OK;
1432}
1433
1434STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1435{
1436 /* check CPU limits */
1437 if ( CPUCount < SchemaDefs::MinCPUCount
1438 || CPUCount > SchemaDefs::MaxCPUCount
1439 )
1440 return setError(E_INVALIDARG,
1441 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1442 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1443
1444 AutoCaller autoCaller(this);
1445 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1446
1447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1448
1449 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1450 if (mHWData->mCPUHotPlugEnabled)
1451 {
1452 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1453 {
1454 if (mHWData->mCPUAttached[idx])
1455 return setError(E_INVALIDARG,
1456 tr("There is still a CPU attached to socket %lu."
1457 "Detach the CPU before removing the socket"),
1458 CPUCount, idx+1);
1459 }
1460 }
1461
1462 HRESULT rc = checkStateDependency(MutableStateDep);
1463 if (FAILED(rc)) return rc;
1464
1465 setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mCPUCount = CPUCount;
1468
1469 return S_OK;
1470}
1471
1472STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1473{
1474 CheckComArgOutPointerValid(aExecutionCap);
1475
1476 AutoCaller autoCaller(this);
1477 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1478
1479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1480
1481 *aExecutionCap = mHWData->mCpuExecutionCap;
1482
1483 return S_OK;
1484}
1485
1486STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1487{
1488 HRESULT rc = S_OK;
1489
1490 /* check throttle limits */
1491 if ( aExecutionCap < 1
1492 || aExecutionCap > 100
1493 )
1494 return setError(E_INVALIDARG,
1495 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1496 aExecutionCap, 1, 100);
1497
1498 AutoCaller autoCaller(this);
1499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1500
1501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1502
1503 alock.release();
1504 rc = onCPUExecutionCapChange(aExecutionCap);
1505 alock.acquire();
1506 if (FAILED(rc)) return rc;
1507
1508 setModified(IsModified_MachineData);
1509 mHWData.backup();
1510 mHWData->mCpuExecutionCap = aExecutionCap;
1511
1512 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1513 if (Global::IsOnline(mData->mMachineState))
1514 saveSettings(NULL);
1515
1516 return S_OK;
1517}
1518
1519
1520STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1521{
1522 CheckComArgOutPointerValid(aEnabled);
1523
1524 AutoCaller autoCaller(this);
1525 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1526
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aEnabled = mHWData->mCPUHotPlugEnabled;
1530
1531 return S_OK;
1532}
1533
1534STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1535{
1536 HRESULT rc = S_OK;
1537
1538 AutoCaller autoCaller(this);
1539 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1540
1541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1542
1543 rc = checkStateDependency(MutableStateDep);
1544 if (FAILED(rc)) return rc;
1545
1546 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1547 {
1548 if (aEnabled)
1549 {
1550 setModified(IsModified_MachineData);
1551 mHWData.backup();
1552
1553 /* Add the amount of CPUs currently attached */
1554 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1555 {
1556 mHWData->mCPUAttached[i] = true;
1557 }
1558 }
1559 else
1560 {
1561 /*
1562 * We can disable hotplug only if the amount of maximum CPUs is equal
1563 * to the amount of attached CPUs
1564 */
1565 unsigned cCpusAttached = 0;
1566 unsigned iHighestId = 0;
1567
1568 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1569 {
1570 if (mHWData->mCPUAttached[i])
1571 {
1572 cCpusAttached++;
1573 iHighestId = i;
1574 }
1575 }
1576
1577 if ( (cCpusAttached != mHWData->mCPUCount)
1578 || (iHighestId >= mHWData->mCPUCount))
1579 return setError(E_INVALIDARG,
1580 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1581
1582 setModified(IsModified_MachineData);
1583 mHWData.backup();
1584 }
1585 }
1586
1587 mHWData->mCPUHotPlugEnabled = aEnabled;
1588
1589 return rc;
1590}
1591
1592STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1593{
1594#ifdef VBOX_WITH_USB_CARDREADER
1595 CheckComArgOutPointerValid(aEnabled);
1596
1597 AutoCaller autoCaller(this);
1598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1599
1600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1601
1602 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1603
1604 return S_OK;
1605#else
1606 NOREF(aEnabled);
1607 return E_NOTIMPL;
1608#endif
1609}
1610
1611STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1612{
1613#ifdef VBOX_WITH_USB_CARDREADER
1614 AutoCaller autoCaller(this);
1615 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 HRESULT rc = checkStateDependency(MutableStateDep);
1619 if (FAILED(rc)) return rc;
1620
1621 setModified(IsModified_MachineData);
1622 mHWData.backup();
1623 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1624
1625 return S_OK;
1626#else
1627 NOREF(aEnabled);
1628 return E_NOTIMPL;
1629#endif
1630}
1631
1632STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1633{
1634 CheckComArgOutPointerValid(aEnabled);
1635
1636 AutoCaller autoCaller(this);
1637 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1639
1640 *aEnabled = mHWData->mHPETEnabled;
1641
1642 return S_OK;
1643}
1644
1645STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1646{
1647 HRESULT rc = S_OK;
1648
1649 AutoCaller autoCaller(this);
1650 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1652
1653 rc = checkStateDependency(MutableStateDep);
1654 if (FAILED(rc)) return rc;
1655
1656 setModified(IsModified_MachineData);
1657 mHWData.backup();
1658
1659 mHWData->mHPETEnabled = aEnabled;
1660
1661 return rc;
1662}
1663
1664STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1665{
1666 AutoCaller autoCaller(this);
1667 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1668
1669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1670
1671 *fEnabled = mHWData->mVideoCaptureEnabled;
1672 return S_OK;
1673}
1674
1675STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1676{
1677 HRESULT rc = S_OK;
1678
1679 AutoCaller autoCaller(this);
1680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 setModified(IsModified_MachineData);
1684 mHWData.backup();
1685 mHWData->mVideoCaptureEnabled = fEnabled;
1686
1687 alock.release();
1688 rc = onVideoCaptureChange();
1689 alock.acquire();
1690 if (FAILED(rc))
1691 {
1692 /*
1693 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1694 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1695 * determine if it should start or stop capturing. Therefore we need to manually
1696 * undo change.
1697 */
1698 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1699 return rc;
1700 }
1701
1702 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1703 if (Global::IsOnline(mData->mMachineState))
1704 saveSettings(NULL);
1705
1706 return rc;
1707}
1708
1709STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1710{
1711 CheckComArgOutSafeArrayPointerValid(aScreens);
1712
1713 AutoCaller autoCaller(this);
1714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1715
1716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1717
1718 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1719 for (unsigned i = 0; i < screens.size(); i++)
1720 screens[i] = mHWData->maVideoCaptureScreens[i];
1721 screens.detachTo(ComSafeArrayOutArg(aScreens));
1722 return S_OK;
1723}
1724
1725STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1726{
1727 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1728 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1729 bool fChanged = false;
1730
1731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1732
1733 for (unsigned i = 0; i < screens.size(); i++)
1734 {
1735 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1736 {
1737 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1738 fChanged = true;
1739 }
1740 }
1741 if (fChanged)
1742 {
1743 alock.release();
1744 HRESULT rc = onVideoCaptureChange();
1745 alock.acquire();
1746 if (FAILED(rc)) return rc;
1747 setModified(IsModified_MachineData);
1748
1749 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1750 if (Global::IsOnline(mData->mMachineState))
1751 saveSettings(NULL);
1752 }
1753
1754 return S_OK;
1755}
1756
1757STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1758{
1759 AutoCaller autoCaller(this);
1760 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1761
1762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1763 if (mHWData->mVideoCaptureFile.isEmpty())
1764 {
1765 Utf8Str defaultFile;
1766 getDefaultVideoCaptureFile(defaultFile);
1767 defaultFile.cloneTo(apFile);
1768 }
1769 else
1770 mHWData->mVideoCaptureFile.cloneTo(apFile);
1771 return S_OK;
1772}
1773
1774STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1775{
1776 Utf8Str strFile(aFile);
1777 AutoCaller autoCaller(this);
1778 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1779
1780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1781
1782 if ( Global::IsOnline(mData->mMachineState)
1783 && mHWData->mVideoCaptureEnabled)
1784 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1785
1786 if (!RTPathStartsWithRoot(strFile.c_str()))
1787 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1788
1789 if (!strFile.isEmpty())
1790 {
1791 Utf8Str defaultFile;
1792 getDefaultVideoCaptureFile(defaultFile);
1793 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1794 strFile.setNull();
1795 }
1796
1797 setModified(IsModified_MachineData);
1798 mHWData.backup();
1799 mHWData->mVideoCaptureFile = strFile;
1800
1801 return S_OK;
1802}
1803
1804STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1805{
1806 AutoCaller autoCaller(this);
1807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1808
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 *aHorzRes = mHWData->mVideoCaptureWidth;
1811 return S_OK;
1812}
1813
1814STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1815{
1816 AutoCaller autoCaller(this);
1817 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1818
1819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1820
1821 if ( Global::IsOnline(mData->mMachineState)
1822 && mHWData->mVideoCaptureEnabled)
1823 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1824
1825 setModified(IsModified_MachineData);
1826 mHWData.backup();
1827 mHWData->mVideoCaptureWidth = aHorzRes;
1828
1829 return S_OK;
1830}
1831
1832STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1833{
1834 AutoCaller autoCaller(this);
1835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1836
1837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1838 *aVertRes = mHWData->mVideoCaptureHeight;
1839 return S_OK;
1840}
1841
1842STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1843{
1844 AutoCaller autoCaller(this);
1845 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1846
1847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1848
1849 if ( Global::IsOnline(mData->mMachineState)
1850 && mHWData->mVideoCaptureEnabled)
1851 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1852
1853 setModified(IsModified_MachineData);
1854 mHWData.backup();
1855 mHWData->mVideoCaptureHeight = aVertRes;
1856
1857 return S_OK;
1858}
1859
1860STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1861{
1862 AutoCaller autoCaller(this);
1863 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1864
1865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1866 *aRate = mHWData->mVideoCaptureRate;
1867 return S_OK;
1868}
1869
1870STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1871{
1872 AutoCaller autoCaller(this);
1873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1874
1875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 if ( Global::IsOnline(mData->mMachineState)
1878 && mHWData->mVideoCaptureEnabled)
1879 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1880
1881 setModified(IsModified_MachineData);
1882 mHWData.backup();
1883 mHWData->mVideoCaptureRate = aRate;
1884
1885 return S_OK;
1886}
1887
1888STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1889{
1890 AutoCaller autoCaller(this);
1891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1892
1893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1894 *aFPS = mHWData->mVideoCaptureFPS;
1895 return S_OK;
1896}
1897
1898STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1899{
1900 AutoCaller autoCaller(this);
1901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1902
1903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1904
1905 if ( Global::IsOnline(mData->mMachineState)
1906 && mHWData->mVideoCaptureEnabled)
1907 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1908
1909 setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mVideoCaptureFPS = aFPS;
1912
1913 return S_OK;
1914}
1915
1916STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1917{
1918 CheckComArgOutPointerValid(aGraphicsControllerType);
1919
1920 AutoCaller autoCaller(this);
1921 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1922
1923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1924
1925 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1926
1927 return S_OK;
1928}
1929
1930STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1931{
1932 switch (aGraphicsControllerType)
1933 {
1934 case GraphicsControllerType_Null:
1935 case GraphicsControllerType_VBoxVGA:
1936 break;
1937 default:
1938 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1939 }
1940
1941 AutoCaller autoCaller(this);
1942 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1943
1944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1945
1946 HRESULT rc = checkStateDependency(MutableStateDep);
1947 if (FAILED(rc)) return rc;
1948
1949 setModified(IsModified_MachineData);
1950 mHWData.backup();
1951 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1952
1953 return S_OK;
1954}
1955
1956STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1957{
1958 CheckComArgOutPointerValid(memorySize);
1959
1960 AutoCaller autoCaller(this);
1961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1962
1963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1964
1965 *memorySize = mHWData->mVRAMSize;
1966
1967 return S_OK;
1968}
1969
1970STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1971{
1972 /* check VRAM limits */
1973 if (memorySize < SchemaDefs::MinGuestVRAM ||
1974 memorySize > SchemaDefs::MaxGuestVRAM)
1975 return setError(E_INVALIDARG,
1976 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1977 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1978
1979 AutoCaller autoCaller(this);
1980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1981
1982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1983
1984 HRESULT rc = checkStateDependency(MutableStateDep);
1985 if (FAILED(rc)) return rc;
1986
1987 setModified(IsModified_MachineData);
1988 mHWData.backup();
1989 mHWData->mVRAMSize = memorySize;
1990
1991 return S_OK;
1992}
1993
1994/** @todo this method should not be public */
1995STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1996{
1997 CheckComArgOutPointerValid(memoryBalloonSize);
1998
1999 AutoCaller autoCaller(this);
2000 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2001
2002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2003
2004 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2005
2006 return S_OK;
2007}
2008
2009/**
2010 * Set the memory balloon size.
2011 *
2012 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2013 * we have to make sure that we never call IGuest from here.
2014 */
2015STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2016{
2017 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2018#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2019 /* check limits */
2020 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2021 return setError(E_INVALIDARG,
2022 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2023 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2024
2025 AutoCaller autoCaller(this);
2026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2027
2028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2029
2030 setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2033
2034 return S_OK;
2035#else
2036 NOREF(memoryBalloonSize);
2037 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2038#endif
2039}
2040
2041STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2042{
2043 CheckComArgOutPointerValid(aEnabled);
2044
2045 AutoCaller autoCaller(this);
2046 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2047
2048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 *aEnabled = mHWData->mPageFusionEnabled;
2051 return S_OK;
2052}
2053
2054STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2055{
2056#ifdef VBOX_WITH_PAGE_SHARING
2057 AutoCaller autoCaller(this);
2058 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2059
2060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2061
2062 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2063 setModified(IsModified_MachineData);
2064 mHWData.backup();
2065 mHWData->mPageFusionEnabled = aEnabled;
2066 return S_OK;
2067#else
2068 NOREF(aEnabled);
2069 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2070#endif
2071}
2072
2073STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2074{
2075 CheckComArgOutPointerValid(aEnabled);
2076
2077 AutoCaller autoCaller(this);
2078 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2079
2080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2081
2082 *aEnabled = mHWData->mAccelerate3DEnabled;
2083
2084 return S_OK;
2085}
2086
2087STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2088{
2089 AutoCaller autoCaller(this);
2090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2091
2092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2093
2094 HRESULT rc = checkStateDependency(MutableStateDep);
2095 if (FAILED(rc)) return rc;
2096
2097 /** @todo check validity! */
2098
2099 setModified(IsModified_MachineData);
2100 mHWData.backup();
2101 mHWData->mAccelerate3DEnabled = enable;
2102
2103 return S_OK;
2104}
2105
2106
2107STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2108{
2109 CheckComArgOutPointerValid(aEnabled);
2110
2111 AutoCaller autoCaller(this);
2112 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2113
2114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2115
2116 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2117
2118 return S_OK;
2119}
2120
2121STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2122{
2123 AutoCaller autoCaller(this);
2124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2125
2126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2127
2128 HRESULT rc = checkStateDependency(MutableStateDep);
2129 if (FAILED(rc)) return rc;
2130
2131 /** @todo check validity! */
2132
2133 setModified(IsModified_MachineData);
2134 mHWData.backup();
2135 mHWData->mAccelerate2DVideoEnabled = enable;
2136
2137 return S_OK;
2138}
2139
2140STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2141{
2142 CheckComArgOutPointerValid(monitorCount);
2143
2144 AutoCaller autoCaller(this);
2145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2146
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 *monitorCount = mHWData->mMonitorCount;
2150
2151 return S_OK;
2152}
2153
2154STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2155{
2156 /* make sure monitor count is a sensible number */
2157 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2158 return setError(E_INVALIDARG,
2159 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2160 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2161
2162 AutoCaller autoCaller(this);
2163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2164
2165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2166
2167 HRESULT rc = checkStateDependency(MutableStateDep);
2168 if (FAILED(rc)) return rc;
2169
2170 setModified(IsModified_MachineData);
2171 mHWData.backup();
2172 mHWData->mMonitorCount = monitorCount;
2173
2174 return S_OK;
2175}
2176
2177STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2178{
2179 CheckComArgOutPointerValid(biosSettings);
2180
2181 AutoCaller autoCaller(this);
2182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2183
2184 /* mBIOSSettings is constant during life time, no need to lock */
2185 mBIOSSettings.queryInterfaceTo(biosSettings);
2186
2187 return S_OK;
2188}
2189
2190STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2191{
2192 CheckComArgOutPointerValid(aVal);
2193
2194 AutoCaller autoCaller(this);
2195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2196
2197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2198
2199 switch (property)
2200 {
2201 case CPUPropertyType_PAE:
2202 *aVal = mHWData->mPAEEnabled;
2203 break;
2204
2205 case CPUPropertyType_Synthetic:
2206 *aVal = mHWData->mSyntheticCpu;
2207 break;
2208
2209 case CPUPropertyType_LongMode:
2210 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2211 *aVal = TRUE;
2212 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2213 *aVal = FALSE;
2214#if HC_ARCH_BITS == 64
2215 else
2216 *aVal = TRUE;
2217#else
2218 else
2219 {
2220 *aVal = FALSE;
2221
2222 ComPtr<IGuestOSType> ptrGuestOSType;
2223 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2224 if (SUCCEEDED(hrc2))
2225 {
2226 BOOL fIs64Bit = FALSE;
2227 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2228 if (SUCCEEDED(hrc2) && fIs64Bit)
2229 {
2230 ComObjPtr<Host> ptrHost = mParent->host();
2231 alock.release();
2232
2233 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2234 if (FAILED(hrc2))
2235 *aVal = FALSE;
2236 }
2237 }
2238 }
2239#endif
2240 break;
2241
2242 case CPUPropertyType_TripleFaultReset:
2243 *aVal = mHWData->mTripleFaultReset;
2244 break;
2245
2246 default:
2247 return E_INVALIDARG;
2248 }
2249 return S_OK;
2250}
2251
2252STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2253{
2254 AutoCaller autoCaller(this);
2255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2256
2257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2258
2259 HRESULT rc = checkStateDependency(MutableStateDep);
2260 if (FAILED(rc)) return rc;
2261
2262 switch (property)
2263 {
2264 case CPUPropertyType_PAE:
2265 setModified(IsModified_MachineData);
2266 mHWData.backup();
2267 mHWData->mPAEEnabled = !!aVal;
2268 break;
2269
2270 case CPUPropertyType_Synthetic:
2271 setModified(IsModified_MachineData);
2272 mHWData.backup();
2273 mHWData->mSyntheticCpu = !!aVal;
2274 break;
2275
2276 case CPUPropertyType_LongMode:
2277 setModified(IsModified_MachineData);
2278 mHWData.backup();
2279 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2280 break;
2281
2282 case CPUPropertyType_TripleFaultReset:
2283 setModified(IsModified_MachineData);
2284 mHWData.backup();
2285 mHWData->mTripleFaultReset = !!aVal;
2286 break;
2287
2288 default:
2289 return E_INVALIDARG;
2290 }
2291 return S_OK;
2292}
2293
2294STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2295{
2296 CheckComArgOutPointerValid(aValEax);
2297 CheckComArgOutPointerValid(aValEbx);
2298 CheckComArgOutPointerValid(aValEcx);
2299 CheckComArgOutPointerValid(aValEdx);
2300
2301 AutoCaller autoCaller(this);
2302 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2303
2304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2305
2306 switch(aId)
2307 {
2308 case 0x0:
2309 case 0x1:
2310 case 0x2:
2311 case 0x3:
2312 case 0x4:
2313 case 0x5:
2314 case 0x6:
2315 case 0x7:
2316 case 0x8:
2317 case 0x9:
2318 case 0xA:
2319 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2320 return E_INVALIDARG;
2321
2322 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2323 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2324 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2325 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2326 break;
2327
2328 case 0x80000000:
2329 case 0x80000001:
2330 case 0x80000002:
2331 case 0x80000003:
2332 case 0x80000004:
2333 case 0x80000005:
2334 case 0x80000006:
2335 case 0x80000007:
2336 case 0x80000008:
2337 case 0x80000009:
2338 case 0x8000000A:
2339 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2340 return E_INVALIDARG;
2341
2342 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2343 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2344 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2345 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2346 break;
2347
2348 default:
2349 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2350 }
2351 return S_OK;
2352}
2353
2354STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2355{
2356 AutoCaller autoCaller(this);
2357 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2358
2359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2360
2361 HRESULT rc = checkStateDependency(MutableStateDep);
2362 if (FAILED(rc)) return rc;
2363
2364 switch(aId)
2365 {
2366 case 0x0:
2367 case 0x1:
2368 case 0x2:
2369 case 0x3:
2370 case 0x4:
2371 case 0x5:
2372 case 0x6:
2373 case 0x7:
2374 case 0x8:
2375 case 0x9:
2376 case 0xA:
2377 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2378 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2379 setModified(IsModified_MachineData);
2380 mHWData.backup();
2381 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2382 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2383 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2384 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2385 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2386 break;
2387
2388 case 0x80000000:
2389 case 0x80000001:
2390 case 0x80000002:
2391 case 0x80000003:
2392 case 0x80000004:
2393 case 0x80000005:
2394 case 0x80000006:
2395 case 0x80000007:
2396 case 0x80000008:
2397 case 0x80000009:
2398 case 0x8000000A:
2399 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2400 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2401 setModified(IsModified_MachineData);
2402 mHWData.backup();
2403 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2404 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2405 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2406 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2407 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2408 break;
2409
2410 default:
2411 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2412 }
2413 return S_OK;
2414}
2415
2416STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2417{
2418 AutoCaller autoCaller(this);
2419 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2420
2421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2422
2423 HRESULT rc = checkStateDependency(MutableStateDep);
2424 if (FAILED(rc)) return rc;
2425
2426 switch(aId)
2427 {
2428 case 0x0:
2429 case 0x1:
2430 case 0x2:
2431 case 0x3:
2432 case 0x4:
2433 case 0x5:
2434 case 0x6:
2435 case 0x7:
2436 case 0x8:
2437 case 0x9:
2438 case 0xA:
2439 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2440 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2441 setModified(IsModified_MachineData);
2442 mHWData.backup();
2443 /* Invalidate leaf. */
2444 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2445 break;
2446
2447 case 0x80000000:
2448 case 0x80000001:
2449 case 0x80000002:
2450 case 0x80000003:
2451 case 0x80000004:
2452 case 0x80000005:
2453 case 0x80000006:
2454 case 0x80000007:
2455 case 0x80000008:
2456 case 0x80000009:
2457 case 0x8000000A:
2458 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2459 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2460 setModified(IsModified_MachineData);
2461 mHWData.backup();
2462 /* Invalidate leaf. */
2463 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2464 break;
2465
2466 default:
2467 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2468 }
2469 return S_OK;
2470}
2471
2472STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2473{
2474 AutoCaller autoCaller(this);
2475 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2476
2477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2478
2479 HRESULT rc = checkStateDependency(MutableStateDep);
2480 if (FAILED(rc)) return rc;
2481
2482 setModified(IsModified_MachineData);
2483 mHWData.backup();
2484
2485 /* Invalidate all standard leafs. */
2486 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2487 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2488
2489 /* Invalidate all extended leafs. */
2490 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2491 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2492
2493 return S_OK;
2494}
2495
2496STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2497{
2498 CheckComArgOutPointerValid(aVal);
2499
2500 AutoCaller autoCaller(this);
2501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2502
2503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 switch(property)
2506 {
2507 case HWVirtExPropertyType_Enabled:
2508 *aVal = mHWData->mHWVirtExEnabled;
2509 break;
2510
2511 case HWVirtExPropertyType_VPID:
2512 *aVal = mHWData->mHWVirtExVPIDEnabled;
2513 break;
2514
2515 case HWVirtExPropertyType_NestedPaging:
2516 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2517 break;
2518
2519 case HWVirtExPropertyType_UnrestrictedExecution:
2520 *aVal = mHWData->mHWVirtExUXEnabled;
2521 break;
2522
2523 case HWVirtExPropertyType_LargePages:
2524 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2525#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2526 *aVal = FALSE;
2527#endif
2528 break;
2529
2530 case HWVirtExPropertyType_Force:
2531 *aVal = mHWData->mHWVirtExForceEnabled;
2532 break;
2533
2534 default:
2535 return E_INVALIDARG;
2536 }
2537 return S_OK;
2538}
2539
2540STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2541{
2542 AutoCaller autoCaller(this);
2543 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2544
2545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2546
2547 HRESULT rc = checkStateDependency(MutableStateDep);
2548 if (FAILED(rc)) return rc;
2549
2550 switch(property)
2551 {
2552 case HWVirtExPropertyType_Enabled:
2553 setModified(IsModified_MachineData);
2554 mHWData.backup();
2555 mHWData->mHWVirtExEnabled = !!aVal;
2556 break;
2557
2558 case HWVirtExPropertyType_VPID:
2559 setModified(IsModified_MachineData);
2560 mHWData.backup();
2561 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2562 break;
2563
2564 case HWVirtExPropertyType_NestedPaging:
2565 setModified(IsModified_MachineData);
2566 mHWData.backup();
2567 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2568 break;
2569
2570 case HWVirtExPropertyType_UnrestrictedExecution:
2571 setModified(IsModified_MachineData);
2572 mHWData.backup();
2573 mHWData->mHWVirtExUXEnabled = !!aVal;
2574 break;
2575
2576 case HWVirtExPropertyType_LargePages:
2577 setModified(IsModified_MachineData);
2578 mHWData.backup();
2579 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2580 break;
2581
2582 case HWVirtExPropertyType_Force:
2583 setModified(IsModified_MachineData);
2584 mHWData.backup();
2585 mHWData->mHWVirtExForceEnabled = !!aVal;
2586 break;
2587
2588 default:
2589 return E_INVALIDARG;
2590 }
2591
2592 return S_OK;
2593}
2594
2595STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2596{
2597 CheckComArgOutPointerValid(aSnapshotFolder);
2598
2599 AutoCaller autoCaller(this);
2600 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2601
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 Utf8Str strFullSnapshotFolder;
2605 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2606 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2607
2608 return S_OK;
2609}
2610
2611STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2612{
2613 /* @todo (r=dmik):
2614 * 1. Allow to change the name of the snapshot folder containing snapshots
2615 * 2. Rename the folder on disk instead of just changing the property
2616 * value (to be smart and not to leave garbage). Note that it cannot be
2617 * done here because the change may be rolled back. Thus, the right
2618 * place is #saveSettings().
2619 */
2620
2621 AutoCaller autoCaller(this);
2622 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2623
2624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2625
2626 HRESULT rc = checkStateDependency(MutableStateDep);
2627 if (FAILED(rc)) return rc;
2628
2629 if (!mData->mCurrentSnapshot.isNull())
2630 return setError(E_FAIL,
2631 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2632
2633 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2634
2635 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2636 if (strSnapshotFolder.isEmpty())
2637 strSnapshotFolder = "Snapshots";
2638 int vrc = calculateFullPath(strSnapshotFolder,
2639 strSnapshotFolder);
2640 if (RT_FAILURE(vrc))
2641 return setError(E_FAIL,
2642 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2643 aSnapshotFolder, vrc);
2644
2645 setModified(IsModified_MachineData);
2646 mUserData.backup();
2647
2648 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2649
2650 return S_OK;
2651}
2652
2653STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2654{
2655 CheckComArgOutSafeArrayPointerValid(aAttachments);
2656
2657 AutoCaller autoCaller(this);
2658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2659
2660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2663 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2664
2665 return S_OK;
2666}
2667
2668STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2669{
2670 CheckComArgOutPointerValid(vrdeServer);
2671
2672 AutoCaller autoCaller(this);
2673 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2674
2675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
2677 Assert(!!mVRDEServer);
2678 mVRDEServer.queryInterfaceTo(vrdeServer);
2679
2680 return S_OK;
2681}
2682
2683STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2684{
2685 CheckComArgOutPointerValid(audioAdapter);
2686
2687 AutoCaller autoCaller(this);
2688 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2689
2690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 mAudioAdapter.queryInterfaceTo(audioAdapter);
2693 return S_OK;
2694}
2695
2696STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2697{
2698#ifdef VBOX_WITH_VUSB
2699 CheckComArgOutPointerValid(aUSBControllers);
2700
2701 AutoCaller autoCaller(this);
2702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2703
2704 clearError();
2705 MultiResult rc(S_OK);
2706
2707# ifdef VBOX_WITH_USB
2708 rc = mParent->host()->checkUSBProxyService();
2709 if (FAILED(rc)) return rc;
2710# endif
2711
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2715 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2716 return S_OK;
2717#else
2718 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2719 * extended error info to indicate that USB is simply not available
2720 * (w/o treating it as a failure), for example, as in OSE */
2721 NOREF(aUSBControllers);
2722 ReturnComNotImplemented();
2723#endif /* VBOX_WITH_VUSB */
2724}
2725
2726STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2727{
2728#ifdef VBOX_WITH_VUSB
2729 CheckComArgOutPointerValid(aUSBDeviceFilters);
2730
2731 AutoCaller autoCaller(this);
2732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2733
2734 clearError();
2735 MultiResult rc(S_OK);
2736
2737# ifdef VBOX_WITH_USB
2738 rc = mParent->host()->checkUSBProxyService();
2739 if (FAILED(rc)) return rc;
2740# endif
2741
2742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2743
2744 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2745#else
2746 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2747 * extended error info to indicate that USB is simply not available
2748 * (w/o treating it as a failure), for example, as in OSE */
2749 NOREF(aUSBDeviceFilters);
2750 ReturnComNotImplemented();
2751#endif /* VBOX_WITH_VUSB */
2752}
2753
2754STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2755{
2756 CheckComArgOutPointerValid(aFilePath);
2757
2758 AutoLimitedCaller autoCaller(this);
2759 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2760
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 mData->m_strConfigFileFull.cloneTo(aFilePath);
2764 return S_OK;
2765}
2766
2767STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2768{
2769 CheckComArgOutPointerValid(aModified);
2770
2771 AutoCaller autoCaller(this);
2772 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2773
2774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2775
2776 HRESULT rc = checkStateDependency(MutableStateDep);
2777 if (FAILED(rc)) return rc;
2778
2779 if (!mData->pMachineConfigFile->fileExists())
2780 // this is a new machine, and no config file exists yet:
2781 *aModified = TRUE;
2782 else
2783 *aModified = (mData->flModifications != 0);
2784
2785 return S_OK;
2786}
2787
2788STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2789{
2790 CheckComArgOutPointerValid(aSessionState);
2791
2792 AutoCaller autoCaller(this);
2793 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2794
2795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2796
2797 *aSessionState = mData->mSession.mState;
2798
2799 return S_OK;
2800}
2801
2802STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2803{
2804 CheckComArgOutPointerValid(aSessionType);
2805
2806 AutoCaller autoCaller(this);
2807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2808
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 mData->mSession.mType.cloneTo(aSessionType);
2812
2813 return S_OK;
2814}
2815
2816STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2817{
2818 CheckComArgOutPointerValid(aSessionPID);
2819
2820 AutoCaller autoCaller(this);
2821 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2822
2823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2824
2825 *aSessionPID = mData->mSession.mPID;
2826
2827 return S_OK;
2828}
2829
2830STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2831{
2832 CheckComArgOutPointerValid(machineState);
2833
2834 AutoCaller autoCaller(this);
2835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2836
2837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2838
2839 *machineState = mData->mMachineState;
2840
2841 return S_OK;
2842}
2843
2844STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2845{
2846 CheckComArgOutPointerValid(aLastStateChange);
2847
2848 AutoCaller autoCaller(this);
2849 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2850
2851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2854
2855 return S_OK;
2856}
2857
2858STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2859{
2860 CheckComArgOutPointerValid(aStateFilePath);
2861
2862 AutoCaller autoCaller(this);
2863 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2864
2865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2866
2867 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2868
2869 return S_OK;
2870}
2871
2872STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2873{
2874 CheckComArgOutPointerValid(aLogFolder);
2875
2876 AutoCaller autoCaller(this);
2877 AssertComRCReturnRC(autoCaller.rc());
2878
2879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 Utf8Str logFolder;
2882 getLogFolder(logFolder);
2883 logFolder.cloneTo(aLogFolder);
2884
2885 return S_OK;
2886}
2887
2888STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2889{
2890 CheckComArgOutPointerValid(aCurrentSnapshot);
2891
2892 AutoCaller autoCaller(this);
2893 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2894
2895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2896
2897 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2898
2899 return S_OK;
2900}
2901
2902STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2903{
2904 CheckComArgOutPointerValid(aSnapshotCount);
2905
2906 AutoCaller autoCaller(this);
2907 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2908
2909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2910
2911 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2912 ? 0
2913 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2914
2915 return S_OK;
2916}
2917
2918STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2919{
2920 CheckComArgOutPointerValid(aCurrentStateModified);
2921
2922 AutoCaller autoCaller(this);
2923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2924
2925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 /* Note: for machines with no snapshots, we always return FALSE
2928 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2929 * reasons :) */
2930
2931 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2932 ? FALSE
2933 : mData->mCurrentStateModified;
2934
2935 return S_OK;
2936}
2937
2938STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2939{
2940 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2941
2942 AutoCaller autoCaller(this);
2943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2944
2945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2946
2947 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2948 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2949
2950 return S_OK;
2951}
2952
2953STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2954{
2955 CheckComArgOutPointerValid(aClipboardMode);
2956
2957 AutoCaller autoCaller(this);
2958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2959
2960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2961
2962 *aClipboardMode = mHWData->mClipboardMode;
2963
2964 return S_OK;
2965}
2966
2967STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2968{
2969 HRESULT rc = S_OK;
2970
2971 AutoCaller autoCaller(this);
2972 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2973
2974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2975
2976 alock.release();
2977 rc = onClipboardModeChange(aClipboardMode);
2978 alock.acquire();
2979 if (FAILED(rc)) return rc;
2980
2981 setModified(IsModified_MachineData);
2982 mHWData.backup();
2983 mHWData->mClipboardMode = aClipboardMode;
2984
2985 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2986 if (Global::IsOnline(mData->mMachineState))
2987 saveSettings(NULL);
2988
2989 return S_OK;
2990}
2991
2992STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2993{
2994 CheckComArgOutPointerValid(aDragAndDropMode);
2995
2996 AutoCaller autoCaller(this);
2997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2998
2999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3000
3001 *aDragAndDropMode = mHWData->mDragAndDropMode;
3002
3003 return S_OK;
3004}
3005
3006STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3007{
3008 HRESULT rc = S_OK;
3009
3010 AutoCaller autoCaller(this);
3011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3012
3013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3014
3015 alock.release();
3016 rc = onDragAndDropModeChange(aDragAndDropMode);
3017 alock.acquire();
3018 if (FAILED(rc)) return rc;
3019
3020 setModified(IsModified_MachineData);
3021 mHWData.backup();
3022 mHWData->mDragAndDropMode = aDragAndDropMode;
3023
3024 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3025 if (Global::IsOnline(mData->mMachineState))
3026 saveSettings(NULL);
3027
3028 return S_OK;
3029}
3030
3031STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3032{
3033 CheckComArgOutPointerValid(aPatterns);
3034
3035 AutoCaller autoCaller(this);
3036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3037
3038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3039
3040 try
3041 {
3042 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3043 }
3044 catch (...)
3045 {
3046 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3047 }
3048
3049 return S_OK;
3050}
3051
3052STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3053{
3054 AutoCaller autoCaller(this);
3055 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3056
3057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 HRESULT rc = checkStateDependency(MutableStateDep);
3060 if (FAILED(rc)) return rc;
3061
3062 setModified(IsModified_MachineData);
3063 mHWData.backup();
3064 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3065 return rc;
3066}
3067
3068STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3069{
3070 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3071
3072 AutoCaller autoCaller(this);
3073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3074
3075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3076
3077 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3078 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3079
3080 return S_OK;
3081}
3082
3083STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3084{
3085 CheckComArgOutPointerValid(aEnabled);
3086
3087 AutoCaller autoCaller(this);
3088 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3089
3090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3091
3092 *aEnabled = mUserData->s.fTeleporterEnabled;
3093
3094 return S_OK;
3095}
3096
3097STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3098{
3099 AutoCaller autoCaller(this);
3100 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3101
3102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3103
3104 /* Only allow it to be set to true when PoweredOff or Aborted.
3105 (Clearing it is always permitted.) */
3106 if ( aEnabled
3107 && mData->mRegistered
3108 && ( !isSessionMachine()
3109 || ( mData->mMachineState != MachineState_PoweredOff
3110 && mData->mMachineState != MachineState_Teleported
3111 && mData->mMachineState != MachineState_Aborted
3112 )
3113 )
3114 )
3115 return setError(VBOX_E_INVALID_VM_STATE,
3116 tr("The machine is not powered off (state is %s)"),
3117 Global::stringifyMachineState(mData->mMachineState));
3118
3119 setModified(IsModified_MachineData);
3120 mUserData.backup();
3121 mUserData->s.fTeleporterEnabled = !!aEnabled;
3122
3123 return S_OK;
3124}
3125
3126STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3127{
3128 CheckComArgOutPointerValid(aPort);
3129
3130 AutoCaller autoCaller(this);
3131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3132
3133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3134
3135 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3136
3137 return S_OK;
3138}
3139
3140STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3141{
3142 if (aPort >= _64K)
3143 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3144
3145 AutoCaller autoCaller(this);
3146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3147
3148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3149
3150 HRESULT rc = checkStateDependency(MutableStateDep);
3151 if (FAILED(rc)) return rc;
3152
3153 setModified(IsModified_MachineData);
3154 mUserData.backup();
3155 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3156
3157 return S_OK;
3158}
3159
3160STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3161{
3162 CheckComArgOutPointerValid(aAddress);
3163
3164 AutoCaller autoCaller(this);
3165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3166
3167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3168
3169 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3170
3171 return S_OK;
3172}
3173
3174STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3175{
3176 AutoCaller autoCaller(this);
3177 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3178
3179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3180
3181 HRESULT rc = checkStateDependency(MutableStateDep);
3182 if (FAILED(rc)) return rc;
3183
3184 setModified(IsModified_MachineData);
3185 mUserData.backup();
3186 mUserData->s.strTeleporterAddress = aAddress;
3187
3188 return S_OK;
3189}
3190
3191STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3192{
3193 CheckComArgOutPointerValid(aPassword);
3194
3195 AutoCaller autoCaller(this);
3196 HRESULT hrc = autoCaller.rc();
3197 if (SUCCEEDED(hrc))
3198 {
3199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3200 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3201 }
3202
3203 return hrc;
3204}
3205
3206STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3207{
3208 /*
3209 * Hash the password first.
3210 */
3211 Utf8Str strPassword(aPassword);
3212 if (!strPassword.isEmpty())
3213 {
3214 if (VBoxIsPasswordHashed(&strPassword))
3215 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3216 VBoxHashPassword(&strPassword);
3217 }
3218
3219 /*
3220 * Do the update.
3221 */
3222 AutoCaller autoCaller(this);
3223 HRESULT hrc = autoCaller.rc();
3224 if (SUCCEEDED(hrc))
3225 {
3226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3227 hrc = checkStateDependency(MutableStateDep);
3228 if (SUCCEEDED(hrc))
3229 {
3230 setModified(IsModified_MachineData);
3231 mUserData.backup();
3232 mUserData->s.strTeleporterPassword = strPassword;
3233 }
3234 }
3235
3236 return hrc;
3237}
3238
3239STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3240{
3241 CheckComArgOutPointerValid(aState);
3242
3243 AutoCaller autoCaller(this);
3244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3245
3246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3247
3248 *aState = mUserData->s.enmFaultToleranceState;
3249 return S_OK;
3250}
3251
3252STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3253{
3254 AutoCaller autoCaller(this);
3255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3256
3257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3258
3259 /* @todo deal with running state change. */
3260 HRESULT rc = checkStateDependency(MutableStateDep);
3261 if (FAILED(rc)) return rc;
3262
3263 setModified(IsModified_MachineData);
3264 mUserData.backup();
3265 mUserData->s.enmFaultToleranceState = aState;
3266 return S_OK;
3267}
3268
3269STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3270{
3271 CheckComArgOutPointerValid(aAddress);
3272
3273 AutoCaller autoCaller(this);
3274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3275
3276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3277
3278 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3279 return S_OK;
3280}
3281
3282STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3283{
3284 AutoCaller autoCaller(this);
3285 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3286
3287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3288
3289 /* @todo deal with running state change. */
3290 HRESULT rc = checkStateDependency(MutableStateDep);
3291 if (FAILED(rc)) return rc;
3292
3293 setModified(IsModified_MachineData);
3294 mUserData.backup();
3295 mUserData->s.strFaultToleranceAddress = aAddress;
3296 return S_OK;
3297}
3298
3299STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3300{
3301 CheckComArgOutPointerValid(aPort);
3302
3303 AutoCaller autoCaller(this);
3304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3305
3306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3307
3308 *aPort = mUserData->s.uFaultTolerancePort;
3309 return S_OK;
3310}
3311
3312STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3313{
3314 AutoCaller autoCaller(this);
3315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3316
3317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3318
3319 /* @todo deal with running state change. */
3320 HRESULT rc = checkStateDependency(MutableStateDep);
3321 if (FAILED(rc)) return rc;
3322
3323 setModified(IsModified_MachineData);
3324 mUserData.backup();
3325 mUserData->s.uFaultTolerancePort = aPort;
3326 return S_OK;
3327}
3328
3329STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3330{
3331 CheckComArgOutPointerValid(aPassword);
3332
3333 AutoCaller autoCaller(this);
3334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3335
3336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3337
3338 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3339
3340 return S_OK;
3341}
3342
3343STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3344{
3345 AutoCaller autoCaller(this);
3346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3347
3348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3349
3350 /* @todo deal with running state change. */
3351 HRESULT rc = checkStateDependency(MutableStateDep);
3352 if (FAILED(rc)) return rc;
3353
3354 setModified(IsModified_MachineData);
3355 mUserData.backup();
3356 mUserData->s.strFaultTolerancePassword = aPassword;
3357
3358 return S_OK;
3359}
3360
3361STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3362{
3363 CheckComArgOutPointerValid(aInterval);
3364
3365 AutoCaller autoCaller(this);
3366 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3367
3368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3369
3370 *aInterval = mUserData->s.uFaultToleranceInterval;
3371 return S_OK;
3372}
3373
3374STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3375{
3376 AutoCaller autoCaller(this);
3377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3378
3379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3380
3381 /* @todo deal with running state change. */
3382 HRESULT rc = checkStateDependency(MutableStateDep);
3383 if (FAILED(rc)) return rc;
3384
3385 setModified(IsModified_MachineData);
3386 mUserData.backup();
3387 mUserData->s.uFaultToleranceInterval = aInterval;
3388 return S_OK;
3389}
3390
3391STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3392{
3393 CheckComArgOutPointerValid(aEnabled);
3394
3395 AutoCaller autoCaller(this);
3396 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3397
3398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3399
3400 *aEnabled = mUserData->s.fRTCUseUTC;
3401
3402 return S_OK;
3403}
3404
3405STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3406{
3407 AutoCaller autoCaller(this);
3408 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3409
3410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3411
3412 /* Only allow it to be set to true when PoweredOff or Aborted.
3413 (Clearing it is always permitted.) */
3414 if ( aEnabled
3415 && mData->mRegistered
3416 && ( !isSessionMachine()
3417 || ( mData->mMachineState != MachineState_PoweredOff
3418 && mData->mMachineState != MachineState_Teleported
3419 && mData->mMachineState != MachineState_Aborted
3420 )
3421 )
3422 )
3423 return setError(VBOX_E_INVALID_VM_STATE,
3424 tr("The machine is not powered off (state is %s)"),
3425 Global::stringifyMachineState(mData->mMachineState));
3426
3427 setModified(IsModified_MachineData);
3428 mUserData.backup();
3429 mUserData->s.fRTCUseUTC = !!aEnabled;
3430
3431 return S_OK;
3432}
3433
3434STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3435{
3436 CheckComArgOutPointerValid(aEnabled);
3437
3438 AutoCaller autoCaller(this);
3439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3440
3441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3442
3443 *aEnabled = mHWData->mIOCacheEnabled;
3444
3445 return S_OK;
3446}
3447
3448STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3449{
3450 AutoCaller autoCaller(this);
3451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3452
3453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3454
3455 HRESULT rc = checkStateDependency(MutableStateDep);
3456 if (FAILED(rc)) return rc;
3457
3458 setModified(IsModified_MachineData);
3459 mHWData.backup();
3460 mHWData->mIOCacheEnabled = aEnabled;
3461
3462 return S_OK;
3463}
3464
3465STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3466{
3467 CheckComArgOutPointerValid(aIOCacheSize);
3468
3469 AutoCaller autoCaller(this);
3470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3471
3472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3473
3474 *aIOCacheSize = mHWData->mIOCacheSize;
3475
3476 return S_OK;
3477}
3478
3479STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3480{
3481 AutoCaller autoCaller(this);
3482 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3483
3484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3485
3486 HRESULT rc = checkStateDependency(MutableStateDep);
3487 if (FAILED(rc)) return rc;
3488
3489 setModified(IsModified_MachineData);
3490 mHWData.backup();
3491 mHWData->mIOCacheSize = aIOCacheSize;
3492
3493 return S_OK;
3494}
3495
3496
3497/**
3498 * @note Locks objects!
3499 */
3500STDMETHODIMP Machine::LockMachine(ISession *aSession,
3501 LockType_T lockType)
3502{
3503 CheckComArgNotNull(aSession);
3504
3505 AutoCaller autoCaller(this);
3506 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3507
3508 /* check the session state */
3509 SessionState_T state;
3510 HRESULT rc = aSession->COMGETTER(State)(&state);
3511 if (FAILED(rc)) return rc;
3512
3513 if (state != SessionState_Unlocked)
3514 return setError(VBOX_E_INVALID_OBJECT_STATE,
3515 tr("The given session is busy"));
3516
3517 // get the client's IInternalSessionControl interface
3518 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3519 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3520 E_INVALIDARG);
3521
3522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3523
3524 if (!mData->mRegistered)
3525 return setError(E_UNEXPECTED,
3526 tr("The machine '%s' is not registered"),
3527 mUserData->s.strName.c_str());
3528
3529 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3530
3531 SessionState_T oldState = mData->mSession.mState;
3532 /* Hack: in case the session is closing and there is a progress object
3533 * which allows waiting for the session to be closed, take the opportunity
3534 * and do a limited wait (max. 1 second). This helps a lot when the system
3535 * is busy and thus session closing can take a little while. */
3536 if ( mData->mSession.mState == SessionState_Unlocking
3537 && mData->mSession.mProgress)
3538 {
3539 alock.release();
3540 mData->mSession.mProgress->WaitForCompletion(1000);
3541 alock.acquire();
3542 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3543 }
3544
3545 // try again now
3546 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3547 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3548 )
3549 {
3550 // OK, share the session... we are now dealing with three processes:
3551 // 1) VBoxSVC (where this code runs);
3552 // 2) process C: the caller's client process (who wants a shared session);
3553 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3554
3555 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3556 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3557 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3558 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3559 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3560
3561 /*
3562 * Release the lock before calling the client process. It's safe here
3563 * since the only thing to do after we get the lock again is to add
3564 * the remote control to the list (which doesn't directly influence
3565 * anything).
3566 */
3567 alock.release();
3568
3569 // get the console of the session holding the write lock (this is a remote call)
3570 ComPtr<IConsole> pConsoleW;
3571 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3572 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3573 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3574 if (FAILED(rc))
3575 // the failure may occur w/o any error info (from RPC), so provide one
3576 return setError(VBOX_E_VM_ERROR,
3577 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3578
3579 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3580
3581 // share the session machine and W's console with the caller's session
3582 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3583 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3584 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3585
3586 if (FAILED(rc))
3587 // the failure may occur w/o any error info (from RPC), so provide one
3588 return setError(VBOX_E_VM_ERROR,
3589 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3590 alock.acquire();
3591
3592 // need to revalidate the state after acquiring the lock again
3593 if (mData->mSession.mState != SessionState_Locked)
3594 {
3595 pSessionControl->Uninitialize();
3596 return setError(VBOX_E_INVALID_SESSION_STATE,
3597 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3598 mUserData->s.strName.c_str());
3599 }
3600
3601 // add the caller's session to the list
3602 mData->mSession.mRemoteControls.push_back(pSessionControl);
3603 }
3604 else if ( mData->mSession.mState == SessionState_Locked
3605 || mData->mSession.mState == SessionState_Unlocking
3606 )
3607 {
3608 // sharing not permitted, or machine still unlocking:
3609 return setError(VBOX_E_INVALID_OBJECT_STATE,
3610 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3611 mUserData->s.strName.c_str());
3612 }
3613 else
3614 {
3615 // machine is not locked: then write-lock the machine (create the session machine)
3616
3617 // must not be busy
3618 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3619
3620 // get the caller's session PID
3621 RTPROCESS pid = NIL_RTPROCESS;
3622 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3623 pSessionControl->GetPID((ULONG*)&pid);
3624 Assert(pid != NIL_RTPROCESS);
3625
3626 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3627
3628 if (fLaunchingVMProcess)
3629 {
3630 if (mData->mSession.mPID == NIL_RTPROCESS)
3631 {
3632 // two or more clients racing for a lock, the one which set the
3633 // session state to Spawning will win, the others will get an
3634 // error as we can't decide here if waiting a little would help
3635 // (only for shared locks this would avoid an error)
3636 return setError(VBOX_E_INVALID_OBJECT_STATE,
3637 tr("The machine '%s' already has a lock request pending"),
3638 mUserData->s.strName.c_str());
3639 }
3640
3641 // this machine is awaiting for a spawning session to be opened:
3642 // then the calling process must be the one that got started by
3643 // LaunchVMProcess()
3644
3645 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3646 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3647
3648 if (mData->mSession.mPID != pid)
3649 return setError(E_ACCESSDENIED,
3650 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3651 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3652 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3653 }
3654
3655 // create the mutable SessionMachine from the current machine
3656 ComObjPtr<SessionMachine> sessionMachine;
3657 sessionMachine.createObject();
3658 rc = sessionMachine->init(this);
3659 AssertComRC(rc);
3660
3661 /* NOTE: doing return from this function after this point but
3662 * before the end is forbidden since it may call SessionMachine::uninit()
3663 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3664 * lock while still holding the Machine lock in alock so that a deadlock
3665 * is possible due to the wrong lock order. */
3666
3667 if (SUCCEEDED(rc))
3668 {
3669 /*
3670 * Set the session state to Spawning to protect against subsequent
3671 * attempts to open a session and to unregister the machine after
3672 * we release the lock.
3673 */
3674 SessionState_T origState = mData->mSession.mState;
3675 mData->mSession.mState = SessionState_Spawning;
3676
3677#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3678 /* Get the client token ID to be passed to the client process */
3679 Utf8Str strTokenId;
3680 sessionMachine->getTokenId(strTokenId);
3681 Assert(!strTokenId.isEmpty());
3682#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3683 /* Get the client token to be passed to the client process */
3684 ComPtr<IToken> pToken(sessionMachine->getToken());
3685 /* The token is now "owned" by pToken, fix refcount */
3686 if (!pToken.isNull())
3687 pToken->Release();
3688#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3689
3690 /*
3691 * Release the lock before calling the client process -- it will call
3692 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3693 * because the state is Spawning, so that LaunchVMProcess() and
3694 * LockMachine() calls will fail. This method, called before we
3695 * acquire the lock again, will fail because of the wrong PID.
3696 *
3697 * Note that mData->mSession.mRemoteControls accessed outside
3698 * the lock may not be modified when state is Spawning, so it's safe.
3699 */
3700 alock.release();
3701
3702 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3703#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3704 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3705#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3706 rc = pSessionControl->AssignMachine(sessionMachine, lockType, pToken);
3707 /* Now the token is owned by the client process. */
3708 pToken.setNull();
3709#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3710 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3711
3712 /* The failure may occur w/o any error info (from RPC), so provide one */
3713 if (FAILED(rc))
3714 setError(VBOX_E_VM_ERROR,
3715 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3716
3717 if ( SUCCEEDED(rc)
3718 && fLaunchingVMProcess
3719 )
3720 {
3721 /* complete the remote session initialization */
3722
3723 /* get the console from the direct session */
3724 ComPtr<IConsole> console;
3725 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3726 ComAssertComRC(rc);
3727
3728 if (SUCCEEDED(rc) && !console)
3729 {
3730 ComAssert(!!console);
3731 rc = E_FAIL;
3732 }
3733
3734 /* assign machine & console to the remote session */
3735 if (SUCCEEDED(rc))
3736 {
3737 /*
3738 * after LaunchVMProcess(), the first and the only
3739 * entry in remoteControls is that remote session
3740 */
3741 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3742 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3743 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3744
3745 /* The failure may occur w/o any error info (from RPC), so provide one */
3746 if (FAILED(rc))
3747 setError(VBOX_E_VM_ERROR,
3748 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3749 }
3750
3751 if (FAILED(rc))
3752 pSessionControl->Uninitialize();
3753 }
3754
3755 /* acquire the lock again */
3756 alock.acquire();
3757
3758 /* Restore the session state */
3759 mData->mSession.mState = origState;
3760 }
3761
3762 // finalize spawning anyway (this is why we don't return on errors above)
3763 if (fLaunchingVMProcess)
3764 {
3765 /* Note that the progress object is finalized later */
3766 /** @todo Consider checking mData->mSession.mProgress for cancellation
3767 * around here. */
3768
3769 /* We don't reset mSession.mPID here because it is necessary for
3770 * SessionMachine::uninit() to reap the child process later. */
3771
3772 if (FAILED(rc))
3773 {
3774 /* Close the remote session, remove the remote control from the list
3775 * and reset session state to Closed (@note keep the code in sync
3776 * with the relevant part in checkForSpawnFailure()). */
3777
3778 Assert(mData->mSession.mRemoteControls.size() == 1);
3779 if (mData->mSession.mRemoteControls.size() == 1)
3780 {
3781 ErrorInfoKeeper eik;
3782 mData->mSession.mRemoteControls.front()->Uninitialize();
3783 }
3784
3785 mData->mSession.mRemoteControls.clear();
3786 mData->mSession.mState = SessionState_Unlocked;
3787 }
3788 }
3789 else
3790 {
3791 /* memorize PID of the directly opened session */
3792 if (SUCCEEDED(rc))
3793 mData->mSession.mPID = pid;
3794 }
3795
3796 if (SUCCEEDED(rc))
3797 {
3798 /* memorize the direct session control and cache IUnknown for it */
3799 mData->mSession.mDirectControl = pSessionControl;
3800 mData->mSession.mState = SessionState_Locked;
3801 /* associate the SessionMachine with this Machine */
3802 mData->mSession.mMachine = sessionMachine;
3803
3804 /* request an IUnknown pointer early from the remote party for later
3805 * identity checks (it will be internally cached within mDirectControl
3806 * at least on XPCOM) */
3807 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3808 NOREF(unk);
3809 }
3810
3811 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3812 * would break the lock order */
3813 alock.release();
3814
3815 /* uninitialize the created session machine on failure */
3816 if (FAILED(rc))
3817 sessionMachine->uninit();
3818
3819 }
3820
3821 if (SUCCEEDED(rc))
3822 {
3823 /*
3824 * tell the client watcher thread to update the set of
3825 * machines that have open sessions
3826 */
3827 mParent->updateClientWatcher();
3828
3829 if (oldState != SessionState_Locked)
3830 /* fire an event */
3831 mParent->onSessionStateChange(getId(), SessionState_Locked);
3832 }
3833
3834 return rc;
3835}
3836
3837/**
3838 * @note Locks objects!
3839 */
3840STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3841 IN_BSTR aFrontend,
3842 IN_BSTR aEnvironment,
3843 IProgress **aProgress)
3844{
3845 CheckComArgStr(aFrontend);
3846 Utf8Str strFrontend(aFrontend);
3847 Utf8Str strEnvironment(aEnvironment);
3848 /* "emergencystop" doesn't need the session, so skip the checks/interface
3849 * retrieval. This code doesn't quite fit in here, but introducing a
3850 * special API method would be even more effort, and would require explicit
3851 * support by every API client. It's better to hide the feature a bit. */
3852 if (strFrontend != "emergencystop")
3853 CheckComArgNotNull(aSession);
3854 CheckComArgOutPointerValid(aProgress);
3855
3856 AutoCaller autoCaller(this);
3857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3858
3859 HRESULT rc = S_OK;
3860 if (strFrontend.isEmpty())
3861 {
3862 Bstr bstrFrontend;
3863 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3864 if (FAILED(rc))
3865 return rc;
3866 strFrontend = bstrFrontend;
3867 if (strFrontend.isEmpty())
3868 {
3869 ComPtr<ISystemProperties> systemProperties;
3870 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3871 if (FAILED(rc))
3872 return rc;
3873 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3874 if (FAILED(rc))
3875 return rc;
3876 strFrontend = bstrFrontend;
3877 }
3878 /* paranoia - emergencystop is not a valid default */
3879 if (strFrontend == "emergencystop")
3880 strFrontend = Utf8Str::Empty;
3881 }
3882 /* default frontend: Qt GUI */
3883 if (strFrontend.isEmpty())
3884 strFrontend = "GUI/Qt";
3885
3886 if (strFrontend != "emergencystop")
3887 {
3888 /* check the session state */
3889 SessionState_T state;
3890 rc = aSession->COMGETTER(State)(&state);
3891 if (FAILED(rc))
3892 return rc;
3893
3894 if (state != SessionState_Unlocked)
3895 return setError(VBOX_E_INVALID_OBJECT_STATE,
3896 tr("The given session is busy"));
3897
3898 /* get the IInternalSessionControl interface */
3899 ComPtr<IInternalSessionControl> control(aSession);
3900 ComAssertMsgRet(!control.isNull(),
3901 ("No IInternalSessionControl interface"),
3902 E_INVALIDARG);
3903
3904 /* get the teleporter enable state for the progress object init. */
3905 BOOL fTeleporterEnabled;
3906 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3907 if (FAILED(rc))
3908 return rc;
3909
3910 /* create a progress object */
3911 ComObjPtr<ProgressProxy> progress;
3912 progress.createObject();
3913 rc = progress->init(mParent,
3914 static_cast<IMachine*>(this),
3915 Bstr(tr("Starting VM")).raw(),
3916 TRUE /* aCancelable */,
3917 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3918 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3919 2 /* uFirstOperationWeight */,
3920 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3921
3922 if (SUCCEEDED(rc))
3923 {
3924 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3925 if (SUCCEEDED(rc))
3926 {
3927 progress.queryInterfaceTo(aProgress);
3928
3929 /* signal the client watcher thread */
3930 mParent->updateClientWatcher();
3931
3932 /* fire an event */
3933 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3934 }
3935 }
3936 }
3937 else
3938 {
3939 /* no progress object - either instant success or failure */
3940 *aProgress = NULL;
3941
3942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3943
3944 if (mData->mSession.mState != SessionState_Locked)
3945 return setError(VBOX_E_INVALID_OBJECT_STATE,
3946 tr("The machine '%s' is not locked by a session"),
3947 mUserData->s.strName.c_str());
3948
3949 /* must have a VM process associated - do not kill normal API clients
3950 * with an open session */
3951 if (!Global::IsOnline(mData->mMachineState))
3952 return setError(VBOX_E_INVALID_OBJECT_STATE,
3953 tr("The machine '%s' does not have a VM process"),
3954 mUserData->s.strName.c_str());
3955
3956 /* forcibly terminate the VM process */
3957 if (mData->mSession.mPID != NIL_RTPROCESS)
3958 RTProcTerminate(mData->mSession.mPID);
3959
3960 /* signal the client watcher thread, as most likely the client has
3961 * been terminated */
3962 mParent->updateClientWatcher();
3963 }
3964
3965 return rc;
3966}
3967
3968STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3969{
3970 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3971 return setError(E_INVALIDARG,
3972 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3973 aPosition, SchemaDefs::MaxBootPosition);
3974
3975 if (aDevice == DeviceType_USB)
3976 return setError(E_NOTIMPL,
3977 tr("Booting from USB device is currently not supported"));
3978
3979 AutoCaller autoCaller(this);
3980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3981
3982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3983
3984 HRESULT rc = checkStateDependency(MutableStateDep);
3985 if (FAILED(rc)) return rc;
3986
3987 setModified(IsModified_MachineData);
3988 mHWData.backup();
3989 mHWData->mBootOrder[aPosition - 1] = aDevice;
3990
3991 return S_OK;
3992}
3993
3994STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3995{
3996 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3997 return setError(E_INVALIDARG,
3998 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3999 aPosition, SchemaDefs::MaxBootPosition);
4000
4001 AutoCaller autoCaller(this);
4002 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4003
4004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4005
4006 *aDevice = mHWData->mBootOrder[aPosition - 1];
4007
4008 return S_OK;
4009}
4010
4011STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4012 LONG aControllerPort,
4013 LONG aDevice,
4014 DeviceType_T aType,
4015 IMedium *aMedium)
4016{
4017 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4018 aControllerName, aControllerPort, aDevice, aType, aMedium));
4019
4020 CheckComArgStrNotEmptyOrNull(aControllerName);
4021
4022 AutoCaller autoCaller(this);
4023 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4024
4025 // request the host lock first, since might be calling Host methods for getting host drives;
4026 // next, protect the media tree all the while we're in here, as well as our member variables
4027 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4028 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4029
4030 HRESULT rc = checkStateDependency(MutableStateDep);
4031 if (FAILED(rc)) return rc;
4032
4033 /// @todo NEWMEDIA implicit machine registration
4034 if (!mData->mRegistered)
4035 return setError(VBOX_E_INVALID_OBJECT_STATE,
4036 tr("Cannot attach storage devices to an unregistered machine"));
4037
4038 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4039
4040 /* Check for an existing controller. */
4041 ComObjPtr<StorageController> ctl;
4042 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4043 if (FAILED(rc)) return rc;
4044
4045 StorageControllerType_T ctrlType;
4046 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4047 if (FAILED(rc))
4048 return setError(E_FAIL,
4049 tr("Could not get type of controller '%ls'"),
4050 aControllerName);
4051
4052 bool fSilent = false;
4053 Utf8Str strReconfig;
4054
4055 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4056 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4057 if ( mData->mMachineState == MachineState_Paused
4058 && strReconfig == "1")
4059 fSilent = true;
4060
4061 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4062 bool fHotplug = false;
4063 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4064 fHotplug = true;
4065
4066 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4067 return setError(VBOX_E_INVALID_VM_STATE,
4068 tr("Controller '%ls' does not support hotplugging"),
4069 aControllerName);
4070
4071 // check that the port and device are not out of range
4072 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4073 if (FAILED(rc)) return rc;
4074
4075 /* check if the device slot is already busy */
4076 MediumAttachment *pAttachTemp;
4077 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4078 aControllerName,
4079 aControllerPort,
4080 aDevice)))
4081 {
4082 Medium *pMedium = pAttachTemp->getMedium();
4083 if (pMedium)
4084 {
4085 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4086 return setError(VBOX_E_OBJECT_IN_USE,
4087 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4088 pMedium->getLocationFull().c_str(),
4089 aControllerPort,
4090 aDevice,
4091 aControllerName);
4092 }
4093 else
4094 return setError(VBOX_E_OBJECT_IN_USE,
4095 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4096 aControllerPort, aDevice, aControllerName);
4097 }
4098
4099 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4100 if (aMedium && medium.isNull())
4101 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4102
4103 AutoCaller mediumCaller(medium);
4104 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4105
4106 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4107
4108 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4109 && !medium.isNull()
4110 )
4111 return setError(VBOX_E_OBJECT_IN_USE,
4112 tr("Medium '%s' is already attached to this virtual machine"),
4113 medium->getLocationFull().c_str());
4114
4115 if (!medium.isNull())
4116 {
4117 MediumType_T mtype = medium->getType();
4118 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4119 // For DVDs it's not written to the config file, so needs no global config
4120 // version bump. For floppies it's a new attribute "type", which is ignored
4121 // by older VirtualBox version, so needs no global config version bump either.
4122 // For hard disks this type is not accepted.
4123 if (mtype == MediumType_MultiAttach)
4124 {
4125 // This type is new with VirtualBox 4.0 and therefore requires settings
4126 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4127 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4128 // two reasons: The medium type is a property of the media registry tree, which
4129 // can reside in the global config file (for pre-4.0 media); we would therefore
4130 // possibly need to bump the global config version. We don't want to do that though
4131 // because that might make downgrading to pre-4.0 impossible.
4132 // As a result, we can only use these two new types if the medium is NOT in the
4133 // global registry:
4134 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4135 if ( medium->isInRegistry(uuidGlobalRegistry)
4136 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4137 )
4138 return setError(VBOX_E_INVALID_OBJECT_STATE,
4139 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4140 "to machines that were created with VirtualBox 4.0 or later"),
4141 medium->getLocationFull().c_str());
4142 }
4143 }
4144
4145 bool fIndirect = false;
4146 if (!medium.isNull())
4147 fIndirect = medium->isReadOnly();
4148 bool associate = true;
4149
4150 do
4151 {
4152 if ( aType == DeviceType_HardDisk
4153 && mMediaData.isBackedUp())
4154 {
4155 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4156
4157 /* check if the medium was attached to the VM before we started
4158 * changing attachments in which case the attachment just needs to
4159 * be restored */
4160 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4161 {
4162 AssertReturn(!fIndirect, E_FAIL);
4163
4164 /* see if it's the same bus/channel/device */
4165 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4166 {
4167 /* the simplest case: restore the whole attachment
4168 * and return, nothing else to do */
4169 mMediaData->mAttachments.push_back(pAttachTemp);
4170
4171 /* Reattach the medium to the VM. */
4172 if (fHotplug || fSilent)
4173 {
4174 mediumLock.release();
4175 treeLock.release();
4176 alock.release();
4177
4178 MediumLockList *pMediumLockList(new MediumLockList());
4179
4180 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4181 true /* fMediumLockWrite */,
4182 NULL,
4183 *pMediumLockList);
4184 alock.acquire();
4185 if (FAILED(rc))
4186 delete pMediumLockList;
4187 else
4188 {
4189 mData->mSession.mLockedMedia.Unlock();
4190 alock.release();
4191 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4192 mData->mSession.mLockedMedia.Lock();
4193 alock.acquire();
4194 }
4195 alock.release();
4196
4197 if (SUCCEEDED(rc))
4198 {
4199 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4200 /* Remove lock list in case of error. */
4201 if (FAILED(rc))
4202 {
4203 mData->mSession.mLockedMedia.Unlock();
4204 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4205 mData->mSession.mLockedMedia.Lock();
4206 }
4207 }
4208 }
4209
4210 return S_OK;
4211 }
4212
4213 /* bus/channel/device differ; we need a new attachment object,
4214 * but don't try to associate it again */
4215 associate = false;
4216 break;
4217 }
4218 }
4219
4220 /* go further only if the attachment is to be indirect */
4221 if (!fIndirect)
4222 break;
4223
4224 /* perform the so called smart attachment logic for indirect
4225 * attachments. Note that smart attachment is only applicable to base
4226 * hard disks. */
4227
4228 if (medium->getParent().isNull())
4229 {
4230 /* first, investigate the backup copy of the current hard disk
4231 * attachments to make it possible to re-attach existing diffs to
4232 * another device slot w/o losing their contents */
4233 if (mMediaData.isBackedUp())
4234 {
4235 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4236
4237 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4238 uint32_t foundLevel = 0;
4239
4240 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4241 it != oldAtts.end();
4242 ++it)
4243 {
4244 uint32_t level = 0;
4245 MediumAttachment *pAttach = *it;
4246 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4247 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4248 if (pMedium.isNull())
4249 continue;
4250
4251 if (pMedium->getBase(&level) == medium)
4252 {
4253 /* skip the hard disk if its currently attached (we
4254 * cannot attach the same hard disk twice) */
4255 if (findAttachment(mMediaData->mAttachments,
4256 pMedium))
4257 continue;
4258
4259 /* matched device, channel and bus (i.e. attached to the
4260 * same place) will win and immediately stop the search;
4261 * otherwise the attachment that has the youngest
4262 * descendant of medium will be used
4263 */
4264 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4265 {
4266 /* the simplest case: restore the whole attachment
4267 * and return, nothing else to do */
4268 mMediaData->mAttachments.push_back(*it);
4269
4270 /* Reattach the medium to the VM. */
4271 if (fHotplug || fSilent)
4272 {
4273 mediumLock.release();
4274 treeLock.release();
4275 alock.release();
4276
4277 MediumLockList *pMediumLockList(new MediumLockList());
4278
4279 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4280 true /* fMediumLockWrite */,
4281 NULL,
4282 *pMediumLockList);
4283 alock.acquire();
4284 if (FAILED(rc))
4285 delete pMediumLockList;
4286 else
4287 {
4288 mData->mSession.mLockedMedia.Unlock();
4289 alock.release();
4290 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4291 mData->mSession.mLockedMedia.Lock();
4292 alock.acquire();
4293 }
4294 alock.release();
4295
4296 if (SUCCEEDED(rc))
4297 {
4298 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4299 /* Remove lock list in case of error. */
4300 if (FAILED(rc))
4301 {
4302 mData->mSession.mLockedMedia.Unlock();
4303 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4304 mData->mSession.mLockedMedia.Lock();
4305 }
4306 }
4307 }
4308
4309 return S_OK;
4310 }
4311 else if ( foundIt == oldAtts.end()
4312 || level > foundLevel /* prefer younger */
4313 )
4314 {
4315 foundIt = it;
4316 foundLevel = level;
4317 }
4318 }
4319 }
4320
4321 if (foundIt != oldAtts.end())
4322 {
4323 /* use the previously attached hard disk */
4324 medium = (*foundIt)->getMedium();
4325 mediumCaller.attach(medium);
4326 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4327 mediumLock.attach(medium);
4328 /* not implicit, doesn't require association with this VM */
4329 fIndirect = false;
4330 associate = false;
4331 /* go right to the MediumAttachment creation */
4332 break;
4333 }
4334 }
4335
4336 /* must give up the medium lock and medium tree lock as below we
4337 * go over snapshots, which needs a lock with higher lock order. */
4338 mediumLock.release();
4339 treeLock.release();
4340
4341 /* then, search through snapshots for the best diff in the given
4342 * hard disk's chain to base the new diff on */
4343
4344 ComObjPtr<Medium> base;
4345 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4346 while (snap)
4347 {
4348 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4349
4350 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4351
4352 MediumAttachment *pAttachFound = NULL;
4353 uint32_t foundLevel = 0;
4354
4355 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4356 it != snapAtts.end();
4357 ++it)
4358 {
4359 MediumAttachment *pAttach = *it;
4360 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4361 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4362 if (pMedium.isNull())
4363 continue;
4364
4365 uint32_t level = 0;
4366 if (pMedium->getBase(&level) == medium)
4367 {
4368 /* matched device, channel and bus (i.e. attached to the
4369 * same place) will win and immediately stop the search;
4370 * otherwise the attachment that has the youngest
4371 * descendant of medium will be used
4372 */
4373 if ( pAttach->getDevice() == aDevice
4374 && pAttach->getPort() == aControllerPort
4375 && pAttach->getControllerName() == aControllerName
4376 )
4377 {
4378 pAttachFound = pAttach;
4379 break;
4380 }
4381 else if ( !pAttachFound
4382 || level > foundLevel /* prefer younger */
4383 )
4384 {
4385 pAttachFound = pAttach;
4386 foundLevel = level;
4387 }
4388 }
4389 }
4390
4391 if (pAttachFound)
4392 {
4393 base = pAttachFound->getMedium();
4394 break;
4395 }
4396
4397 snap = snap->getParent();
4398 }
4399
4400 /* re-lock medium tree and the medium, as we need it below */
4401 treeLock.acquire();
4402 mediumLock.acquire();
4403
4404 /* found a suitable diff, use it as a base */
4405 if (!base.isNull())
4406 {
4407 medium = base;
4408 mediumCaller.attach(medium);
4409 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4410 mediumLock.attach(medium);
4411 }
4412 }
4413
4414 Utf8Str strFullSnapshotFolder;
4415 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4416
4417 ComObjPtr<Medium> diff;
4418 diff.createObject();
4419 // store this diff in the same registry as the parent
4420 Guid uuidRegistryParent;
4421 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4422 {
4423 // parent image has no registry: this can happen if we're attaching a new immutable
4424 // image that has not yet been attached (medium then points to the base and we're
4425 // creating the diff image for the immutable, and the parent is not yet registered);
4426 // put the parent in the machine registry then
4427 mediumLock.release();
4428 treeLock.release();
4429 alock.release();
4430 addMediumToRegistry(medium);
4431 alock.acquire();
4432 treeLock.acquire();
4433 mediumLock.acquire();
4434 medium->getFirstRegistryMachineId(uuidRegistryParent);
4435 }
4436 rc = diff->init(mParent,
4437 medium->getPreferredDiffFormat(),
4438 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4439 uuidRegistryParent);
4440 if (FAILED(rc)) return rc;
4441
4442 /* Apply the normal locking logic to the entire chain. */
4443 MediumLockList *pMediumLockList(new MediumLockList());
4444 mediumLock.release();
4445 treeLock.release();
4446 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4447 true /* fMediumLockWrite */,
4448 medium,
4449 *pMediumLockList);
4450 treeLock.acquire();
4451 mediumLock.acquire();
4452 if (SUCCEEDED(rc))
4453 {
4454 mediumLock.release();
4455 treeLock.release();
4456 rc = pMediumLockList->Lock();
4457 treeLock.acquire();
4458 mediumLock.acquire();
4459 if (FAILED(rc))
4460 setError(rc,
4461 tr("Could not lock medium when creating diff '%s'"),
4462 diff->getLocationFull().c_str());
4463 else
4464 {
4465 /* will release the lock before the potentially lengthy
4466 * operation, so protect with the special state */
4467 MachineState_T oldState = mData->mMachineState;
4468 setMachineState(MachineState_SettingUp);
4469
4470 mediumLock.release();
4471 treeLock.release();
4472 alock.release();
4473
4474 rc = medium->createDiffStorage(diff,
4475 MediumVariant_Standard,
4476 pMediumLockList,
4477 NULL /* aProgress */,
4478 true /* aWait */);
4479
4480 alock.acquire();
4481 treeLock.acquire();
4482 mediumLock.acquire();
4483
4484 setMachineState(oldState);
4485 }
4486 }
4487
4488 /* Unlock the media and free the associated memory. */
4489 delete pMediumLockList;
4490
4491 if (FAILED(rc)) return rc;
4492
4493 /* use the created diff for the actual attachment */
4494 medium = diff;
4495 mediumCaller.attach(medium);
4496 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4497 mediumLock.attach(medium);
4498 }
4499 while (0);
4500
4501 ComObjPtr<MediumAttachment> attachment;
4502 attachment.createObject();
4503 rc = attachment->init(this,
4504 medium,
4505 aControllerName,
4506 aControllerPort,
4507 aDevice,
4508 aType,
4509 fIndirect,
4510 false /* fPassthrough */,
4511 false /* fTempEject */,
4512 false /* fNonRotational */,
4513 false /* fDiscard */,
4514 fHotplug /* fHotPluggable */,
4515 Utf8Str::Empty);
4516 if (FAILED(rc)) return rc;
4517
4518 if (associate && !medium.isNull())
4519 {
4520 // as the last step, associate the medium to the VM
4521 rc = medium->addBackReference(mData->mUuid);
4522 // here we can fail because of Deleting, or being in process of creating a Diff
4523 if (FAILED(rc)) return rc;
4524
4525 mediumLock.release();
4526 treeLock.release();
4527 alock.release();
4528 addMediumToRegistry(medium);
4529 alock.acquire();
4530 treeLock.acquire();
4531 mediumLock.acquire();
4532 }
4533
4534 /* success: finally remember the attachment */
4535 setModified(IsModified_Storage);
4536 mMediaData.backup();
4537 mMediaData->mAttachments.push_back(attachment);
4538
4539 mediumLock.release();
4540 treeLock.release();
4541 alock.release();
4542
4543 if (fHotplug || fSilent)
4544 {
4545 if (!medium.isNull())
4546 {
4547 MediumLockList *pMediumLockList(new MediumLockList());
4548
4549 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4550 true /* fMediumLockWrite */,
4551 NULL,
4552 *pMediumLockList);
4553 alock.acquire();
4554 if (FAILED(rc))
4555 delete pMediumLockList;
4556 else
4557 {
4558 mData->mSession.mLockedMedia.Unlock();
4559 alock.release();
4560 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4561 mData->mSession.mLockedMedia.Lock();
4562 alock.acquire();
4563 }
4564 alock.release();
4565 }
4566
4567 if (SUCCEEDED(rc))
4568 {
4569 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4570 /* Remove lock list in case of error. */
4571 if (FAILED(rc))
4572 {
4573 mData->mSession.mLockedMedia.Unlock();
4574 mData->mSession.mLockedMedia.Remove(attachment);
4575 mData->mSession.mLockedMedia.Lock();
4576 }
4577 }
4578 }
4579
4580 mParent->saveModifiedRegistries();
4581
4582 return rc;
4583}
4584
4585STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4586 LONG aDevice)
4587{
4588 CheckComArgStrNotEmptyOrNull(aControllerName);
4589
4590 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4591 aControllerName, aControllerPort, aDevice));
4592
4593 AutoCaller autoCaller(this);
4594 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4595
4596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4597
4598 HRESULT rc = checkStateDependency(MutableStateDep);
4599 if (FAILED(rc)) return rc;
4600
4601 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4602
4603 /* Check for an existing controller. */
4604 ComObjPtr<StorageController> ctl;
4605 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4606 if (FAILED(rc)) return rc;
4607
4608 StorageControllerType_T ctrlType;
4609 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4610 if (FAILED(rc))
4611 return setError(E_FAIL,
4612 tr("Could not get type of controller '%ls'"),
4613 aControllerName);
4614
4615 bool fSilent = false;
4616 Utf8Str strReconfig;
4617
4618 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4619 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4620 if ( mData->mMachineState == MachineState_Paused
4621 && strReconfig == "1")
4622 fSilent = true;
4623
4624 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4625 bool fHotplug = false;
4626 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4627 fHotplug = true;
4628
4629 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4630 return setError(VBOX_E_INVALID_VM_STATE,
4631 tr("Controller '%ls' does not support hotplugging"),
4632 aControllerName);
4633
4634 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4635 aControllerName,
4636 aControllerPort,
4637 aDevice);
4638 if (!pAttach)
4639 return setError(VBOX_E_OBJECT_NOT_FOUND,
4640 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4641 aDevice, aControllerPort, aControllerName);
4642
4643 if (fHotplug && !pAttach->getHotPluggable())
4644 return setError(VBOX_E_NOT_SUPPORTED,
4645 tr("The device slot %d on port %d of controller '%ls' does not support hotplugging"),
4646 aDevice, aControllerPort, aControllerName);
4647
4648 /*
4649 * The VM has to detach the device before we delete any implicit diffs.
4650 * If this fails we can roll back without loosing data.
4651 */
4652 if (fHotplug || fSilent)
4653 {
4654 alock.release();
4655 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4656 alock.acquire();
4657 }
4658 if (FAILED(rc)) return rc;
4659
4660 /* If we are here everything went well and we can delete the implicit now. */
4661 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4662
4663 alock.release();
4664
4665 mParent->saveModifiedRegistries();
4666
4667 return rc;
4668}
4669
4670STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4671 LONG aDevice, BOOL aPassthrough)
4672{
4673 CheckComArgStrNotEmptyOrNull(aControllerName);
4674
4675 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4676 aControllerName, aControllerPort, aDevice, aPassthrough));
4677
4678 AutoCaller autoCaller(this);
4679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4680
4681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4682
4683 HRESULT rc = checkStateDependency(MutableStateDep);
4684 if (FAILED(rc)) return rc;
4685
4686 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4687
4688 if (Global::IsOnlineOrTransient(mData->mMachineState))
4689 return setError(VBOX_E_INVALID_VM_STATE,
4690 tr("Invalid machine state: %s"),
4691 Global::stringifyMachineState(mData->mMachineState));
4692
4693 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4694 aControllerName,
4695 aControllerPort,
4696 aDevice);
4697 if (!pAttach)
4698 return setError(VBOX_E_OBJECT_NOT_FOUND,
4699 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4700 aDevice, aControllerPort, aControllerName);
4701
4702
4703 setModified(IsModified_Storage);
4704 mMediaData.backup();
4705
4706 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4707
4708 if (pAttach->getType() != DeviceType_DVD)
4709 return setError(E_INVALIDARG,
4710 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4711 aDevice, aControllerPort, aControllerName);
4712 pAttach->updatePassthrough(!!aPassthrough);
4713
4714 return S_OK;
4715}
4716
4717STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4718 LONG aDevice, BOOL aTemporaryEject)
4719{
4720 CheckComArgStrNotEmptyOrNull(aControllerName);
4721
4722 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4723 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4724
4725 AutoCaller autoCaller(this);
4726 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4727
4728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4729
4730 HRESULT rc = checkStateDependency(MutableStateDep);
4731 if (FAILED(rc)) return rc;
4732
4733 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4734 aControllerName,
4735 aControllerPort,
4736 aDevice);
4737 if (!pAttach)
4738 return setError(VBOX_E_OBJECT_NOT_FOUND,
4739 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4740 aDevice, aControllerPort, aControllerName);
4741
4742
4743 setModified(IsModified_Storage);
4744 mMediaData.backup();
4745
4746 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4747
4748 if (pAttach->getType() != DeviceType_DVD)
4749 return setError(E_INVALIDARG,
4750 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4751 aDevice, aControllerPort, aControllerName);
4752 pAttach->updateTempEject(!!aTemporaryEject);
4753
4754 return S_OK;
4755}
4756
4757STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4758 LONG aDevice, BOOL aNonRotational)
4759{
4760 CheckComArgStrNotEmptyOrNull(aControllerName);
4761
4762 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4763 aControllerName, aControllerPort, aDevice, aNonRotational));
4764
4765 AutoCaller autoCaller(this);
4766 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4767
4768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4769
4770 HRESULT rc = checkStateDependency(MutableStateDep);
4771 if (FAILED(rc)) return rc;
4772
4773 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4774
4775 if (Global::IsOnlineOrTransient(mData->mMachineState))
4776 return setError(VBOX_E_INVALID_VM_STATE,
4777 tr("Invalid machine state: %s"),
4778 Global::stringifyMachineState(mData->mMachineState));
4779
4780 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4781 aControllerName,
4782 aControllerPort,
4783 aDevice);
4784 if (!pAttach)
4785 return setError(VBOX_E_OBJECT_NOT_FOUND,
4786 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4787 aDevice, aControllerPort, aControllerName);
4788
4789
4790 setModified(IsModified_Storage);
4791 mMediaData.backup();
4792
4793 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4794
4795 if (pAttach->getType() != DeviceType_HardDisk)
4796 return setError(E_INVALIDARG,
4797 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4798 aDevice, aControllerPort, aControllerName);
4799 pAttach->updateNonRotational(!!aNonRotational);
4800
4801 return S_OK;
4802}
4803
4804STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4805 LONG aDevice, BOOL aDiscard)
4806{
4807 CheckComArgStrNotEmptyOrNull(aControllerName);
4808
4809 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4810 aControllerName, aControllerPort, aDevice, aDiscard));
4811
4812 AutoCaller autoCaller(this);
4813 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4814
4815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4816
4817 HRESULT rc = checkStateDependency(MutableStateDep);
4818 if (FAILED(rc)) return rc;
4819
4820 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4821
4822 if (Global::IsOnlineOrTransient(mData->mMachineState))
4823 return setError(VBOX_E_INVALID_VM_STATE,
4824 tr("Invalid machine state: %s"),
4825 Global::stringifyMachineState(mData->mMachineState));
4826
4827 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4828 aControllerName,
4829 aControllerPort,
4830 aDevice);
4831 if (!pAttach)
4832 return setError(VBOX_E_OBJECT_NOT_FOUND,
4833 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4834 aDevice, aControllerPort, aControllerName);
4835
4836
4837 setModified(IsModified_Storage);
4838 mMediaData.backup();
4839
4840 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4841
4842 if (pAttach->getType() != DeviceType_HardDisk)
4843 return setError(E_INVALIDARG,
4844 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4845 aDevice, aControllerPort, aControllerName);
4846 pAttach->updateDiscard(!!aDiscard);
4847
4848 return S_OK;
4849}
4850
4851STDMETHODIMP Machine::SetHotPluggableForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4852 LONG aDevice, BOOL aHotPluggable)
4853{
4854 CheckComArgStrNotEmptyOrNull(aControllerName);
4855
4856 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4857 aControllerName, aControllerPort, aDevice, aHotPluggable));
4858
4859 AutoCaller autoCaller(this);
4860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4861
4862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4863
4864 HRESULT rc = checkStateDependency(MutableStateDep);
4865 if (FAILED(rc)) return rc;
4866
4867 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4868
4869 if (Global::IsOnlineOrTransient(mData->mMachineState))
4870 return setError(VBOX_E_INVALID_VM_STATE,
4871 tr("Invalid machine state: %s"),
4872 Global::stringifyMachineState(mData->mMachineState));
4873
4874 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4875 aControllerName,
4876 aControllerPort,
4877 aDevice);
4878 if (!pAttach)
4879 return setError(VBOX_E_OBJECT_NOT_FOUND,
4880 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4881 aDevice, aControllerPort, aControllerName);
4882
4883 /* Check for an existing controller. */
4884 ComObjPtr<StorageController> ctl;
4885 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4886 if (FAILED(rc)) return rc;
4887
4888 StorageControllerType_T ctrlType;
4889 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4890 if (FAILED(rc))
4891 return setError(E_FAIL,
4892 tr("Could not get type of controller '%ls'"),
4893 aControllerName);
4894
4895 if (!isControllerHotplugCapable(ctrlType))
4896 return setError(VBOX_E_NOT_SUPPORTED,
4897 tr("Controller '%ls' does not support changing the hot-pluggable device flag"),
4898 aControllerName);
4899
4900 setModified(IsModified_Storage);
4901 mMediaData.backup();
4902
4903 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4904
4905 if (pAttach->getType() == DeviceType_Floppy)
4906 return setError(E_INVALIDARG,
4907 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%ls' is a floppy drive"),
4908 aDevice, aControllerPort, aControllerName);
4909 pAttach->updateHotPluggable(!!aHotPluggable);
4910
4911 return S_OK;
4912}
4913
4914STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4915 LONG aDevice)
4916{
4917 int rc = S_OK;
4918 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4919 aControllerName, aControllerPort, aDevice));
4920
4921 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4922
4923 return rc;
4924}
4925
4926STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4927 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4928{
4929 CheckComArgStrNotEmptyOrNull(aControllerName);
4930
4931 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4932 aControllerName, aControllerPort, aDevice));
4933
4934 AutoCaller autoCaller(this);
4935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4936
4937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4938
4939 HRESULT rc = checkStateDependency(MutableStateDep);
4940 if (FAILED(rc)) return rc;
4941
4942 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4943
4944 if (Global::IsOnlineOrTransient(mData->mMachineState))
4945 return setError(VBOX_E_INVALID_VM_STATE,
4946 tr("Invalid machine state: %s"),
4947 Global::stringifyMachineState(mData->mMachineState));
4948
4949 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4950 aControllerName,
4951 aControllerPort,
4952 aDevice);
4953 if (!pAttach)
4954 return setError(VBOX_E_OBJECT_NOT_FOUND,
4955 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4956 aDevice, aControllerPort, aControllerName);
4957
4958
4959 setModified(IsModified_Storage);
4960 mMediaData.backup();
4961
4962 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4963 if (aBandwidthGroup && group.isNull())
4964 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4965
4966 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4967
4968 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4969 if (strBandwidthGroupOld.isNotEmpty())
4970 {
4971 /* Get the bandwidth group object and release it - this must not fail. */
4972 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4973 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4974 Assert(SUCCEEDED(rc));
4975
4976 pBandwidthGroupOld->release();
4977 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4978 }
4979
4980 if (!group.isNull())
4981 {
4982 group->reference();
4983 pAttach->updateBandwidthGroup(group->getName());
4984 }
4985
4986 return S_OK;
4987}
4988
4989STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4990 LONG aControllerPort,
4991 LONG aDevice,
4992 DeviceType_T aType)
4993{
4994 HRESULT rc = S_OK;
4995
4996 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4997 aControllerName, aControllerPort, aDevice, aType));
4998
4999 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
5000
5001 return rc;
5002}
5003
5004
5005
5006STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
5007 LONG aControllerPort,
5008 LONG aDevice,
5009 BOOL aForce)
5010{
5011 int rc = S_OK;
5012 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
5013 aControllerName, aControllerPort, aForce));
5014
5015 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
5016
5017 return rc;
5018}
5019
5020STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
5021 LONG aControllerPort,
5022 LONG aDevice,
5023 IMedium *aMedium,
5024 BOOL aForce)
5025{
5026 int rc = S_OK;
5027 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
5028 aControllerName, aControllerPort, aDevice, aForce));
5029
5030 CheckComArgStrNotEmptyOrNull(aControllerName);
5031
5032 AutoCaller autoCaller(this);
5033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5034
5035 // request the host lock first, since might be calling Host methods for getting host drives;
5036 // next, protect the media tree all the while we're in here, as well as our member variables
5037 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
5038 this->lockHandle(),
5039 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5040
5041 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5042 aControllerName,
5043 aControllerPort,
5044 aDevice);
5045 if (pAttach.isNull())
5046 return setError(VBOX_E_OBJECT_NOT_FOUND,
5047 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
5048 aDevice, aControllerPort, aControllerName);
5049
5050 /* Remember previously mounted medium. The medium before taking the
5051 * backup is not necessarily the same thing. */
5052 ComObjPtr<Medium> oldmedium;
5053 oldmedium = pAttach->getMedium();
5054
5055 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
5056 if (aMedium && pMedium.isNull())
5057 return setError(E_INVALIDARG, "The given medium pointer is invalid");
5058
5059 AutoCaller mediumCaller(pMedium);
5060 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
5061
5062 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5063 if (pMedium)
5064 {
5065 DeviceType_T mediumType = pAttach->getType();
5066 switch (mediumType)
5067 {
5068 case DeviceType_DVD:
5069 case DeviceType_Floppy:
5070 break;
5071
5072 default:
5073 return setError(VBOX_E_INVALID_OBJECT_STATE,
5074 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5075 aControllerPort,
5076 aDevice,
5077 aControllerName);
5078 }
5079 }
5080
5081 setModified(IsModified_Storage);
5082 mMediaData.backup();
5083
5084 {
5085 // The backup operation makes the pAttach reference point to the
5086 // old settings. Re-get the correct reference.
5087 pAttach = findAttachment(mMediaData->mAttachments,
5088 aControllerName,
5089 aControllerPort,
5090 aDevice);
5091 if (!oldmedium.isNull())
5092 oldmedium->removeBackReference(mData->mUuid);
5093 if (!pMedium.isNull())
5094 {
5095 pMedium->addBackReference(mData->mUuid);
5096
5097 mediumLock.release();
5098 multiLock.release();
5099 addMediumToRegistry(pMedium);
5100 multiLock.acquire();
5101 mediumLock.acquire();
5102 }
5103
5104 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5105 pAttach->updateMedium(pMedium);
5106 }
5107
5108 setModified(IsModified_Storage);
5109
5110 mediumLock.release();
5111 multiLock.release();
5112 rc = onMediumChange(pAttach, aForce);
5113 multiLock.acquire();
5114 mediumLock.acquire();
5115
5116 /* On error roll back this change only. */
5117 if (FAILED(rc))
5118 {
5119 if (!pMedium.isNull())
5120 pMedium->removeBackReference(mData->mUuid);
5121 pAttach = findAttachment(mMediaData->mAttachments,
5122 aControllerName,
5123 aControllerPort,
5124 aDevice);
5125 /* If the attachment is gone in the meantime, bail out. */
5126 if (pAttach.isNull())
5127 return rc;
5128 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5129 if (!oldmedium.isNull())
5130 oldmedium->addBackReference(mData->mUuid);
5131 pAttach->updateMedium(oldmedium);
5132 }
5133
5134 mediumLock.release();
5135 multiLock.release();
5136
5137 mParent->saveModifiedRegistries();
5138
5139 return rc;
5140}
5141
5142STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5143 LONG aControllerPort,
5144 LONG aDevice,
5145 IMedium **aMedium)
5146{
5147 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5148 aControllerName, aControllerPort, aDevice));
5149
5150 CheckComArgStrNotEmptyOrNull(aControllerName);
5151 CheckComArgOutPointerValid(aMedium);
5152
5153 AutoCaller autoCaller(this);
5154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5155
5156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5157
5158 *aMedium = NULL;
5159
5160 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5161 aControllerName,
5162 aControllerPort,
5163 aDevice);
5164 if (pAttach.isNull())
5165 return setError(VBOX_E_OBJECT_NOT_FOUND,
5166 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5167 aDevice, aControllerPort, aControllerName);
5168
5169 pAttach->getMedium().queryInterfaceTo(aMedium);
5170
5171 return S_OK;
5172}
5173
5174STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5175{
5176 CheckComArgOutPointerValid(port);
5177 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5178
5179 AutoCaller autoCaller(this);
5180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5181
5182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5183
5184 mSerialPorts[slot].queryInterfaceTo(port);
5185
5186 return S_OK;
5187}
5188
5189STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5190{
5191 CheckComArgOutPointerValid(port);
5192 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5193
5194 AutoCaller autoCaller(this);
5195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5196
5197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5198
5199 mParallelPorts[slot].queryInterfaceTo(port);
5200
5201 return S_OK;
5202}
5203
5204STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5205{
5206 CheckComArgOutPointerValid(adapter);
5207 /* Do not assert if slot is out of range, just return the advertised
5208 status. testdriver/vbox.py triggers this in logVmInfo. */
5209 if (slot >= mNetworkAdapters.size())
5210 return setError(E_INVALIDARG,
5211 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5212 slot, mNetworkAdapters.size());
5213
5214 AutoCaller autoCaller(this);
5215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5216
5217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5218
5219 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5220
5221 return S_OK;
5222}
5223
5224STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5225{
5226 CheckComArgOutSafeArrayPointerValid(aKeys);
5227
5228 AutoCaller autoCaller(this);
5229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5230
5231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5232
5233 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5234 int i = 0;
5235 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5236 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5237 ++it, ++i)
5238 {
5239 const Utf8Str &strKey = it->first;
5240 strKey.cloneTo(&saKeys[i]);
5241 }
5242 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5243
5244 return S_OK;
5245 }
5246
5247 /**
5248 * @note Locks this object for reading.
5249 */
5250STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5251 BSTR *aValue)
5252{
5253 CheckComArgStrNotEmptyOrNull(aKey);
5254 CheckComArgOutPointerValid(aValue);
5255
5256 AutoCaller autoCaller(this);
5257 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5258
5259 /* start with nothing found */
5260 Bstr bstrResult("");
5261
5262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5263
5264 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5265 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5266 // found:
5267 bstrResult = it->second; // source is a Utf8Str
5268
5269 /* return the result to caller (may be empty) */
5270 bstrResult.cloneTo(aValue);
5271
5272 return S_OK;
5273}
5274
5275 /**
5276 * @note Locks mParent for writing + this object for writing.
5277 */
5278STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5279{
5280 CheckComArgStrNotEmptyOrNull(aKey);
5281
5282 AutoCaller autoCaller(this);
5283 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5284
5285 Utf8Str strKey(aKey);
5286 Utf8Str strValue(aValue);
5287 Utf8Str strOldValue; // empty
5288
5289 // locking note: we only hold the read lock briefly to look up the old value,
5290 // then release it and call the onExtraCanChange callbacks. There is a small
5291 // chance of a race insofar as the callback might be called twice if two callers
5292 // change the same key at the same time, but that's a much better solution
5293 // than the deadlock we had here before. The actual changing of the extradata
5294 // is then performed under the write lock and race-free.
5295
5296 // look up the old value first; if nothing has changed then we need not do anything
5297 {
5298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5299 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5300 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5301 strOldValue = it->second;
5302 }
5303
5304 bool fChanged;
5305 if ((fChanged = (strOldValue != strValue)))
5306 {
5307 // ask for permission from all listeners outside the locks;
5308 // onExtraDataCanChange() only briefly requests the VirtualBox
5309 // lock to copy the list of callbacks to invoke
5310 Bstr error;
5311 Bstr bstrValue(aValue);
5312
5313 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5314 {
5315 const char *sep = error.isEmpty() ? "" : ": ";
5316 CBSTR err = error.raw();
5317 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5318 sep, err));
5319 return setError(E_ACCESSDENIED,
5320 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5321 aKey,
5322 bstrValue.raw(),
5323 sep,
5324 err);
5325 }
5326
5327 // data is changing and change not vetoed: then write it out under the lock
5328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5329
5330 if (isSnapshotMachine())
5331 {
5332 HRESULT rc = checkStateDependency(MutableStateDep);
5333 if (FAILED(rc)) return rc;
5334 }
5335
5336 if (strValue.isEmpty())
5337 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5338 else
5339 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5340 // creates a new key if needed
5341
5342 bool fNeedsGlobalSaveSettings = false;
5343 saveSettings(&fNeedsGlobalSaveSettings);
5344
5345 if (fNeedsGlobalSaveSettings)
5346 {
5347 // save the global settings; for that we should hold only the VirtualBox lock
5348 alock.release();
5349 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5350 mParent->saveSettings();
5351 }
5352 }
5353
5354 // fire notification outside the lock
5355 if (fChanged)
5356 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5357
5358 return S_OK;
5359}
5360
5361STDMETHODIMP Machine::SetSettingsFilePath(IN_BSTR aFilePath, IProgress **aProgress)
5362{
5363 CheckComArgStrNotEmptyOrNull(aFilePath);
5364 CheckComArgOutPointerValid(aProgress);
5365
5366 AutoCaller autoCaller(this);
5367 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5368
5369 *aProgress = NULL;
5370 ReturnComNotImplemented();
5371}
5372
5373STDMETHODIMP Machine::SaveSettings()
5374{
5375 AutoCaller autoCaller(this);
5376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5377
5378 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5379
5380 /* when there was auto-conversion, we want to save the file even if
5381 * the VM is saved */
5382 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5383 if (FAILED(rc)) return rc;
5384
5385 /* the settings file path may never be null */
5386 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5387
5388 /* save all VM data excluding snapshots */
5389 bool fNeedsGlobalSaveSettings = false;
5390 rc = saveSettings(&fNeedsGlobalSaveSettings);
5391 mlock.release();
5392
5393 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5394 {
5395 // save the global settings; for that we should hold only the VirtualBox lock
5396 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5397 rc = mParent->saveSettings();
5398 }
5399
5400 return rc;
5401}
5402
5403STDMETHODIMP Machine::DiscardSettings()
5404{
5405 AutoCaller autoCaller(this);
5406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5407
5408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5409
5410 HRESULT rc = checkStateDependency(MutableStateDep);
5411 if (FAILED(rc)) return rc;
5412
5413 /*
5414 * during this rollback, the session will be notified if data has
5415 * been actually changed
5416 */
5417 rollback(true /* aNotify */);
5418
5419 return S_OK;
5420}
5421
5422/** @note Locks objects! */
5423STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5424 ComSafeArrayOut(IMedium*, aMedia))
5425{
5426 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5427 AutoLimitedCaller autoCaller(this);
5428 AssertComRCReturnRC(autoCaller.rc());
5429
5430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5431
5432 Guid id(getId());
5433
5434 if (mData->mSession.mState != SessionState_Unlocked)
5435 return setError(VBOX_E_INVALID_OBJECT_STATE,
5436 tr("Cannot unregister the machine '%s' while it is locked"),
5437 mUserData->s.strName.c_str());
5438
5439 // wait for state dependents to drop to zero
5440 ensureNoStateDependencies();
5441
5442 if (!mData->mAccessible)
5443 {
5444 // inaccessible maschines can only be unregistered; uninitialize ourselves
5445 // here because currently there may be no unregistered that are inaccessible
5446 // (this state combination is not supported). Note releasing the caller and
5447 // leaving the lock before calling uninit()
5448 alock.release();
5449 autoCaller.release();
5450
5451 uninit();
5452
5453 mParent->unregisterMachine(this, id);
5454 // calls VirtualBox::saveSettings()
5455
5456 return S_OK;
5457 }
5458
5459 HRESULT rc = S_OK;
5460
5461 // discard saved state
5462 if (mData->mMachineState == MachineState_Saved)
5463 {
5464 // add the saved state file to the list of files the caller should delete
5465 Assert(!mSSData->strStateFilePath.isEmpty());
5466 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5467
5468 mSSData->strStateFilePath.setNull();
5469
5470 // unconditionally set the machine state to powered off, we now
5471 // know no session has locked the machine
5472 mData->mMachineState = MachineState_PoweredOff;
5473 }
5474
5475 size_t cSnapshots = 0;
5476 if (mData->mFirstSnapshot)
5477 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5478 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5479 // fail now before we start detaching media
5480 return setError(VBOX_E_INVALID_OBJECT_STATE,
5481 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5482 mUserData->s.strName.c_str(), cSnapshots);
5483
5484 // This list collects the medium objects from all medium attachments
5485 // which we will detach from the machine and its snapshots, in a specific
5486 // order which allows for closing all media without getting "media in use"
5487 // errors, simply by going through the list from the front to the back:
5488 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5489 // and must be closed before the parent media from the snapshots, or closing the parents
5490 // will fail because they still have children);
5491 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5492 // the root ("first") snapshot of the machine.
5493 MediaList llMedia;
5494
5495 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5496 && mMediaData->mAttachments.size()
5497 )
5498 {
5499 // we have media attachments: detach them all and add the Medium objects to our list
5500 if (cleanupMode != CleanupMode_UnregisterOnly)
5501 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5502 else
5503 return setError(VBOX_E_INVALID_OBJECT_STATE,
5504 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5505 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5506 }
5507
5508 if (cSnapshots)
5509 {
5510 // autoCleanup must be true here, or we would have failed above
5511
5512 // add the media from the medium attachments of the snapshots to llMedia
5513 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5514 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5515 // into the children first
5516
5517 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5518 MachineState_T oldState = mData->mMachineState;
5519 mData->mMachineState = MachineState_DeletingSnapshot;
5520
5521 // make a copy of the first snapshot so the refcount does not drop to 0
5522 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5523 // because of the AutoCaller voodoo)
5524 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5525
5526 // GO!
5527 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5528
5529 mData->mMachineState = oldState;
5530 }
5531
5532 if (FAILED(rc))
5533 {
5534 rollbackMedia();
5535 return rc;
5536 }
5537
5538 // commit all the media changes made above
5539 commitMedia();
5540
5541 mData->mRegistered = false;
5542
5543 // machine lock no longer needed
5544 alock.release();
5545
5546 // return media to caller
5547 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5548 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5549
5550 mParent->unregisterMachine(this, id);
5551 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5552
5553 return S_OK;
5554}
5555
5556struct Machine::DeleteTask
5557{
5558 ComObjPtr<Machine> pMachine;
5559 RTCList<ComPtr<IMedium> > llMediums;
5560 StringsList llFilesToDelete;
5561 ComObjPtr<Progress> pProgress;
5562};
5563
5564STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5565{
5566 LogFlowFuncEnter();
5567
5568 AutoCaller autoCaller(this);
5569 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5570
5571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5572
5573 HRESULT rc = checkStateDependency(MutableStateDep);
5574 if (FAILED(rc)) return rc;
5575
5576 if (mData->mRegistered)
5577 return setError(VBOX_E_INVALID_VM_STATE,
5578 tr("Cannot delete settings of a registered machine"));
5579
5580 DeleteTask *pTask = new DeleteTask;
5581 pTask->pMachine = this;
5582 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5583
5584 // collect files to delete
5585 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5586
5587 for (size_t i = 0; i < sfaMedia.size(); ++i)
5588 {
5589 IMedium *pIMedium(sfaMedia[i]);
5590 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5591 if (pMedium.isNull())
5592 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5593 SafeArray<BSTR> ids;
5594 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5595 if (FAILED(rc)) return rc;
5596 /* At this point the medium should not have any back references
5597 * anymore. If it has it is attached to another VM and *must* not
5598 * deleted. */
5599 if (ids.size() < 1)
5600 pTask->llMediums.append(pMedium);
5601 }
5602 if (mData->pMachineConfigFile->fileExists())
5603 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5604
5605 pTask->pProgress.createObject();
5606 pTask->pProgress->init(getVirtualBox(),
5607 static_cast<IMachine*>(this) /* aInitiator */,
5608 Bstr(tr("Deleting files")).raw(),
5609 true /* fCancellable */,
5610 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5611 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5612
5613 int vrc = RTThreadCreate(NULL,
5614 Machine::deleteThread,
5615 (void*)pTask,
5616 0,
5617 RTTHREADTYPE_MAIN_WORKER,
5618 0,
5619 "MachineDelete");
5620
5621 pTask->pProgress.queryInterfaceTo(aProgress);
5622
5623 if (RT_FAILURE(vrc))
5624 {
5625 delete pTask;
5626 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5627 }
5628
5629 LogFlowFuncLeave();
5630
5631 return S_OK;
5632}
5633
5634/**
5635 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5636 * calls Machine::deleteTaskWorker() on the actual machine object.
5637 * @param Thread
5638 * @param pvUser
5639 * @return
5640 */
5641/*static*/
5642DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5643{
5644 LogFlowFuncEnter();
5645
5646 DeleteTask *pTask = (DeleteTask*)pvUser;
5647 Assert(pTask);
5648 Assert(pTask->pMachine);
5649 Assert(pTask->pProgress);
5650
5651 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5652 pTask->pProgress->notifyComplete(rc);
5653
5654 delete pTask;
5655
5656 LogFlowFuncLeave();
5657
5658 NOREF(Thread);
5659
5660 return VINF_SUCCESS;
5661}
5662
5663/**
5664 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5665 * @param task
5666 * @return
5667 */
5668HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5669{
5670 AutoCaller autoCaller(this);
5671 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5672
5673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5674
5675 HRESULT rc = S_OK;
5676
5677 try
5678 {
5679 ULONG uLogHistoryCount = 3;
5680 ComPtr<ISystemProperties> systemProperties;
5681 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5682 if (FAILED(rc)) throw rc;
5683
5684 if (!systemProperties.isNull())
5685 {
5686 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5687 if (FAILED(rc)) throw rc;
5688 }
5689
5690 MachineState_T oldState = mData->mMachineState;
5691 setMachineState(MachineState_SettingUp);
5692 alock.release();
5693 for (size_t i = 0; i < task.llMediums.size(); ++i)
5694 {
5695 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5696 {
5697 AutoCaller mac(pMedium);
5698 if (FAILED(mac.rc())) throw mac.rc();
5699 Utf8Str strLocation = pMedium->getLocationFull();
5700 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5701 if (FAILED(rc)) throw rc;
5702 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5703 }
5704 ComPtr<IProgress> pProgress2;
5705 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5706 if (FAILED(rc)) throw rc;
5707 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5708 if (FAILED(rc)) throw rc;
5709 /* Check the result of the asynchrony process. */
5710 LONG iRc;
5711 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5712 if (FAILED(rc)) throw rc;
5713 /* If the thread of the progress object has an error, then
5714 * retrieve the error info from there, or it'll be lost. */
5715 if (FAILED(iRc))
5716 throw setError(ProgressErrorInfo(pProgress2));
5717 }
5718 setMachineState(oldState);
5719 alock.acquire();
5720
5721 // delete the files pushed on the task list by Machine::Delete()
5722 // (this includes saved states of the machine and snapshots and
5723 // medium storage files from the IMedium list passed in, and the
5724 // machine XML file)
5725 StringsList::const_iterator it = task.llFilesToDelete.begin();
5726 while (it != task.llFilesToDelete.end())
5727 {
5728 const Utf8Str &strFile = *it;
5729 LogFunc(("Deleting file %s\n", strFile.c_str()));
5730 int vrc = RTFileDelete(strFile.c_str());
5731 if (RT_FAILURE(vrc))
5732 throw setError(VBOX_E_IPRT_ERROR,
5733 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5734
5735 ++it;
5736 if (it == task.llFilesToDelete.end())
5737 {
5738 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5739 if (FAILED(rc)) throw rc;
5740 break;
5741 }
5742
5743 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5744 if (FAILED(rc)) throw rc;
5745 }
5746
5747 /* delete the settings only when the file actually exists */
5748 if (mData->pMachineConfigFile->fileExists())
5749 {
5750 /* Delete any backup or uncommitted XML files. Ignore failures.
5751 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5752 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5753 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5754 RTFileDelete(otherXml.c_str());
5755 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5756 RTFileDelete(otherXml.c_str());
5757
5758 /* delete the Logs folder, nothing important should be left
5759 * there (we don't check for errors because the user might have
5760 * some private files there that we don't want to delete) */
5761 Utf8Str logFolder;
5762 getLogFolder(logFolder);
5763 Assert(logFolder.length());
5764 if (RTDirExists(logFolder.c_str()))
5765 {
5766 /* Delete all VBox.log[.N] files from the Logs folder
5767 * (this must be in sync with the rotation logic in
5768 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5769 * files that may have been created by the GUI. */
5770 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5771 logFolder.c_str(), RTPATH_DELIMITER);
5772 RTFileDelete(log.c_str());
5773 log = Utf8StrFmt("%s%cVBox.png",
5774 logFolder.c_str(), RTPATH_DELIMITER);
5775 RTFileDelete(log.c_str());
5776 for (int i = uLogHistoryCount; i > 0; i--)
5777 {
5778 log = Utf8StrFmt("%s%cVBox.log.%d",
5779 logFolder.c_str(), RTPATH_DELIMITER, i);
5780 RTFileDelete(log.c_str());
5781 log = Utf8StrFmt("%s%cVBox.png.%d",
5782 logFolder.c_str(), RTPATH_DELIMITER, i);
5783 RTFileDelete(log.c_str());
5784 }
5785
5786 RTDirRemove(logFolder.c_str());
5787 }
5788
5789 /* delete the Snapshots folder, nothing important should be left
5790 * there (we don't check for errors because the user might have
5791 * some private files there that we don't want to delete) */
5792 Utf8Str strFullSnapshotFolder;
5793 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5794 Assert(!strFullSnapshotFolder.isEmpty());
5795 if (RTDirExists(strFullSnapshotFolder.c_str()))
5796 RTDirRemove(strFullSnapshotFolder.c_str());
5797
5798 // delete the directory that contains the settings file, but only
5799 // if it matches the VM name
5800 Utf8Str settingsDir;
5801 if (isInOwnDir(&settingsDir))
5802 RTDirRemove(settingsDir.c_str());
5803 }
5804
5805 alock.release();
5806
5807 mParent->saveModifiedRegistries();
5808 }
5809 catch (HRESULT aRC) { rc = aRC; }
5810
5811 return rc;
5812}
5813
5814STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5815{
5816 CheckComArgOutPointerValid(aSnapshot);
5817
5818 AutoCaller autoCaller(this);
5819 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5820
5821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5822
5823 ComObjPtr<Snapshot> pSnapshot;
5824 HRESULT rc;
5825
5826 if (!aNameOrId || !*aNameOrId)
5827 // null case (caller wants root snapshot): findSnapshotById() handles this
5828 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5829 else
5830 {
5831 Guid uuid(aNameOrId);
5832 if (uuid.isValid())
5833 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5834 else
5835 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5836 }
5837 pSnapshot.queryInterfaceTo(aSnapshot);
5838
5839 return rc;
5840}
5841
5842STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5843{
5844 CheckComArgStrNotEmptyOrNull(aName);
5845 CheckComArgStrNotEmptyOrNull(aHostPath);
5846
5847 AutoCaller autoCaller(this);
5848 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5849
5850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5851
5852 HRESULT rc = checkStateDependency(MutableStateDep);
5853 if (FAILED(rc)) return rc;
5854
5855 Utf8Str strName(aName);
5856
5857 ComObjPtr<SharedFolder> sharedFolder;
5858 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5859 if (SUCCEEDED(rc))
5860 return setError(VBOX_E_OBJECT_IN_USE,
5861 tr("Shared folder named '%s' already exists"),
5862 strName.c_str());
5863
5864 sharedFolder.createObject();
5865 rc = sharedFolder->init(getMachine(),
5866 strName,
5867 aHostPath,
5868 !!aWritable,
5869 !!aAutoMount,
5870 true /* fFailOnError */);
5871 if (FAILED(rc)) return rc;
5872
5873 setModified(IsModified_SharedFolders);
5874 mHWData.backup();
5875 mHWData->mSharedFolders.push_back(sharedFolder);
5876
5877 /* inform the direct session if any */
5878 alock.release();
5879 onSharedFolderChange();
5880
5881 return S_OK;
5882}
5883
5884STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5885{
5886 CheckComArgStrNotEmptyOrNull(aName);
5887
5888 AutoCaller autoCaller(this);
5889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5890
5891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5892
5893 HRESULT rc = checkStateDependency(MutableStateDep);
5894 if (FAILED(rc)) return rc;
5895
5896 ComObjPtr<SharedFolder> sharedFolder;
5897 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5898 if (FAILED(rc)) return rc;
5899
5900 setModified(IsModified_SharedFolders);
5901 mHWData.backup();
5902 mHWData->mSharedFolders.remove(sharedFolder);
5903
5904 /* inform the direct session if any */
5905 alock.release();
5906 onSharedFolderChange();
5907
5908 return S_OK;
5909}
5910
5911STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5912{
5913 CheckComArgOutPointerValid(aCanShow);
5914
5915 /* start with No */
5916 *aCanShow = FALSE;
5917
5918 AutoCaller autoCaller(this);
5919 AssertComRCReturnRC(autoCaller.rc());
5920
5921 ComPtr<IInternalSessionControl> directControl;
5922 {
5923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5924
5925 if (mData->mSession.mState != SessionState_Locked)
5926 return setError(VBOX_E_INVALID_VM_STATE,
5927 tr("Machine is not locked for session (session state: %s)"),
5928 Global::stringifySessionState(mData->mSession.mState));
5929
5930 directControl = mData->mSession.mDirectControl;
5931 }
5932
5933 /* ignore calls made after #OnSessionEnd() is called */
5934 if (!directControl)
5935 return S_OK;
5936
5937 LONG64 dummy;
5938 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5939}
5940
5941STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5942{
5943 CheckComArgOutPointerValid(aWinId);
5944
5945 AutoCaller autoCaller(this);
5946 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5947
5948 ComPtr<IInternalSessionControl> directControl;
5949 {
5950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5951
5952 if (mData->mSession.mState != SessionState_Locked)
5953 return setError(E_FAIL,
5954 tr("Machine is not locked for session (session state: %s)"),
5955 Global::stringifySessionState(mData->mSession.mState));
5956
5957 directControl = mData->mSession.mDirectControl;
5958 }
5959
5960 /* ignore calls made after #OnSessionEnd() is called */
5961 if (!directControl)
5962 return S_OK;
5963
5964 BOOL dummy;
5965 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5966}
5967
5968#ifdef VBOX_WITH_GUEST_PROPS
5969/**
5970 * Look up a guest property in VBoxSVC's internal structures.
5971 */
5972HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5973 BSTR *aValue,
5974 LONG64 *aTimestamp,
5975 BSTR *aFlags) const
5976{
5977 using namespace guestProp;
5978
5979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5980 Utf8Str strName(aName);
5981 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5982
5983 if (it != mHWData->mGuestProperties.end())
5984 {
5985 char szFlags[MAX_FLAGS_LEN + 1];
5986 it->second.strValue.cloneTo(aValue);
5987 *aTimestamp = it->second.mTimestamp;
5988 writeFlags(it->second.mFlags, szFlags);
5989 Bstr(szFlags).cloneTo(aFlags);
5990 }
5991
5992 return S_OK;
5993}
5994
5995/**
5996 * Query the VM that a guest property belongs to for the property.
5997 * @returns E_ACCESSDENIED if the VM process is not available or not
5998 * currently handling queries and the lookup should then be done in
5999 * VBoxSVC.
6000 */
6001HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
6002 BSTR *aValue,
6003 LONG64 *aTimestamp,
6004 BSTR *aFlags) const
6005{
6006 HRESULT rc;
6007 ComPtr<IInternalSessionControl> directControl;
6008 directControl = mData->mSession.mDirectControl;
6009
6010 /* fail if we were called after #OnSessionEnd() is called. This is a
6011 * silly race condition. */
6012
6013 /** @todo This code is bothering API clients (like python script clients) with
6014 * the AccessGuestProperty call, creating unncessary IPC. Need to
6015 * have a way of figuring out which kind of direct session it is... */
6016 if (!directControl)
6017 rc = E_ACCESSDENIED;
6018 else
6019 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
6020 false /* isSetter */,
6021 aValue, aTimestamp, aFlags);
6022 return rc;
6023}
6024#endif // VBOX_WITH_GUEST_PROPS
6025
6026STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
6027 BSTR *aValue,
6028 LONG64 *aTimestamp,
6029 BSTR *aFlags)
6030{
6031#ifndef VBOX_WITH_GUEST_PROPS
6032 ReturnComNotImplemented();
6033#else // VBOX_WITH_GUEST_PROPS
6034 CheckComArgStrNotEmptyOrNull(aName);
6035 CheckComArgOutPointerValid(aValue);
6036 CheckComArgOutPointerValid(aTimestamp);
6037 CheckComArgOutPointerValid(aFlags);
6038
6039 AutoCaller autoCaller(this);
6040 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6041
6042 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
6043 if (rc == E_ACCESSDENIED)
6044 /* The VM is not running or the service is not (yet) accessible */
6045 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
6046 return rc;
6047#endif // VBOX_WITH_GUEST_PROPS
6048}
6049
6050STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
6051{
6052 LONG64 dummyTimestamp;
6053 Bstr dummyFlags;
6054 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
6055}
6056
6057STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
6058{
6059 Bstr dummyValue;
6060 Bstr dummyFlags;
6061 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
6062}
6063
6064#ifdef VBOX_WITH_GUEST_PROPS
6065/**
6066 * Set a guest property in VBoxSVC's internal structures.
6067 */
6068HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
6069 IN_BSTR aFlags)
6070{
6071 using namespace guestProp;
6072
6073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6074 HRESULT rc = S_OK;
6075
6076 rc = checkStateDependency(MutableStateDep);
6077 if (FAILED(rc)) return rc;
6078
6079 try
6080 {
6081 Utf8Str utf8Name(aName);
6082 Utf8Str utf8Flags(aFlags);
6083 uint32_t fFlags = NILFLAG;
6084 if ( aFlags != NULL
6085 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6086 return setError(E_INVALIDARG,
6087 tr("Invalid guest property flag values: '%ls'"),
6088 aFlags);
6089
6090 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6091 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6092 if (it == mHWData->mGuestProperties.end())
6093 {
6094 if (!fDelete)
6095 {
6096 setModified(IsModified_MachineData);
6097 mHWData.backupEx();
6098
6099 RTTIMESPEC time;
6100 HWData::GuestProperty prop;
6101 prop.strValue = aValue;
6102 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6103 prop.mFlags = fFlags;
6104 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6105 }
6106 }
6107 else
6108 {
6109 if (it->second.mFlags & (RDONLYHOST))
6110 {
6111 rc = setError(E_ACCESSDENIED,
6112 tr("The property '%ls' cannot be changed by the host"),
6113 aName);
6114 }
6115 else
6116 {
6117 setModified(IsModified_MachineData);
6118 mHWData.backupEx();
6119
6120 /* The backupEx() operation invalidates our iterator,
6121 * so get a new one. */
6122 it = mHWData->mGuestProperties.find(utf8Name);
6123 Assert(it != mHWData->mGuestProperties.end());
6124
6125 if (!fDelete)
6126 {
6127 RTTIMESPEC time;
6128 it->second.strValue = aValue;
6129 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6130 it->second.mFlags = fFlags;
6131 }
6132 else
6133 mHWData->mGuestProperties.erase(it);
6134 }
6135 }
6136
6137 if ( SUCCEEDED(rc)
6138 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6139 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6140 RTSTR_MAX,
6141 utf8Name.c_str(),
6142 RTSTR_MAX,
6143 NULL)
6144 )
6145 )
6146 {
6147 alock.release();
6148
6149 mParent->onGuestPropertyChange(mData->mUuid, aName,
6150 aValue ? aValue : Bstr("").raw(),
6151 aFlags ? aFlags : Bstr("").raw());
6152 }
6153 }
6154 catch (std::bad_alloc &)
6155 {
6156 rc = E_OUTOFMEMORY;
6157 }
6158
6159 return rc;
6160}
6161
6162/**
6163 * Set a property on the VM that that property belongs to.
6164 * @returns E_ACCESSDENIED if the VM process is not available or not
6165 * currently handling queries and the setting should then be done in
6166 * VBoxSVC.
6167 */
6168HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6169 IN_BSTR aFlags)
6170{
6171 HRESULT rc;
6172
6173 try
6174 {
6175 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6176
6177 BSTR dummy = NULL; /* will not be changed (setter) */
6178 LONG64 dummy64;
6179 if (!directControl)
6180 rc = E_ACCESSDENIED;
6181 else
6182 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6183 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6184 true /* isSetter */,
6185 &dummy, &dummy64, &dummy);
6186 }
6187 catch (std::bad_alloc &)
6188 {
6189 rc = E_OUTOFMEMORY;
6190 }
6191
6192 return rc;
6193}
6194#endif // VBOX_WITH_GUEST_PROPS
6195
6196STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6197 IN_BSTR aFlags)
6198{
6199#ifndef VBOX_WITH_GUEST_PROPS
6200 ReturnComNotImplemented();
6201#else // VBOX_WITH_GUEST_PROPS
6202 CheckComArgStrNotEmptyOrNull(aName);
6203 CheckComArgMaybeNull(aFlags);
6204 CheckComArgMaybeNull(aValue);
6205
6206 AutoCaller autoCaller(this);
6207 if (FAILED(autoCaller.rc()))
6208 return autoCaller.rc();
6209
6210 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6211 if (rc == E_ACCESSDENIED)
6212 /* The VM is not running or the service is not (yet) accessible */
6213 rc = setGuestPropertyToService(aName, aValue, aFlags);
6214 return rc;
6215#endif // VBOX_WITH_GUEST_PROPS
6216}
6217
6218STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6219{
6220 return SetGuestProperty(aName, aValue, NULL);
6221}
6222
6223STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6224{
6225 return SetGuestProperty(aName, NULL, NULL);
6226}
6227
6228#ifdef VBOX_WITH_GUEST_PROPS
6229/**
6230 * Enumerate the guest properties in VBoxSVC's internal structures.
6231 */
6232HRESULT Machine::enumerateGuestPropertiesInService
6233 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6234 ComSafeArrayOut(BSTR, aValues),
6235 ComSafeArrayOut(LONG64, aTimestamps),
6236 ComSafeArrayOut(BSTR, aFlags))
6237{
6238 using namespace guestProp;
6239
6240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6241 Utf8Str strPatterns(aPatterns);
6242
6243 HWData::GuestPropertyMap propMap;
6244
6245 /*
6246 * Look for matching patterns and build up a list.
6247 */
6248 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6249 while (it != mHWData->mGuestProperties.end())
6250 {
6251 if ( strPatterns.isEmpty()
6252 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6253 RTSTR_MAX,
6254 it->first.c_str(),
6255 RTSTR_MAX,
6256 NULL)
6257 )
6258 {
6259 propMap.insert(*it);
6260 }
6261
6262 it++;
6263 }
6264
6265 alock.release();
6266
6267 /*
6268 * And build up the arrays for returning the property information.
6269 */
6270 size_t cEntries = propMap.size();
6271 SafeArray<BSTR> names(cEntries);
6272 SafeArray<BSTR> values(cEntries);
6273 SafeArray<LONG64> timestamps(cEntries);
6274 SafeArray<BSTR> flags(cEntries);
6275 size_t iProp = 0;
6276
6277 it = propMap.begin();
6278 while (it != propMap.end())
6279 {
6280 char szFlags[MAX_FLAGS_LEN + 1];
6281 it->first.cloneTo(&names[iProp]);
6282 it->second.strValue.cloneTo(&values[iProp]);
6283 timestamps[iProp] = it->second.mTimestamp;
6284 writeFlags(it->second.mFlags, szFlags);
6285 Bstr(szFlags).cloneTo(&flags[iProp++]);
6286 it++;
6287 }
6288 names.detachTo(ComSafeArrayOutArg(aNames));
6289 values.detachTo(ComSafeArrayOutArg(aValues));
6290 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6291 flags.detachTo(ComSafeArrayOutArg(aFlags));
6292 return S_OK;
6293}
6294
6295/**
6296 * Enumerate the properties managed by a VM.
6297 * @returns E_ACCESSDENIED if the VM process is not available or not
6298 * currently handling queries and the setting should then be done in
6299 * VBoxSVC.
6300 */
6301HRESULT Machine::enumerateGuestPropertiesOnVM
6302 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6303 ComSafeArrayOut(BSTR, aValues),
6304 ComSafeArrayOut(LONG64, aTimestamps),
6305 ComSafeArrayOut(BSTR, aFlags))
6306{
6307 HRESULT rc;
6308 ComPtr<IInternalSessionControl> directControl;
6309 directControl = mData->mSession.mDirectControl;
6310
6311 if (!directControl)
6312 rc = E_ACCESSDENIED;
6313 else
6314 rc = directControl->EnumerateGuestProperties
6315 (aPatterns, ComSafeArrayOutArg(aNames),
6316 ComSafeArrayOutArg(aValues),
6317 ComSafeArrayOutArg(aTimestamps),
6318 ComSafeArrayOutArg(aFlags));
6319 return rc;
6320}
6321#endif // VBOX_WITH_GUEST_PROPS
6322
6323STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6324 ComSafeArrayOut(BSTR, aNames),
6325 ComSafeArrayOut(BSTR, aValues),
6326 ComSafeArrayOut(LONG64, aTimestamps),
6327 ComSafeArrayOut(BSTR, aFlags))
6328{
6329#ifndef VBOX_WITH_GUEST_PROPS
6330 ReturnComNotImplemented();
6331#else // VBOX_WITH_GUEST_PROPS
6332 CheckComArgMaybeNull(aPatterns);
6333 CheckComArgOutSafeArrayPointerValid(aNames);
6334 CheckComArgOutSafeArrayPointerValid(aValues);
6335 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6336 CheckComArgOutSafeArrayPointerValid(aFlags);
6337
6338 AutoCaller autoCaller(this);
6339 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6340
6341 HRESULT rc = enumerateGuestPropertiesOnVM
6342 (aPatterns, ComSafeArrayOutArg(aNames),
6343 ComSafeArrayOutArg(aValues),
6344 ComSafeArrayOutArg(aTimestamps),
6345 ComSafeArrayOutArg(aFlags));
6346 if (rc == E_ACCESSDENIED)
6347 /* The VM is not running or the service is not (yet) accessible */
6348 rc = enumerateGuestPropertiesInService
6349 (aPatterns, ComSafeArrayOutArg(aNames),
6350 ComSafeArrayOutArg(aValues),
6351 ComSafeArrayOutArg(aTimestamps),
6352 ComSafeArrayOutArg(aFlags));
6353 return rc;
6354#endif // VBOX_WITH_GUEST_PROPS
6355}
6356
6357STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6358 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6359{
6360 MediaData::AttachmentList atts;
6361
6362 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6363 if (FAILED(rc)) return rc;
6364
6365 SafeIfaceArray<IMediumAttachment> attachments(atts);
6366 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6367
6368 return S_OK;
6369}
6370
6371STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6372 LONG aControllerPort,
6373 LONG aDevice,
6374 IMediumAttachment **aAttachment)
6375{
6376 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6377 aControllerName, aControllerPort, aDevice));
6378
6379 CheckComArgStrNotEmptyOrNull(aControllerName);
6380 CheckComArgOutPointerValid(aAttachment);
6381
6382 AutoCaller autoCaller(this);
6383 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6384
6385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6386
6387 *aAttachment = NULL;
6388
6389 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6390 aControllerName,
6391 aControllerPort,
6392 aDevice);
6393 if (pAttach.isNull())
6394 return setError(VBOX_E_OBJECT_NOT_FOUND,
6395 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6396 aDevice, aControllerPort, aControllerName);
6397
6398 pAttach.queryInterfaceTo(aAttachment);
6399
6400 return S_OK;
6401}
6402
6403STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6404 StorageBus_T aConnectionType,
6405 IStorageController **controller)
6406{
6407 CheckComArgStrNotEmptyOrNull(aName);
6408
6409 if ( (aConnectionType <= StorageBus_Null)
6410 || (aConnectionType > StorageBus_USB))
6411 return setError(E_INVALIDARG,
6412 tr("Invalid connection type: %d"),
6413 aConnectionType);
6414
6415 AutoCaller autoCaller(this);
6416 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6417
6418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6419
6420 HRESULT rc = checkStateDependency(MutableStateDep);
6421 if (FAILED(rc)) return rc;
6422
6423 /* try to find one with the name first. */
6424 ComObjPtr<StorageController> ctrl;
6425
6426 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6427 if (SUCCEEDED(rc))
6428 return setError(VBOX_E_OBJECT_IN_USE,
6429 tr("Storage controller named '%ls' already exists"),
6430 aName);
6431
6432 ctrl.createObject();
6433
6434 /* get a new instance number for the storage controller */
6435 ULONG ulInstance = 0;
6436 bool fBootable = true;
6437 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6438 it != mStorageControllers->end();
6439 ++it)
6440 {
6441 if ((*it)->getStorageBus() == aConnectionType)
6442 {
6443 ULONG ulCurInst = (*it)->getInstance();
6444
6445 if (ulCurInst >= ulInstance)
6446 ulInstance = ulCurInst + 1;
6447
6448 /* Only one controller of each type can be marked as bootable. */
6449 if ((*it)->getBootable())
6450 fBootable = false;
6451 }
6452 }
6453
6454 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6455 if (FAILED(rc)) return rc;
6456
6457 setModified(IsModified_Storage);
6458 mStorageControllers.backup();
6459 mStorageControllers->push_back(ctrl);
6460
6461 ctrl.queryInterfaceTo(controller);
6462
6463 /* inform the direct session if any */
6464 alock.release();
6465 onStorageControllerChange();
6466
6467 return S_OK;
6468}
6469
6470STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6471 IStorageController **aStorageController)
6472{
6473 CheckComArgStrNotEmptyOrNull(aName);
6474
6475 AutoCaller autoCaller(this);
6476 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6477
6478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6479
6480 ComObjPtr<StorageController> ctrl;
6481
6482 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6483 if (SUCCEEDED(rc))
6484 ctrl.queryInterfaceTo(aStorageController);
6485
6486 return rc;
6487}
6488
6489STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6490 IStorageController **aStorageController)
6491{
6492 AutoCaller autoCaller(this);
6493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6494
6495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6496
6497 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6498 it != mStorageControllers->end();
6499 ++it)
6500 {
6501 if ((*it)->getInstance() == aInstance)
6502 {
6503 (*it).queryInterfaceTo(aStorageController);
6504 return S_OK;
6505 }
6506 }
6507
6508 return setError(VBOX_E_OBJECT_NOT_FOUND,
6509 tr("Could not find a storage controller with instance number '%lu'"),
6510 aInstance);
6511}
6512
6513STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6514{
6515 AutoCaller autoCaller(this);
6516 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6517
6518 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6519
6520 HRESULT rc = checkStateDependency(MutableStateDep);
6521 if (FAILED(rc)) return rc;
6522
6523 ComObjPtr<StorageController> ctrl;
6524
6525 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6526 if (SUCCEEDED(rc))
6527 {
6528 /* Ensure that only one controller of each type is marked as bootable. */
6529 if (fBootable == TRUE)
6530 {
6531 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6532 it != mStorageControllers->end();
6533 ++it)
6534 {
6535 ComObjPtr<StorageController> aCtrl = (*it);
6536
6537 if ( (aCtrl->getName() != Utf8Str(aName))
6538 && aCtrl->getBootable() == TRUE
6539 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6540 && aCtrl->getControllerType() == ctrl->getControllerType())
6541 {
6542 aCtrl->setBootable(FALSE);
6543 break;
6544 }
6545 }
6546 }
6547
6548 if (SUCCEEDED(rc))
6549 {
6550 ctrl->setBootable(fBootable);
6551 setModified(IsModified_Storage);
6552 }
6553 }
6554
6555 if (SUCCEEDED(rc))
6556 {
6557 /* inform the direct session if any */
6558 alock.release();
6559 onStorageControllerChange();
6560 }
6561
6562 return rc;
6563}
6564
6565STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6566{
6567 CheckComArgStrNotEmptyOrNull(aName);
6568
6569 AutoCaller autoCaller(this);
6570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6571
6572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6573
6574 HRESULT rc = checkStateDependency(MutableStateDep);
6575 if (FAILED(rc)) return rc;
6576
6577 ComObjPtr<StorageController> ctrl;
6578 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6579 if (FAILED(rc)) return rc;
6580
6581 {
6582 /* find all attached devices to the appropriate storage controller and detach them all */
6583 // make a temporary list because detachDevice invalidates iterators into
6584 // mMediaData->mAttachments
6585 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6586
6587 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6588 it != llAttachments2.end();
6589 ++it)
6590 {
6591 MediumAttachment *pAttachTemp = *it;
6592
6593 AutoCaller localAutoCaller(pAttachTemp);
6594 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6595
6596 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6597
6598 if (pAttachTemp->getControllerName() == aName)
6599 {
6600 rc = detachDevice(pAttachTemp, alock, NULL);
6601 if (FAILED(rc)) return rc;
6602 }
6603 }
6604 }
6605
6606 /* We can remove it now. */
6607 setModified(IsModified_Storage);
6608 mStorageControllers.backup();
6609
6610 ctrl->unshare();
6611
6612 mStorageControllers->remove(ctrl);
6613
6614 /* inform the direct session if any */
6615 alock.release();
6616 onStorageControllerChange();
6617
6618 return S_OK;
6619}
6620
6621STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6622 IUSBController **controller)
6623{
6624 if ( (aType <= USBControllerType_Null)
6625 || (aType >= USBControllerType_Last))
6626 return setError(E_INVALIDARG,
6627 tr("Invalid USB controller type: %d"),
6628 aType);
6629
6630 AutoCaller autoCaller(this);
6631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6632
6633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6634
6635 HRESULT rc = checkStateDependency(MutableStateDep);
6636 if (FAILED(rc)) return rc;
6637
6638 /* try to find one with the same type first. */
6639 ComObjPtr<USBController> ctrl;
6640
6641 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6642 if (SUCCEEDED(rc))
6643 return setError(VBOX_E_OBJECT_IN_USE,
6644 tr("USB controller named '%ls' already exists"),
6645 aName);
6646
6647 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6648 ULONG maxInstances;
6649 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6650 if (FAILED(rc))
6651 return rc;
6652
6653 ULONG cInstances = getUSBControllerCountByType(aType);
6654 if (cInstances >= maxInstances)
6655 return setError(E_INVALIDARG,
6656 tr("Too many USB controllers of this type"));
6657
6658 ctrl.createObject();
6659
6660 rc = ctrl->init(this, aName, aType);
6661 if (FAILED(rc)) return rc;
6662
6663 setModified(IsModified_USB);
6664 mUSBControllers.backup();
6665 mUSBControllers->push_back(ctrl);
6666
6667 ctrl.queryInterfaceTo(controller);
6668
6669 /* inform the direct session if any */
6670 alock.release();
6671 onUSBControllerChange();
6672
6673 return S_OK;
6674}
6675
6676STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6677{
6678 CheckComArgStrNotEmptyOrNull(aName);
6679
6680 AutoCaller autoCaller(this);
6681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6682
6683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6684
6685 ComObjPtr<USBController> ctrl;
6686
6687 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6688 if (SUCCEEDED(rc))
6689 ctrl.queryInterfaceTo(aUSBController);
6690
6691 return rc;
6692}
6693
6694STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6695 ULONG *aControllers)
6696{
6697 CheckComArgOutPointerValid(aControllers);
6698
6699 if ( (aType <= USBControllerType_Null)
6700 || (aType >= USBControllerType_Last))
6701 return setError(E_INVALIDARG,
6702 tr("Invalid USB controller type: %d"),
6703 aType);
6704
6705 AutoCaller autoCaller(this);
6706 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6707
6708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6709
6710 ComObjPtr<USBController> ctrl;
6711
6712 *aControllers = getUSBControllerCountByType(aType);
6713
6714 return S_OK;
6715}
6716
6717STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6718{
6719 CheckComArgStrNotEmptyOrNull(aName);
6720
6721 AutoCaller autoCaller(this);
6722 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6723
6724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6725
6726 HRESULT rc = checkStateDependency(MutableStateDep);
6727 if (FAILED(rc)) return rc;
6728
6729 ComObjPtr<USBController> ctrl;
6730 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6731 if (FAILED(rc)) return rc;
6732
6733 setModified(IsModified_USB);
6734 mUSBControllers.backup();
6735
6736 ctrl->unshare();
6737
6738 mUSBControllers->remove(ctrl);
6739
6740 /* inform the direct session if any */
6741 alock.release();
6742 onUSBControllerChange();
6743
6744 return S_OK;
6745}
6746
6747STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6748 ULONG *puOriginX,
6749 ULONG *puOriginY,
6750 ULONG *puWidth,
6751 ULONG *puHeight,
6752 BOOL *pfEnabled)
6753{
6754 LogFlowThisFunc(("\n"));
6755
6756 CheckComArgNotNull(puOriginX);
6757 CheckComArgNotNull(puOriginY);
6758 CheckComArgNotNull(puWidth);
6759 CheckComArgNotNull(puHeight);
6760 CheckComArgNotNull(pfEnabled);
6761
6762 uint32_t u32OriginX= 0;
6763 uint32_t u32OriginY= 0;
6764 uint32_t u32Width = 0;
6765 uint32_t u32Height = 0;
6766 uint16_t u16Flags = 0;
6767
6768 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6769 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6770 if (RT_FAILURE(vrc))
6771 {
6772#ifdef RT_OS_WINDOWS
6773 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6774 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6775 * So just assign fEnable to TRUE again.
6776 * The right fix would be to change GUI API wrappers to make sure that parameters
6777 * are changed only if API succeeds.
6778 */
6779 *pfEnabled = TRUE;
6780#endif
6781 return setError(VBOX_E_IPRT_ERROR,
6782 tr("Saved guest size is not available (%Rrc)"),
6783 vrc);
6784 }
6785
6786 *puOriginX = u32OriginX;
6787 *puOriginY = u32OriginY;
6788 *puWidth = u32Width;
6789 *puHeight = u32Height;
6790 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6791
6792 return S_OK;
6793}
6794
6795STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6796{
6797 LogFlowThisFunc(("\n"));
6798
6799 CheckComArgNotNull(aSize);
6800 CheckComArgNotNull(aWidth);
6801 CheckComArgNotNull(aHeight);
6802
6803 if (aScreenId != 0)
6804 return E_NOTIMPL;
6805
6806 AutoCaller autoCaller(this);
6807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6808
6809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6810
6811 uint8_t *pu8Data = NULL;
6812 uint32_t cbData = 0;
6813 uint32_t u32Width = 0;
6814 uint32_t u32Height = 0;
6815
6816 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6817
6818 if (RT_FAILURE(vrc))
6819 return setError(VBOX_E_IPRT_ERROR,
6820 tr("Saved screenshot data is not available (%Rrc)"),
6821 vrc);
6822
6823 *aSize = cbData;
6824 *aWidth = u32Width;
6825 *aHeight = u32Height;
6826
6827 freeSavedDisplayScreenshot(pu8Data);
6828
6829 return S_OK;
6830}
6831
6832STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6833{
6834 LogFlowThisFunc(("\n"));
6835
6836 CheckComArgNotNull(aWidth);
6837 CheckComArgNotNull(aHeight);
6838 CheckComArgOutSafeArrayPointerValid(aData);
6839
6840 if (aScreenId != 0)
6841 return E_NOTIMPL;
6842
6843 AutoCaller autoCaller(this);
6844 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6845
6846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6847
6848 uint8_t *pu8Data = NULL;
6849 uint32_t cbData = 0;
6850 uint32_t u32Width = 0;
6851 uint32_t u32Height = 0;
6852
6853 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6854
6855 if (RT_FAILURE(vrc))
6856 return setError(VBOX_E_IPRT_ERROR,
6857 tr("Saved screenshot data is not available (%Rrc)"),
6858 vrc);
6859
6860 *aWidth = u32Width;
6861 *aHeight = u32Height;
6862
6863 com::SafeArray<BYTE> bitmap(cbData);
6864 /* Convert pixels to format expected by the API caller. */
6865 if (aBGR)
6866 {
6867 /* [0] B, [1] G, [2] R, [3] A. */
6868 for (unsigned i = 0; i < cbData; i += 4)
6869 {
6870 bitmap[i] = pu8Data[i];
6871 bitmap[i + 1] = pu8Data[i + 1];
6872 bitmap[i + 2] = pu8Data[i + 2];
6873 bitmap[i + 3] = 0xff;
6874 }
6875 }
6876 else
6877 {
6878 /* [0] R, [1] G, [2] B, [3] A. */
6879 for (unsigned i = 0; i < cbData; i += 4)
6880 {
6881 bitmap[i] = pu8Data[i + 2];
6882 bitmap[i + 1] = pu8Data[i + 1];
6883 bitmap[i + 2] = pu8Data[i];
6884 bitmap[i + 3] = 0xff;
6885 }
6886 }
6887 bitmap.detachTo(ComSafeArrayOutArg(aData));
6888
6889 freeSavedDisplayScreenshot(pu8Data);
6890
6891 return S_OK;
6892}
6893
6894
6895STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6896{
6897 LogFlowThisFunc(("\n"));
6898
6899 CheckComArgNotNull(aWidth);
6900 CheckComArgNotNull(aHeight);
6901 CheckComArgOutSafeArrayPointerValid(aData);
6902
6903 if (aScreenId != 0)
6904 return E_NOTIMPL;
6905
6906 AutoCaller autoCaller(this);
6907 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6908
6909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6910
6911 uint8_t *pu8Data = NULL;
6912 uint32_t cbData = 0;
6913 uint32_t u32Width = 0;
6914 uint32_t u32Height = 0;
6915
6916 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6917
6918 if (RT_FAILURE(vrc))
6919 return setError(VBOX_E_IPRT_ERROR,
6920 tr("Saved screenshot data is not available (%Rrc)"),
6921 vrc);
6922
6923 *aWidth = u32Width;
6924 *aHeight = u32Height;
6925
6926 HRESULT rc = S_OK;
6927 uint8_t *pu8PNG = NULL;
6928 uint32_t cbPNG = 0;
6929 uint32_t cxPNG = 0;
6930 uint32_t cyPNG = 0;
6931
6932 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6933
6934 if (RT_SUCCESS(vrc))
6935 {
6936 com::SafeArray<BYTE> screenData(cbPNG);
6937 screenData.initFrom(pu8PNG, cbPNG);
6938 if (pu8PNG)
6939 RTMemFree(pu8PNG);
6940 screenData.detachTo(ComSafeArrayOutArg(aData));
6941 }
6942 else
6943 {
6944 if (pu8PNG)
6945 RTMemFree(pu8PNG);
6946 return setError(VBOX_E_IPRT_ERROR,
6947 tr("Could not convert screenshot to PNG (%Rrc)"),
6948 vrc);
6949 }
6950
6951 freeSavedDisplayScreenshot(pu8Data);
6952
6953 return rc;
6954}
6955
6956STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6957{
6958 LogFlowThisFunc(("\n"));
6959
6960 CheckComArgNotNull(aSize);
6961 CheckComArgNotNull(aWidth);
6962 CheckComArgNotNull(aHeight);
6963
6964 if (aScreenId != 0)
6965 return E_NOTIMPL;
6966
6967 AutoCaller autoCaller(this);
6968 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6969
6970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6971
6972 uint8_t *pu8Data = NULL;
6973 uint32_t cbData = 0;
6974 uint32_t u32Width = 0;
6975 uint32_t u32Height = 0;
6976
6977 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6978
6979 if (RT_FAILURE(vrc))
6980 return setError(VBOX_E_IPRT_ERROR,
6981 tr("Saved screenshot data is not available (%Rrc)"),
6982 vrc);
6983
6984 *aSize = cbData;
6985 *aWidth = u32Width;
6986 *aHeight = u32Height;
6987
6988 freeSavedDisplayScreenshot(pu8Data);
6989
6990 return S_OK;
6991}
6992
6993STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6994{
6995 LogFlowThisFunc(("\n"));
6996
6997 CheckComArgNotNull(aWidth);
6998 CheckComArgNotNull(aHeight);
6999 CheckComArgOutSafeArrayPointerValid(aData);
7000
7001 if (aScreenId != 0)
7002 return E_NOTIMPL;
7003
7004 AutoCaller autoCaller(this);
7005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7006
7007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7008
7009 uint8_t *pu8Data = NULL;
7010 uint32_t cbData = 0;
7011 uint32_t u32Width = 0;
7012 uint32_t u32Height = 0;
7013
7014 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
7015
7016 if (RT_FAILURE(vrc))
7017 return setError(VBOX_E_IPRT_ERROR,
7018 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
7019 vrc);
7020
7021 *aWidth = u32Width;
7022 *aHeight = u32Height;
7023
7024 com::SafeArray<BYTE> png(cbData);
7025 png.initFrom(pu8Data, cbData);
7026 png.detachTo(ComSafeArrayOutArg(aData));
7027
7028 freeSavedDisplayScreenshot(pu8Data);
7029
7030 return S_OK;
7031}
7032
7033STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
7034{
7035 HRESULT rc = S_OK;
7036 LogFlowThisFunc(("\n"));
7037
7038 AutoCaller autoCaller(this);
7039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7040
7041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7042
7043 if (!mHWData->mCPUHotPlugEnabled)
7044 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7045
7046 if (aCpu >= mHWData->mCPUCount)
7047 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
7048
7049 if (mHWData->mCPUAttached[aCpu])
7050 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
7051
7052 alock.release();
7053 rc = onCPUChange(aCpu, false);
7054 alock.acquire();
7055 if (FAILED(rc)) return rc;
7056
7057 setModified(IsModified_MachineData);
7058 mHWData.backup();
7059 mHWData->mCPUAttached[aCpu] = true;
7060
7061 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7062 if (Global::IsOnline(mData->mMachineState))
7063 saveSettings(NULL);
7064
7065 return S_OK;
7066}
7067
7068STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
7069{
7070 HRESULT rc = S_OK;
7071 LogFlowThisFunc(("\n"));
7072
7073 AutoCaller autoCaller(this);
7074 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7075
7076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7077
7078 if (!mHWData->mCPUHotPlugEnabled)
7079 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7080
7081 if (aCpu >= SchemaDefs::MaxCPUCount)
7082 return setError(E_INVALIDARG,
7083 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7084 SchemaDefs::MaxCPUCount);
7085
7086 if (!mHWData->mCPUAttached[aCpu])
7087 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7088
7089 /* CPU 0 can't be detached */
7090 if (aCpu == 0)
7091 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7092
7093 alock.release();
7094 rc = onCPUChange(aCpu, true);
7095 alock.acquire();
7096 if (FAILED(rc)) return rc;
7097
7098 setModified(IsModified_MachineData);
7099 mHWData.backup();
7100 mHWData->mCPUAttached[aCpu] = false;
7101
7102 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7103 if (Global::IsOnline(mData->mMachineState))
7104 saveSettings(NULL);
7105
7106 return S_OK;
7107}
7108
7109STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7110{
7111 LogFlowThisFunc(("\n"));
7112
7113 CheckComArgNotNull(aCpuAttached);
7114
7115 *aCpuAttached = false;
7116
7117 AutoCaller autoCaller(this);
7118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7119
7120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7121
7122 /* If hotplug is enabled the CPU is always enabled. */
7123 if (!mHWData->mCPUHotPlugEnabled)
7124 {
7125 if (aCpu < mHWData->mCPUCount)
7126 *aCpuAttached = true;
7127 }
7128 else
7129 {
7130 if (aCpu < SchemaDefs::MaxCPUCount)
7131 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7132 }
7133
7134 return S_OK;
7135}
7136
7137STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7138{
7139 CheckComArgOutPointerValid(aName);
7140
7141 AutoCaller autoCaller(this);
7142 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7143
7144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7145
7146 Utf8Str log = queryLogFilename(aIdx);
7147 if (!RTFileExists(log.c_str()))
7148 log.setNull();
7149 log.cloneTo(aName);
7150
7151 return S_OK;
7152}
7153
7154STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7155{
7156 LogFlowThisFunc(("\n"));
7157 CheckComArgOutSafeArrayPointerValid(aData);
7158 if (aSize < 0)
7159 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7160
7161 AutoCaller autoCaller(this);
7162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7163
7164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7165
7166 HRESULT rc = S_OK;
7167 Utf8Str log = queryLogFilename(aIdx);
7168
7169 /* do not unnecessarily hold the lock while doing something which does
7170 * not need the lock and potentially takes a long time. */
7171 alock.release();
7172
7173 /* Limit the chunk size to 32K for now, as that gives better performance
7174 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7175 * One byte expands to approx. 25 bytes of breathtaking XML. */
7176 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7177 com::SafeArray<BYTE> logData(cbData);
7178
7179 RTFILE LogFile;
7180 int vrc = RTFileOpen(&LogFile, log.c_str(),
7181 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7182 if (RT_SUCCESS(vrc))
7183 {
7184 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7185 if (RT_SUCCESS(vrc))
7186 logData.resize(cbData);
7187 else
7188 rc = setError(VBOX_E_IPRT_ERROR,
7189 tr("Could not read log file '%s' (%Rrc)"),
7190 log.c_str(), vrc);
7191 RTFileClose(LogFile);
7192 }
7193 else
7194 rc = setError(VBOX_E_IPRT_ERROR,
7195 tr("Could not open log file '%s' (%Rrc)"),
7196 log.c_str(), vrc);
7197
7198 if (FAILED(rc))
7199 logData.resize(0);
7200 logData.detachTo(ComSafeArrayOutArg(aData));
7201
7202 return rc;
7203}
7204
7205
7206/**
7207 * Currently this method doesn't attach device to the running VM,
7208 * just makes sure it's plugged on next VM start.
7209 */
7210STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7211{
7212 AutoCaller autoCaller(this);
7213 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7214
7215 // lock scope
7216 {
7217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7218
7219 HRESULT rc = checkStateDependency(MutableStateDep);
7220 if (FAILED(rc)) return rc;
7221
7222 ChipsetType_T aChipset = ChipsetType_PIIX3;
7223 COMGETTER(ChipsetType)(&aChipset);
7224
7225 if (aChipset != ChipsetType_ICH9)
7226 {
7227 return setError(E_INVALIDARG,
7228 tr("Host PCI attachment only supported with ICH9 chipset"));
7229 }
7230
7231 // check if device with this host PCI address already attached
7232 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7233 it != mHWData->mPCIDeviceAssignments.end();
7234 ++it)
7235 {
7236 LONG iHostAddress = -1;
7237 ComPtr<PCIDeviceAttachment> pAttach;
7238 pAttach = *it;
7239 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7240 if (iHostAddress == hostAddress)
7241 return setError(E_INVALIDARG,
7242 tr("Device with host PCI address already attached to this VM"));
7243 }
7244
7245 ComObjPtr<PCIDeviceAttachment> pda;
7246 char name[32];
7247
7248 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7249 Bstr bname(name);
7250 pda.createObject();
7251 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7252 setModified(IsModified_MachineData);
7253 mHWData.backup();
7254 mHWData->mPCIDeviceAssignments.push_back(pda);
7255 }
7256
7257 return S_OK;
7258}
7259
7260/**
7261 * Currently this method doesn't detach device from the running VM,
7262 * just makes sure it's not plugged on next VM start.
7263 */
7264STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7265{
7266 AutoCaller autoCaller(this);
7267 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7268
7269 ComObjPtr<PCIDeviceAttachment> pAttach;
7270 bool fRemoved = false;
7271 HRESULT rc;
7272
7273 // lock scope
7274 {
7275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7276
7277 rc = checkStateDependency(MutableStateDep);
7278 if (FAILED(rc)) return rc;
7279
7280 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7281 it != mHWData->mPCIDeviceAssignments.end();
7282 ++it)
7283 {
7284 LONG iHostAddress = -1;
7285 pAttach = *it;
7286 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7287 if (iHostAddress != -1 && iHostAddress == hostAddress)
7288 {
7289 setModified(IsModified_MachineData);
7290 mHWData.backup();
7291 mHWData->mPCIDeviceAssignments.remove(pAttach);
7292 fRemoved = true;
7293 break;
7294 }
7295 }
7296 }
7297
7298
7299 /* Fire event outside of the lock */
7300 if (fRemoved)
7301 {
7302 Assert(!pAttach.isNull());
7303 ComPtr<IEventSource> es;
7304 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7305 Assert(SUCCEEDED(rc));
7306 Bstr mid;
7307 rc = this->COMGETTER(Id)(mid.asOutParam());
7308 Assert(SUCCEEDED(rc));
7309 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7310 }
7311
7312 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7313 tr("No host PCI device %08x attached"),
7314 hostAddress
7315 );
7316}
7317
7318STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7319{
7320 CheckComArgOutSafeArrayPointerValid(aAssignments);
7321
7322 AutoCaller autoCaller(this);
7323 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7324
7325 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7326
7327 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7328 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7329
7330 return S_OK;
7331}
7332
7333STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7334{
7335 CheckComArgOutPointerValid(aBandwidthControl);
7336
7337 AutoCaller autoCaller(this);
7338 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7339
7340 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7341
7342 return S_OK;
7343}
7344
7345STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7346{
7347 CheckComArgOutPointerValid(pfEnabled);
7348 AutoCaller autoCaller(this);
7349 HRESULT hrc = autoCaller.rc();
7350 if (SUCCEEDED(hrc))
7351 {
7352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7353 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7354 }
7355 return hrc;
7356}
7357
7358STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7359{
7360 AutoCaller autoCaller(this);
7361 HRESULT hrc = autoCaller.rc();
7362 if (SUCCEEDED(hrc))
7363 {
7364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7365 hrc = checkStateDependency(MutableStateDep);
7366 if (SUCCEEDED(hrc))
7367 {
7368 hrc = mHWData.backupEx();
7369 if (SUCCEEDED(hrc))
7370 {
7371 setModified(IsModified_MachineData);
7372 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7373 }
7374 }
7375 }
7376 return hrc;
7377}
7378
7379STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7380{
7381 CheckComArgOutPointerValid(pbstrConfig);
7382 AutoCaller autoCaller(this);
7383 HRESULT hrc = autoCaller.rc();
7384 if (SUCCEEDED(hrc))
7385 {
7386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7387 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7388 }
7389 return hrc;
7390}
7391
7392STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7393{
7394 CheckComArgStr(bstrConfig);
7395 AutoCaller autoCaller(this);
7396 HRESULT hrc = autoCaller.rc();
7397 if (SUCCEEDED(hrc))
7398 {
7399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7400 hrc = checkStateDependency(MutableStateDep);
7401 if (SUCCEEDED(hrc))
7402 {
7403 hrc = mHWData.backupEx();
7404 if (SUCCEEDED(hrc))
7405 {
7406 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7407 if (SUCCEEDED(hrc))
7408 setModified(IsModified_MachineData);
7409 }
7410 }
7411 }
7412 return hrc;
7413
7414}
7415
7416STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7417{
7418 CheckComArgOutPointerValid(pfAllow);
7419 AutoCaller autoCaller(this);
7420 HRESULT hrc = autoCaller.rc();
7421 if (SUCCEEDED(hrc))
7422 {
7423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7424 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7425 }
7426 return hrc;
7427}
7428
7429STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7430{
7431 AutoCaller autoCaller(this);
7432 HRESULT hrc = autoCaller.rc();
7433 if (SUCCEEDED(hrc))
7434 {
7435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7436 hrc = checkStateDependency(MutableStateDep);
7437 if (SUCCEEDED(hrc))
7438 {
7439 hrc = mHWData.backupEx();
7440 if (SUCCEEDED(hrc))
7441 {
7442 setModified(IsModified_MachineData);
7443 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7444 }
7445 }
7446 }
7447 return hrc;
7448}
7449
7450STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7451{
7452 CheckComArgOutPointerValid(pfEnabled);
7453 AutoCaller autoCaller(this);
7454 HRESULT hrc = autoCaller.rc();
7455 if (SUCCEEDED(hrc))
7456 {
7457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7458 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7459 }
7460 return hrc;
7461}
7462
7463STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7464{
7465 AutoCaller autoCaller(this);
7466 HRESULT hrc = autoCaller.rc();
7467 if (SUCCEEDED(hrc))
7468 {
7469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7470 hrc = checkStateDependency(MutableStateDep);
7471 if ( SUCCEEDED(hrc)
7472 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7473 {
7474 AutostartDb *autostartDb = mParent->getAutostartDb();
7475 int vrc;
7476
7477 if (fEnabled)
7478 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7479 else
7480 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7481
7482 if (RT_SUCCESS(vrc))
7483 {
7484 hrc = mHWData.backupEx();
7485 if (SUCCEEDED(hrc))
7486 {
7487 setModified(IsModified_MachineData);
7488 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7489 }
7490 }
7491 else if (vrc == VERR_NOT_SUPPORTED)
7492 hrc = setError(VBOX_E_NOT_SUPPORTED,
7493 tr("The VM autostart feature is not supported on this platform"));
7494 else if (vrc == VERR_PATH_NOT_FOUND)
7495 hrc = setError(E_FAIL,
7496 tr("The path to the autostart database is not set"));
7497 else
7498 hrc = setError(E_UNEXPECTED,
7499 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7500 fEnabled ? "Adding" : "Removing",
7501 mUserData->s.strName.c_str(), vrc);
7502 }
7503 }
7504 return hrc;
7505}
7506
7507STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7508{
7509 CheckComArgOutPointerValid(puDelay);
7510 AutoCaller autoCaller(this);
7511 HRESULT hrc = autoCaller.rc();
7512 if (SUCCEEDED(hrc))
7513 {
7514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7515 *puDelay = mHWData->mAutostart.uAutostartDelay;
7516 }
7517 return hrc;
7518}
7519
7520STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7521{
7522 AutoCaller autoCaller(this);
7523 HRESULT hrc = autoCaller.rc();
7524 if (SUCCEEDED(hrc))
7525 {
7526 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7527 hrc = checkStateDependency(MutableStateDep);
7528 if (SUCCEEDED(hrc))
7529 {
7530 hrc = mHWData.backupEx();
7531 if (SUCCEEDED(hrc))
7532 {
7533 setModified(IsModified_MachineData);
7534 mHWData->mAutostart.uAutostartDelay = uDelay;
7535 }
7536 }
7537 }
7538 return hrc;
7539}
7540
7541STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7542{
7543 CheckComArgOutPointerValid(penmAutostopType);
7544 AutoCaller autoCaller(this);
7545 HRESULT hrc = autoCaller.rc();
7546 if (SUCCEEDED(hrc))
7547 {
7548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7549 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7550 }
7551 return hrc;
7552}
7553
7554STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7555{
7556 AutoCaller autoCaller(this);
7557 HRESULT hrc = autoCaller.rc();
7558 if (SUCCEEDED(hrc))
7559 {
7560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7561 hrc = checkStateDependency(MutableStateDep);
7562 if ( SUCCEEDED(hrc)
7563 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7564 {
7565 AutostartDb *autostartDb = mParent->getAutostartDb();
7566 int vrc;
7567
7568 if (enmAutostopType != AutostopType_Disabled)
7569 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7570 else
7571 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7572
7573 if (RT_SUCCESS(vrc))
7574 {
7575 hrc = mHWData.backupEx();
7576 if (SUCCEEDED(hrc))
7577 {
7578 setModified(IsModified_MachineData);
7579 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7580 }
7581 }
7582 else if (vrc == VERR_NOT_SUPPORTED)
7583 hrc = setError(VBOX_E_NOT_SUPPORTED,
7584 tr("The VM autostop feature is not supported on this platform"));
7585 else if (vrc == VERR_PATH_NOT_FOUND)
7586 hrc = setError(E_FAIL,
7587 tr("The path to the autostart database is not set"));
7588 else
7589 hrc = setError(E_UNEXPECTED,
7590 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7591 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7592 mUserData->s.strName.c_str(), vrc);
7593 }
7594 }
7595 return hrc;
7596}
7597
7598STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7599{
7600 CheckComArgOutPointerValid(aDefaultFrontend);
7601 AutoCaller autoCaller(this);
7602 HRESULT hrc = autoCaller.rc();
7603 if (SUCCEEDED(hrc))
7604 {
7605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7606 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7607 }
7608 return hrc;
7609}
7610
7611STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7612{
7613 CheckComArgStr(aDefaultFrontend);
7614 AutoCaller autoCaller(this);
7615 HRESULT hrc = autoCaller.rc();
7616 if (SUCCEEDED(hrc))
7617 {
7618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7619 hrc = checkStateDependency(MutableOrSavedStateDep);
7620 if (SUCCEEDED(hrc))
7621 {
7622 hrc = mHWData.backupEx();
7623 if (SUCCEEDED(hrc))
7624 {
7625 setModified(IsModified_MachineData);
7626 mHWData->mDefaultFrontend = aDefaultFrontend;
7627 }
7628 }
7629 }
7630 return hrc;
7631}
7632
7633STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7634{
7635 CheckComArgSafeArrayNotNull(aIcon);
7636 CheckComArgOutSafeArrayPointerValid(aIcon);
7637 AutoCaller autoCaller(this);
7638 HRESULT hrc = autoCaller.rc();
7639 if (SUCCEEDED(hrc))
7640 {
7641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7642 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7643 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7644 icon.detachTo(ComSafeArrayOutArg(aIcon));
7645 }
7646 return hrc;
7647}
7648
7649STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7650{
7651 CheckComArgSafeArrayNotNull(aIcon);
7652 AutoCaller autoCaller(this);
7653 HRESULT hrc = autoCaller.rc();
7654 if (SUCCEEDED(hrc))
7655 {
7656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7657 hrc = checkStateDependency(MutableOrSavedStateDep);
7658 if (SUCCEEDED(hrc))
7659 {
7660 setModified(IsModified_MachineData);
7661 mUserData.backup();
7662 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7663 mUserData->mIcon.resize(icon.size());
7664 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7665 }
7666 }
7667 return hrc;
7668}
7669
7670STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7671{
7672 CheckComArgOutPointerValid(aAvailable);
7673
7674 AutoCaller autoCaller(this);
7675 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7676
7677#ifdef VBOX_WITH_USB
7678 *aAvailable = true;
7679#else
7680 *aAvailable = false;
7681#endif
7682 return S_OK;
7683}
7684
7685STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7686{
7687 LogFlowFuncEnter();
7688
7689 CheckComArgNotNull(pTarget);
7690 CheckComArgOutPointerValid(pProgress);
7691
7692 /* Convert the options. */
7693 RTCList<CloneOptions_T> optList;
7694 if (options != NULL)
7695 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7696
7697 if (optList.contains(CloneOptions_Link))
7698 {
7699 if (!isSnapshotMachine())
7700 return setError(E_INVALIDARG,
7701 tr("Linked clone can only be created from a snapshot"));
7702 if (mode != CloneMode_MachineState)
7703 return setError(E_INVALIDARG,
7704 tr("Linked clone can only be created for a single machine state"));
7705 }
7706 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7707
7708 AutoCaller autoCaller(this);
7709 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7710
7711
7712 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7713
7714 HRESULT rc = pWorker->start(pProgress);
7715
7716 LogFlowFuncLeave();
7717
7718 return rc;
7719}
7720
7721// public methods for internal purposes
7722/////////////////////////////////////////////////////////////////////////////
7723
7724/**
7725 * Adds the given IsModified_* flag to the dirty flags of the machine.
7726 * This must be called either during loadSettings or under the machine write lock.
7727 * @param fl
7728 */
7729void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7730{
7731 mData->flModifications |= fl;
7732 if (fAllowStateModification && isStateModificationAllowed())
7733 mData->mCurrentStateModified = true;
7734}
7735
7736/**
7737 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7738 * care of the write locking.
7739 *
7740 * @param fModifications The flag to add.
7741 */
7742void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7743{
7744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7745 setModified(fModification, fAllowStateModification);
7746}
7747
7748/**
7749 * Saves the registry entry of this machine to the given configuration node.
7750 *
7751 * @param aEntryNode Node to save the registry entry to.
7752 *
7753 * @note locks this object for reading.
7754 */
7755HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7756{
7757 AutoLimitedCaller autoCaller(this);
7758 AssertComRCReturnRC(autoCaller.rc());
7759
7760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7761
7762 data.uuid = mData->mUuid;
7763 data.strSettingsFile = mData->m_strConfigFile;
7764
7765 return S_OK;
7766}
7767
7768/**
7769 * Calculates the absolute path of the given path taking the directory of the
7770 * machine settings file as the current directory.
7771 *
7772 * @param aPath Path to calculate the absolute path for.
7773 * @param aResult Where to put the result (used only on success, can be the
7774 * same Utf8Str instance as passed in @a aPath).
7775 * @return IPRT result.
7776 *
7777 * @note Locks this object for reading.
7778 */
7779int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7780{
7781 AutoCaller autoCaller(this);
7782 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7783
7784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7785
7786 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7787
7788 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7789
7790 strSettingsDir.stripFilename();
7791 char folder[RTPATH_MAX];
7792 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7793 if (RT_SUCCESS(vrc))
7794 aResult = folder;
7795
7796 return vrc;
7797}
7798
7799/**
7800 * Copies strSource to strTarget, making it relative to the machine folder
7801 * if it is a subdirectory thereof, or simply copying it otherwise.
7802 *
7803 * @param strSource Path to evaluate and copy.
7804 * @param strTarget Buffer to receive target path.
7805 *
7806 * @note Locks this object for reading.
7807 */
7808void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7809 Utf8Str &strTarget)
7810{
7811 AutoCaller autoCaller(this);
7812 AssertComRCReturn(autoCaller.rc(), (void)0);
7813
7814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7815
7816 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7817 // use strTarget as a temporary buffer to hold the machine settings dir
7818 strTarget = mData->m_strConfigFileFull;
7819 strTarget.stripFilename();
7820 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7821 {
7822 // is relative: then append what's left
7823 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7824 // for empty paths (only possible for subdirs) use "." to avoid
7825 // triggering default settings for not present config attributes.
7826 if (strTarget.isEmpty())
7827 strTarget = ".";
7828 }
7829 else
7830 // is not relative: then overwrite
7831 strTarget = strSource;
7832}
7833
7834/**
7835 * Returns the full path to the machine's log folder in the
7836 * \a aLogFolder argument.
7837 */
7838void Machine::getLogFolder(Utf8Str &aLogFolder)
7839{
7840 AutoCaller autoCaller(this);
7841 AssertComRCReturnVoid(autoCaller.rc());
7842
7843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7844
7845 char szTmp[RTPATH_MAX];
7846 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7847 if (RT_SUCCESS(vrc))
7848 {
7849 if (szTmp[0] && !mUserData.isNull())
7850 {
7851 char szTmp2[RTPATH_MAX];
7852 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7853 if (RT_SUCCESS(vrc))
7854 aLogFolder = BstrFmt("%s%c%s",
7855 szTmp2,
7856 RTPATH_DELIMITER,
7857 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7858 }
7859 else
7860 vrc = VERR_PATH_IS_RELATIVE;
7861 }
7862
7863 if (RT_FAILURE(vrc))
7864 {
7865 // fallback if VBOX_USER_LOGHOME is not set or invalid
7866 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7867 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7868 aLogFolder.append(RTPATH_DELIMITER);
7869 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7870 }
7871}
7872
7873/**
7874 * Returns the full path to the machine's log file for an given index.
7875 */
7876Utf8Str Machine::queryLogFilename(ULONG idx)
7877{
7878 Utf8Str logFolder;
7879 getLogFolder(logFolder);
7880 Assert(logFolder.length());
7881 Utf8Str log;
7882 if (idx == 0)
7883 log = Utf8StrFmt("%s%cVBox.log",
7884 logFolder.c_str(), RTPATH_DELIMITER);
7885 else
7886 log = Utf8StrFmt("%s%cVBox.log.%d",
7887 logFolder.c_str(), RTPATH_DELIMITER, idx);
7888 return log;
7889}
7890
7891/**
7892 * Composes a unique saved state filename based on the current system time. The filename is
7893 * granular to the second so this will work so long as no more than one snapshot is taken on
7894 * a machine per second.
7895 *
7896 * Before version 4.1, we used this formula for saved state files:
7897 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7898 * which no longer works because saved state files can now be shared between the saved state of the
7899 * "saved" machine and an online snapshot, and the following would cause problems:
7900 * 1) save machine
7901 * 2) create online snapshot from that machine state --> reusing saved state file
7902 * 3) save machine again --> filename would be reused, breaking the online snapshot
7903 *
7904 * So instead we now use a timestamp.
7905 *
7906 * @param str
7907 */
7908void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7909{
7910 AutoCaller autoCaller(this);
7911 AssertComRCReturnVoid(autoCaller.rc());
7912
7913 {
7914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7915 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7916 }
7917
7918 RTTIMESPEC ts;
7919 RTTimeNow(&ts);
7920 RTTIME time;
7921 RTTimeExplode(&time, &ts);
7922
7923 strStateFilePath += RTPATH_DELIMITER;
7924 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7925 time.i32Year, time.u8Month, time.u8MonthDay,
7926 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7927}
7928
7929/**
7930 * Returns the full path to the default video capture file.
7931 */
7932void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7933{
7934 AutoCaller autoCaller(this);
7935 AssertComRCReturnVoid(autoCaller.rc());
7936
7937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7938
7939 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7940 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7941 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7942}
7943
7944/**
7945 * Returns whether at least one USB controller is present for the VM.
7946 */
7947bool Machine::isUSBControllerPresent()
7948{
7949 AutoCaller autoCaller(this);
7950 AssertComRCReturn(autoCaller.rc(), false);
7951
7952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7953
7954 return (mUSBControllers->size() > 0);
7955}
7956
7957/**
7958 * @note Locks this object for writing, calls the client process
7959 * (inside the lock).
7960 */
7961HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7962 const Utf8Str &strFrontend,
7963 const Utf8Str &strEnvironment,
7964 ProgressProxy *aProgress)
7965{
7966 LogFlowThisFuncEnter();
7967
7968 AssertReturn(aControl, E_FAIL);
7969 AssertReturn(aProgress, E_FAIL);
7970 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7971
7972 AutoCaller autoCaller(this);
7973 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7974
7975 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7976
7977 if (!mData->mRegistered)
7978 return setError(E_UNEXPECTED,
7979 tr("The machine '%s' is not registered"),
7980 mUserData->s.strName.c_str());
7981
7982 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7983
7984 if ( mData->mSession.mState == SessionState_Locked
7985 || mData->mSession.mState == SessionState_Spawning
7986 || mData->mSession.mState == SessionState_Unlocking)
7987 return setError(VBOX_E_INVALID_OBJECT_STATE,
7988 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7989 mUserData->s.strName.c_str());
7990
7991 /* may not be busy */
7992 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7993
7994 /* get the path to the executable */
7995 char szPath[RTPATH_MAX];
7996 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7997 size_t sz = strlen(szPath);
7998 szPath[sz++] = RTPATH_DELIMITER;
7999 szPath[sz] = 0;
8000 char *cmd = szPath + sz;
8001 sz = sizeof(szPath) - sz;
8002
8003 int vrc = VINF_SUCCESS;
8004 RTPROCESS pid = NIL_RTPROCESS;
8005
8006 RTENV env = RTENV_DEFAULT;
8007
8008 if (!strEnvironment.isEmpty())
8009 {
8010 char *newEnvStr = NULL;
8011
8012 do
8013 {
8014 /* clone the current environment */
8015 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
8016 AssertRCBreakStmt(vrc2, vrc = vrc2);
8017
8018 newEnvStr = RTStrDup(strEnvironment.c_str());
8019 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
8020
8021 /* put new variables to the environment
8022 * (ignore empty variable names here since RTEnv API
8023 * intentionally doesn't do that) */
8024 char *var = newEnvStr;
8025 for (char *p = newEnvStr; *p; ++p)
8026 {
8027 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
8028 {
8029 *p = '\0';
8030 if (*var)
8031 {
8032 char *val = strchr(var, '=');
8033 if (val)
8034 {
8035 *val++ = '\0';
8036 vrc2 = RTEnvSetEx(env, var, val);
8037 }
8038 else
8039 vrc2 = RTEnvUnsetEx(env, var);
8040 if (RT_FAILURE(vrc2))
8041 break;
8042 }
8043 var = p + 1;
8044 }
8045 }
8046 if (RT_SUCCESS(vrc2) && *var)
8047 vrc2 = RTEnvPutEx(env, var);
8048
8049 AssertRCBreakStmt(vrc2, vrc = vrc2);
8050 }
8051 while (0);
8052
8053 if (newEnvStr != NULL)
8054 RTStrFree(newEnvStr);
8055 }
8056
8057#ifdef VBOX_WITH_QTGUI
8058 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
8059 {
8060# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
8061 /* Modify the base path so that we don't need to use ".." below. */
8062 RTPathStripTrailingSlash(szPath);
8063 RTPathStripFilename(szPath);
8064 sz = strlen(szPath);
8065 cmd = szPath + sz;
8066 sz = sizeof(szPath) - sz;
8067
8068#define OSX_APP_NAME "VirtualBoxVM"
8069#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
8070
8071 Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
8072 if ( strAppOverride.contains(".")
8073 || strAppOverride.contains("/")
8074 || strAppOverride.contains("\\")
8075 || strAppOverride.contains(":"))
8076 strAppOverride.setNull();
8077 Utf8Str strAppPath;
8078 if (!strAppOverride.isEmpty())
8079 {
8080 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
8081 Utf8Str strFullPath(szPath);
8082 strFullPath.append(strAppPath);
8083 /* there is a race, but people using this deserve the failure */
8084 if (!RTFileExists(strFullPath.c_str()))
8085 strAppOverride.setNull();
8086 }
8087 if (strAppOverride.isEmpty())
8088 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
8089 const char *VirtualBox_exe = strAppPath.c_str();
8090 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
8091# else
8092 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8093 Assert(sz >= sizeof(VirtualBox_exe));
8094# endif
8095 strcpy(cmd, VirtualBox_exe);
8096
8097 Utf8Str idStr = mData->mUuid.toString();
8098 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8099 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8100 }
8101#else /* !VBOX_WITH_QTGUI */
8102 if (0)
8103 ;
8104#endif /* VBOX_WITH_QTGUI */
8105
8106 else
8107
8108#ifdef VBOX_WITH_VBOXSDL
8109 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8110 {
8111 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8112 Assert(sz >= sizeof(VBoxSDL_exe));
8113 strcpy(cmd, VBoxSDL_exe);
8114
8115 Utf8Str idStr = mData->mUuid.toString();
8116 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8117 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8118 }
8119#else /* !VBOX_WITH_VBOXSDL */
8120 if (0)
8121 ;
8122#endif /* !VBOX_WITH_VBOXSDL */
8123
8124 else
8125
8126#ifdef VBOX_WITH_HEADLESS
8127 if ( strFrontend == "headless"
8128 || strFrontend == "capture"
8129 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8130 )
8131 {
8132 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8133 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8134 * and a VM works even if the server has not been installed.
8135 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8136 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8137 * differently in 4.0 and 3.x.
8138 */
8139 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8140 Assert(sz >= sizeof(VBoxHeadless_exe));
8141 strcpy(cmd, VBoxHeadless_exe);
8142
8143 Utf8Str idStr = mData->mUuid.toString();
8144 /* Leave space for "--capture" arg. */
8145 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8146 "--startvm", idStr.c_str(),
8147 "--vrde", "config",
8148 0, /* For "--capture". */
8149 0 };
8150 if (strFrontend == "capture")
8151 {
8152 unsigned pos = RT_ELEMENTS(args) - 2;
8153 args[pos] = "--capture";
8154 }
8155 vrc = RTProcCreate(szPath, args, env,
8156#ifdef RT_OS_WINDOWS
8157 RTPROC_FLAGS_NO_WINDOW
8158#else
8159 0
8160#endif
8161 , &pid);
8162 }
8163#else /* !VBOX_WITH_HEADLESS */
8164 if (0)
8165 ;
8166#endif /* !VBOX_WITH_HEADLESS */
8167 else
8168 {
8169 RTEnvDestroy(env);
8170 return setError(E_INVALIDARG,
8171 tr("Invalid frontend name: '%s'"),
8172 strFrontend.c_str());
8173 }
8174
8175 RTEnvDestroy(env);
8176
8177 if (RT_FAILURE(vrc))
8178 return setError(VBOX_E_IPRT_ERROR,
8179 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8180 mUserData->s.strName.c_str(), vrc);
8181
8182 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8183
8184 /*
8185 * Note that we don't release the lock here before calling the client,
8186 * because it doesn't need to call us back if called with a NULL argument.
8187 * Releasing the lock here is dangerous because we didn't prepare the
8188 * launch data yet, but the client we've just started may happen to be
8189 * too fast and call LockMachine() that will fail (because of PID, etc.),
8190 * so that the Machine will never get out of the Spawning session state.
8191 */
8192
8193 /* inform the session that it will be a remote one */
8194 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8195#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8196 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8197#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8198 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8199#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8200 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8201
8202 if (FAILED(rc))
8203 {
8204 /* restore the session state */
8205 mData->mSession.mState = SessionState_Unlocked;
8206 /* The failure may occur w/o any error info (from RPC), so provide one */
8207 return setError(VBOX_E_VM_ERROR,
8208 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8209 }
8210
8211 /* attach launch data to the machine */
8212 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8213 mData->mSession.mRemoteControls.push_back(aControl);
8214 mData->mSession.mProgress = aProgress;
8215 mData->mSession.mPID = pid;
8216 mData->mSession.mState = SessionState_Spawning;
8217 mData->mSession.mType = strFrontend;
8218
8219 LogFlowThisFuncLeave();
8220 return S_OK;
8221}
8222
8223/**
8224 * Returns @c true if the given session machine instance has an open direct
8225 * session (and optionally also for direct sessions which are closing) and
8226 * returns the session control machine instance if so.
8227 *
8228 * Note that when the method returns @c false, the arguments remain unchanged.
8229 *
8230 * @param aMachine Session machine object.
8231 * @param aControl Direct session control object (optional).
8232 * @param aAllowClosing If true then additionally a session which is currently
8233 * being closed will also be allowed.
8234 *
8235 * @note locks this object for reading.
8236 */
8237bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8238 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8239 bool aAllowClosing /*= false*/)
8240{
8241 AutoLimitedCaller autoCaller(this);
8242 AssertComRCReturn(autoCaller.rc(), false);
8243
8244 /* just return false for inaccessible machines */
8245 if (autoCaller.state() != Ready)
8246 return false;
8247
8248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8249
8250 if ( mData->mSession.mState == SessionState_Locked
8251 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8252 )
8253 {
8254 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8255
8256 aMachine = mData->mSession.mMachine;
8257
8258 if (aControl != NULL)
8259 *aControl = mData->mSession.mDirectControl;
8260
8261 return true;
8262 }
8263
8264 return false;
8265}
8266
8267/**
8268 * Returns @c true if the given machine has an spawning direct session.
8269 *
8270 * @note locks this object for reading.
8271 */
8272bool Machine::isSessionSpawning()
8273{
8274 AutoLimitedCaller autoCaller(this);
8275 AssertComRCReturn(autoCaller.rc(), false);
8276
8277 /* just return false for inaccessible machines */
8278 if (autoCaller.state() != Ready)
8279 return false;
8280
8281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8282
8283 if (mData->mSession.mState == SessionState_Spawning)
8284 return true;
8285
8286 return false;
8287}
8288
8289#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8290/**
8291 * Called from the client watcher thread to check for unexpected client process
8292 * death during Session_Spawning state (e.g. before it successfully opened a
8293 * direct session).
8294 *
8295 * On Win32 and on OS/2, this method is called only when we've got the
8296 * direct client's process termination notification, so it always returns @c
8297 * true.
8298 *
8299 * On other platforms, this method returns @c true if the client process is
8300 * terminated and @c false if it's still alive.
8301 *
8302 * @note Locks this object for writing.
8303 */
8304bool Machine::checkForSpawnFailure()
8305{
8306 AutoCaller autoCaller(this);
8307 if (!autoCaller.isOk())
8308 {
8309 /* nothing to do */
8310 LogFlowThisFunc(("Already uninitialized!\n"));
8311 return true;
8312 }
8313
8314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8315
8316 if (mData->mSession.mState != SessionState_Spawning)
8317 {
8318 /* nothing to do */
8319 LogFlowThisFunc(("Not spawning any more!\n"));
8320 return true;
8321 }
8322
8323 HRESULT rc = S_OK;
8324
8325 /* PID not yet initialized, skip check. */
8326 if (mData->mSession.mPID == NIL_RTPROCESS)
8327 return false;
8328
8329 RTPROCSTATUS status;
8330 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8331
8332 if (vrc != VERR_PROCESS_RUNNING)
8333 {
8334 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8335 rc = setError(E_FAIL,
8336 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8337 getName().c_str(), status.iStatus);
8338 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8339 rc = setError(E_FAIL,
8340 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8341 getName().c_str(), status.iStatus);
8342 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8343 rc = setError(E_FAIL,
8344 tr("The virtual machine '%s' has terminated abnormally"),
8345 getName().c_str(), status.iStatus);
8346 else
8347 rc = setError(E_FAIL,
8348 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8349 getName().c_str(), vrc);
8350 }
8351
8352 if (FAILED(rc))
8353 {
8354 /* Close the remote session, remove the remote control from the list
8355 * and reset session state to Closed (@note keep the code in sync with
8356 * the relevant part in LockMachine()). */
8357
8358 Assert(mData->mSession.mRemoteControls.size() == 1);
8359 if (mData->mSession.mRemoteControls.size() == 1)
8360 {
8361 ErrorInfoKeeper eik;
8362 mData->mSession.mRemoteControls.front()->Uninitialize();
8363 }
8364
8365 mData->mSession.mRemoteControls.clear();
8366 mData->mSession.mState = SessionState_Unlocked;
8367
8368 /* finalize the progress after setting the state */
8369 if (!mData->mSession.mProgress.isNull())
8370 {
8371 mData->mSession.mProgress->notifyComplete(rc);
8372 mData->mSession.mProgress.setNull();
8373 }
8374
8375 mData->mSession.mPID = NIL_RTPROCESS;
8376
8377 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8378 return true;
8379 }
8380
8381 return false;
8382}
8383#endif /* !VBOX_WITH_GENERIC_SESSION_WATCHER */
8384
8385/**
8386 * Checks whether the machine can be registered. If so, commits and saves
8387 * all settings.
8388 *
8389 * @note Must be called from mParent's write lock. Locks this object and
8390 * children for writing.
8391 */
8392HRESULT Machine::prepareRegister()
8393{
8394 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8395
8396 AutoLimitedCaller autoCaller(this);
8397 AssertComRCReturnRC(autoCaller.rc());
8398
8399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8400
8401 /* wait for state dependents to drop to zero */
8402 ensureNoStateDependencies();
8403
8404 if (!mData->mAccessible)
8405 return setError(VBOX_E_INVALID_OBJECT_STATE,
8406 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8407 mUserData->s.strName.c_str(),
8408 mData->mUuid.toString().c_str());
8409
8410 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8411
8412 if (mData->mRegistered)
8413 return setError(VBOX_E_INVALID_OBJECT_STATE,
8414 tr("The machine '%s' with UUID {%s} is already registered"),
8415 mUserData->s.strName.c_str(),
8416 mData->mUuid.toString().c_str());
8417
8418 HRESULT rc = S_OK;
8419
8420 // Ensure the settings are saved. If we are going to be registered and
8421 // no config file exists yet, create it by calling saveSettings() too.
8422 if ( (mData->flModifications)
8423 || (!mData->pMachineConfigFile->fileExists())
8424 )
8425 {
8426 rc = saveSettings(NULL);
8427 // no need to check whether VirtualBox.xml needs saving too since
8428 // we can't have a machine XML file rename pending
8429 if (FAILED(rc)) return rc;
8430 }
8431
8432 /* more config checking goes here */
8433
8434 if (SUCCEEDED(rc))
8435 {
8436 /* we may have had implicit modifications we want to fix on success */
8437 commit();
8438
8439 mData->mRegistered = true;
8440 }
8441 else
8442 {
8443 /* we may have had implicit modifications we want to cancel on failure*/
8444 rollback(false /* aNotify */);
8445 }
8446
8447 return rc;
8448}
8449
8450/**
8451 * Increases the number of objects dependent on the machine state or on the
8452 * registered state. Guarantees that these two states will not change at least
8453 * until #releaseStateDependency() is called.
8454 *
8455 * Depending on the @a aDepType value, additional state checks may be made.
8456 * These checks will set extended error info on failure. See
8457 * #checkStateDependency() for more info.
8458 *
8459 * If this method returns a failure, the dependency is not added and the caller
8460 * is not allowed to rely on any particular machine state or registration state
8461 * value and may return the failed result code to the upper level.
8462 *
8463 * @param aDepType Dependency type to add.
8464 * @param aState Current machine state (NULL if not interested).
8465 * @param aRegistered Current registered state (NULL if not interested).
8466 *
8467 * @note Locks this object for writing.
8468 */
8469HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8470 MachineState_T *aState /* = NULL */,
8471 BOOL *aRegistered /* = NULL */)
8472{
8473 AutoCaller autoCaller(this);
8474 AssertComRCReturnRC(autoCaller.rc());
8475
8476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8477
8478 HRESULT rc = checkStateDependency(aDepType);
8479 if (FAILED(rc)) return rc;
8480
8481 {
8482 if (mData->mMachineStateChangePending != 0)
8483 {
8484 /* ensureNoStateDependencies() is waiting for state dependencies to
8485 * drop to zero so don't add more. It may make sense to wait a bit
8486 * and retry before reporting an error (since the pending state
8487 * transition should be really quick) but let's just assert for
8488 * now to see if it ever happens on practice. */
8489
8490 AssertFailed();
8491
8492 return setError(E_ACCESSDENIED,
8493 tr("Machine state change is in progress. Please retry the operation later."));
8494 }
8495
8496 ++mData->mMachineStateDeps;
8497 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8498 }
8499
8500 if (aState)
8501 *aState = mData->mMachineState;
8502 if (aRegistered)
8503 *aRegistered = mData->mRegistered;
8504
8505 return S_OK;
8506}
8507
8508/**
8509 * Decreases the number of objects dependent on the machine state.
8510 * Must always complete the #addStateDependency() call after the state
8511 * dependency is no more necessary.
8512 */
8513void Machine::releaseStateDependency()
8514{
8515 AutoCaller autoCaller(this);
8516 AssertComRCReturnVoid(autoCaller.rc());
8517
8518 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8519
8520 /* releaseStateDependency() w/o addStateDependency()? */
8521 AssertReturnVoid(mData->mMachineStateDeps != 0);
8522 -- mData->mMachineStateDeps;
8523
8524 if (mData->mMachineStateDeps == 0)
8525 {
8526 /* inform ensureNoStateDependencies() that there are no more deps */
8527 if (mData->mMachineStateChangePending != 0)
8528 {
8529 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8530 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8531 }
8532 }
8533}
8534
8535Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8536{
8537 /* start with nothing found */
8538 Utf8Str strResult("");
8539
8540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8541
8542 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8543 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8544 // found:
8545 strResult = it->second; // source is a Utf8Str
8546
8547 return strResult;
8548}
8549
8550// protected methods
8551/////////////////////////////////////////////////////////////////////////////
8552
8553/**
8554 * Performs machine state checks based on the @a aDepType value. If a check
8555 * fails, this method will set extended error info, otherwise it will return
8556 * S_OK. It is supposed, that on failure, the caller will immediately return
8557 * the return value of this method to the upper level.
8558 *
8559 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8560 *
8561 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8562 * current state of this machine object allows to change settings of the
8563 * machine (i.e. the machine is not registered, or registered but not running
8564 * and not saved). It is useful to call this method from Machine setters
8565 * before performing any change.
8566 *
8567 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8568 * as for MutableStateDep except that if the machine is saved, S_OK is also
8569 * returned. This is useful in setters which allow changing machine
8570 * properties when it is in the saved state.
8571 *
8572 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8573 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8574 * Aborted).
8575 *
8576 * @param aDepType Dependency type to check.
8577 *
8578 * @note Non Machine based classes should use #addStateDependency() and
8579 * #releaseStateDependency() methods or the smart AutoStateDependency
8580 * template.
8581 *
8582 * @note This method must be called from under this object's read or write
8583 * lock.
8584 */
8585HRESULT Machine::checkStateDependency(StateDependency aDepType)
8586{
8587 switch (aDepType)
8588 {
8589 case AnyStateDep:
8590 {
8591 break;
8592 }
8593 case MutableStateDep:
8594 {
8595 if ( mData->mRegistered
8596 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8597 || ( mData->mMachineState != MachineState_Paused
8598 && mData->mMachineState != MachineState_Running
8599 && mData->mMachineState != MachineState_Aborted
8600 && mData->mMachineState != MachineState_Teleported
8601 && mData->mMachineState != MachineState_PoweredOff
8602 )
8603 )
8604 )
8605 return setError(VBOX_E_INVALID_VM_STATE,
8606 tr("The machine is not mutable (state is %s)"),
8607 Global::stringifyMachineState(mData->mMachineState));
8608 break;
8609 }
8610 case MutableOrSavedStateDep:
8611 {
8612 if ( mData->mRegistered
8613 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8614 || ( mData->mMachineState != MachineState_Paused
8615 && mData->mMachineState != MachineState_Running
8616 && mData->mMachineState != MachineState_Aborted
8617 && mData->mMachineState != MachineState_Teleported
8618 && mData->mMachineState != MachineState_Saved
8619 && mData->mMachineState != MachineState_PoweredOff
8620 )
8621 )
8622 )
8623 return setError(VBOX_E_INVALID_VM_STATE,
8624 tr("The machine is not mutable (state is %s)"),
8625 Global::stringifyMachineState(mData->mMachineState));
8626 break;
8627 }
8628 case OfflineStateDep:
8629 {
8630 if ( mData->mRegistered
8631 && ( !isSessionMachine()
8632 || ( mData->mMachineState != MachineState_PoweredOff
8633 && mData->mMachineState != MachineState_Saved
8634 && mData->mMachineState != MachineState_Aborted
8635 && mData->mMachineState != MachineState_Teleported
8636 )
8637 )
8638 )
8639 return setError(VBOX_E_INVALID_VM_STATE,
8640 tr("The machine is not offline (state is %s)"),
8641 Global::stringifyMachineState(mData->mMachineState));
8642 break;
8643 }
8644 }
8645
8646 return S_OK;
8647}
8648
8649/**
8650 * Helper to initialize all associated child objects and allocate data
8651 * structures.
8652 *
8653 * This method must be called as a part of the object's initialization procedure
8654 * (usually done in the #init() method).
8655 *
8656 * @note Must be called only from #init() or from #registeredInit().
8657 */
8658HRESULT Machine::initDataAndChildObjects()
8659{
8660 AutoCaller autoCaller(this);
8661 AssertComRCReturnRC(autoCaller.rc());
8662 AssertComRCReturn(autoCaller.state() == InInit ||
8663 autoCaller.state() == Limited, E_FAIL);
8664
8665 AssertReturn(!mData->mAccessible, E_FAIL);
8666
8667 /* allocate data structures */
8668 mSSData.allocate();
8669 mUserData.allocate();
8670 mHWData.allocate();
8671 mMediaData.allocate();
8672 mStorageControllers.allocate();
8673 mUSBControllers.allocate();
8674
8675 /* initialize mOSTypeId */
8676 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8677
8678 /* create associated BIOS settings object */
8679 unconst(mBIOSSettings).createObject();
8680 mBIOSSettings->init(this);
8681
8682 /* create an associated VRDE object (default is disabled) */
8683 unconst(mVRDEServer).createObject();
8684 mVRDEServer->init(this);
8685
8686 /* create associated serial port objects */
8687 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8688 {
8689 unconst(mSerialPorts[slot]).createObject();
8690 mSerialPorts[slot]->init(this, slot);
8691 }
8692
8693 /* create associated parallel port objects */
8694 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8695 {
8696 unconst(mParallelPorts[slot]).createObject();
8697 mParallelPorts[slot]->init(this, slot);
8698 }
8699
8700 /* create the audio adapter object (always present, default is disabled) */
8701 unconst(mAudioAdapter).createObject();
8702 mAudioAdapter->init(this);
8703
8704 /* create the USB device filters object (always present) */
8705 unconst(mUSBDeviceFilters).createObject();
8706 mUSBDeviceFilters->init(this);
8707
8708 /* create associated network adapter objects */
8709 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8710 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8711 {
8712 unconst(mNetworkAdapters[slot]).createObject();
8713 mNetworkAdapters[slot]->init(this, slot);
8714 }
8715
8716 /* create the bandwidth control */
8717 unconst(mBandwidthControl).createObject();
8718 mBandwidthControl->init(this);
8719
8720 return S_OK;
8721}
8722
8723/**
8724 * Helper to uninitialize all associated child objects and to free all data
8725 * structures.
8726 *
8727 * This method must be called as a part of the object's uninitialization
8728 * procedure (usually done in the #uninit() method).
8729 *
8730 * @note Must be called only from #uninit() or from #registeredInit().
8731 */
8732void Machine::uninitDataAndChildObjects()
8733{
8734 AutoCaller autoCaller(this);
8735 AssertComRCReturnVoid(autoCaller.rc());
8736 AssertComRCReturnVoid( autoCaller.state() == InUninit
8737 || autoCaller.state() == Limited);
8738
8739 /* tell all our other child objects we've been uninitialized */
8740 if (mBandwidthControl)
8741 {
8742 mBandwidthControl->uninit();
8743 unconst(mBandwidthControl).setNull();
8744 }
8745
8746 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8747 {
8748 if (mNetworkAdapters[slot])
8749 {
8750 mNetworkAdapters[slot]->uninit();
8751 unconst(mNetworkAdapters[slot]).setNull();
8752 }
8753 }
8754
8755 if (mUSBDeviceFilters)
8756 {
8757 mUSBDeviceFilters->uninit();
8758 unconst(mUSBDeviceFilters).setNull();
8759 }
8760
8761 if (mAudioAdapter)
8762 {
8763 mAudioAdapter->uninit();
8764 unconst(mAudioAdapter).setNull();
8765 }
8766
8767 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8768 {
8769 if (mParallelPorts[slot])
8770 {
8771 mParallelPorts[slot]->uninit();
8772 unconst(mParallelPorts[slot]).setNull();
8773 }
8774 }
8775
8776 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8777 {
8778 if (mSerialPorts[slot])
8779 {
8780 mSerialPorts[slot]->uninit();
8781 unconst(mSerialPorts[slot]).setNull();
8782 }
8783 }
8784
8785 if (mVRDEServer)
8786 {
8787 mVRDEServer->uninit();
8788 unconst(mVRDEServer).setNull();
8789 }
8790
8791 if (mBIOSSettings)
8792 {
8793 mBIOSSettings->uninit();
8794 unconst(mBIOSSettings).setNull();
8795 }
8796
8797 /* Deassociate media (only when a real Machine or a SnapshotMachine
8798 * instance is uninitialized; SessionMachine instances refer to real
8799 * Machine media). This is necessary for a clean re-initialization of
8800 * the VM after successfully re-checking the accessibility state. Note
8801 * that in case of normal Machine or SnapshotMachine uninitialization (as
8802 * a result of unregistering or deleting the snapshot), outdated media
8803 * attachments will already be uninitialized and deleted, so this
8804 * code will not affect them. */
8805 if ( !!mMediaData
8806 && (!isSessionMachine())
8807 )
8808 {
8809 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8810 it != mMediaData->mAttachments.end();
8811 ++it)
8812 {
8813 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8814 if (pMedium.isNull())
8815 continue;
8816 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8817 AssertComRC(rc);
8818 }
8819 }
8820
8821 if (!isSessionMachine() && !isSnapshotMachine())
8822 {
8823 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8824 if (mData->mFirstSnapshot)
8825 {
8826 // snapshots tree is protected by machine write lock; strictly
8827 // this isn't necessary here since we're deleting the entire
8828 // machine, but otherwise we assert in Snapshot::uninit()
8829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8830 mData->mFirstSnapshot->uninit();
8831 mData->mFirstSnapshot.setNull();
8832 }
8833
8834 mData->mCurrentSnapshot.setNull();
8835 }
8836
8837 /* free data structures (the essential mData structure is not freed here
8838 * since it may be still in use) */
8839 mMediaData.free();
8840 mStorageControllers.free();
8841 mUSBControllers.free();
8842 mHWData.free();
8843 mUserData.free();
8844 mSSData.free();
8845}
8846
8847/**
8848 * Returns a pointer to the Machine object for this machine that acts like a
8849 * parent for complex machine data objects such as shared folders, etc.
8850 *
8851 * For primary Machine objects and for SnapshotMachine objects, returns this
8852 * object's pointer itself. For SessionMachine objects, returns the peer
8853 * (primary) machine pointer.
8854 */
8855Machine* Machine::getMachine()
8856{
8857 if (isSessionMachine())
8858 return (Machine*)mPeer;
8859 return this;
8860}
8861
8862/**
8863 * Makes sure that there are no machine state dependents. If necessary, waits
8864 * for the number of dependents to drop to zero.
8865 *
8866 * Make sure this method is called from under this object's write lock to
8867 * guarantee that no new dependents may be added when this method returns
8868 * control to the caller.
8869 *
8870 * @note Locks this object for writing. The lock will be released while waiting
8871 * (if necessary).
8872 *
8873 * @warning To be used only in methods that change the machine state!
8874 */
8875void Machine::ensureNoStateDependencies()
8876{
8877 AssertReturnVoid(isWriteLockOnCurrentThread());
8878
8879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8880
8881 /* Wait for all state dependents if necessary */
8882 if (mData->mMachineStateDeps != 0)
8883 {
8884 /* lazy semaphore creation */
8885 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8886 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8887
8888 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8889 mData->mMachineStateDeps));
8890
8891 ++mData->mMachineStateChangePending;
8892
8893 /* reset the semaphore before waiting, the last dependent will signal
8894 * it */
8895 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8896
8897 alock.release();
8898
8899 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8900
8901 alock.acquire();
8902
8903 -- mData->mMachineStateChangePending;
8904 }
8905}
8906
8907/**
8908 * Changes the machine state and informs callbacks.
8909 *
8910 * This method is not intended to fail so it either returns S_OK or asserts (and
8911 * returns a failure).
8912 *
8913 * @note Locks this object for writing.
8914 */
8915HRESULT Machine::setMachineState(MachineState_T aMachineState)
8916{
8917 LogFlowThisFuncEnter();
8918 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8919
8920 AutoCaller autoCaller(this);
8921 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8922
8923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8924
8925 /* wait for state dependents to drop to zero */
8926 ensureNoStateDependencies();
8927
8928 if (mData->mMachineState != aMachineState)
8929 {
8930 mData->mMachineState = aMachineState;
8931
8932 RTTimeNow(&mData->mLastStateChange);
8933
8934 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8935 }
8936
8937 LogFlowThisFuncLeave();
8938 return S_OK;
8939}
8940
8941/**
8942 * Searches for a shared folder with the given logical name
8943 * in the collection of shared folders.
8944 *
8945 * @param aName logical name of the shared folder
8946 * @param aSharedFolder where to return the found object
8947 * @param aSetError whether to set the error info if the folder is
8948 * not found
8949 * @return
8950 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8951 *
8952 * @note
8953 * must be called from under the object's lock!
8954 */
8955HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8956 ComObjPtr<SharedFolder> &aSharedFolder,
8957 bool aSetError /* = false */)
8958{
8959 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8960 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8961 it != mHWData->mSharedFolders.end();
8962 ++it)
8963 {
8964 SharedFolder *pSF = *it;
8965 AutoCaller autoCaller(pSF);
8966 if (pSF->getName() == aName)
8967 {
8968 aSharedFolder = pSF;
8969 rc = S_OK;
8970 break;
8971 }
8972 }
8973
8974 if (aSetError && FAILED(rc))
8975 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8976
8977 return rc;
8978}
8979
8980/**
8981 * Initializes all machine instance data from the given settings structures
8982 * from XML. The exception is the machine UUID which needs special handling
8983 * depending on the caller's use case, so the caller needs to set that herself.
8984 *
8985 * This gets called in several contexts during machine initialization:
8986 *
8987 * -- When machine XML exists on disk already and needs to be loaded into memory,
8988 * for example, from registeredInit() to load all registered machines on
8989 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8990 * attached to the machine should be part of some media registry already.
8991 *
8992 * -- During OVF import, when a machine config has been constructed from an
8993 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8994 * ensure that the media listed as attachments in the config (which have
8995 * been imported from the OVF) receive the correct registry ID.
8996 *
8997 * -- During VM cloning.
8998 *
8999 * @param config Machine settings from XML.
9000 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
9001 * @return
9002 */
9003HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9004 const Guid *puuidRegistry)
9005{
9006 // copy name, description, OS type, teleporter, UTC etc.
9007 mUserData->s = config.machineUserData;
9008
9009 // Decode the Icon overide data from config userdata and set onto Machine.
9010 #define DECODE_STR_MAX _1M
9011 const char* pszStr = config.machineUserData.ovIcon.c_str();
9012 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
9013 if (cbOut > DECODE_STR_MAX)
9014 return setError(E_FAIL,
9015 tr("Icon Data too long.'%d' > '%d'"),
9016 cbOut,
9017 DECODE_STR_MAX);
9018 com::SafeArray<BYTE> iconByte(cbOut);
9019 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
9020 if (FAILED(rc))
9021 return setError(E_FAIL,
9022 tr("Failure to Decode Icon Data. '%s' (%d)"),
9023 pszStr,
9024 rc);
9025 mUserData->mIcon.resize(iconByte.size());
9026 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
9027
9028 // look up the object by Id to check it is valid
9029 ComPtr<IGuestOSType> guestOSType;
9030 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
9031 guestOSType.asOutParam());
9032 if (FAILED(rc)) return rc;
9033
9034 // stateFile (optional)
9035 if (config.strStateFile.isEmpty())
9036 mSSData->strStateFilePath.setNull();
9037 else
9038 {
9039 Utf8Str stateFilePathFull(config.strStateFile);
9040 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
9041 if (RT_FAILURE(vrc))
9042 return setError(E_FAIL,
9043 tr("Invalid saved state file path '%s' (%Rrc)"),
9044 config.strStateFile.c_str(),
9045 vrc);
9046 mSSData->strStateFilePath = stateFilePathFull;
9047 }
9048
9049 // snapshot folder needs special processing so set it again
9050 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9051 if (FAILED(rc)) return rc;
9052
9053 /* Copy the extra data items (Not in any case config is already the same as
9054 * mData->pMachineConfigFile, like when the xml files are read from disk. So
9055 * make sure the extra data map is copied). */
9056 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9057
9058 /* currentStateModified (optional, default is true) */
9059 mData->mCurrentStateModified = config.fCurrentStateModified;
9060
9061 mData->mLastStateChange = config.timeLastStateChange;
9062
9063 /*
9064 * note: all mUserData members must be assigned prior this point because
9065 * we need to commit changes in order to let mUserData be shared by all
9066 * snapshot machine instances.
9067 */
9068 mUserData.commitCopy();
9069
9070 // machine registry, if present (must be loaded before snapshots)
9071 if (config.canHaveOwnMediaRegistry())
9072 {
9073 // determine machine folder
9074 Utf8Str strMachineFolder = getSettingsFileFull();
9075 strMachineFolder.stripFilename();
9076 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
9077 config.mediaRegistry,
9078 strMachineFolder);
9079 if (FAILED(rc)) return rc;
9080 }
9081
9082 /* Snapshot node (optional) */
9083 size_t cRootSnapshots;
9084 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9085 {
9086 // there must be only one root snapshot
9087 Assert(cRootSnapshots == 1);
9088
9089 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9090
9091 rc = loadSnapshot(snap,
9092 config.uuidCurrentSnapshot,
9093 NULL); // no parent == first snapshot
9094 if (FAILED(rc)) return rc;
9095 }
9096
9097 // hardware data
9098 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9099 if (FAILED(rc)) return rc;
9100
9101 // load storage controllers
9102 rc = loadStorageControllers(config.storageMachine,
9103 puuidRegistry,
9104 NULL /* puuidSnapshot */);
9105 if (FAILED(rc)) return rc;
9106
9107 /*
9108 * NOTE: the assignment below must be the last thing to do,
9109 * otherwise it will be not possible to change the settings
9110 * somewhere in the code above because all setters will be
9111 * blocked by checkStateDependency(MutableStateDep).
9112 */
9113
9114 /* set the machine state to Aborted or Saved when appropriate */
9115 if (config.fAborted)
9116 {
9117 mSSData->strStateFilePath.setNull();
9118
9119 /* no need to use setMachineState() during init() */
9120 mData->mMachineState = MachineState_Aborted;
9121 }
9122 else if (!mSSData->strStateFilePath.isEmpty())
9123 {
9124 /* no need to use setMachineState() during init() */
9125 mData->mMachineState = MachineState_Saved;
9126 }
9127
9128 // after loading settings, we are no longer different from the XML on disk
9129 mData->flModifications = 0;
9130
9131 return S_OK;
9132}
9133
9134/**
9135 * Recursively loads all snapshots starting from the given.
9136 *
9137 * @param aNode <Snapshot> node.
9138 * @param aCurSnapshotId Current snapshot ID from the settings file.
9139 * @param aParentSnapshot Parent snapshot.
9140 */
9141HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9142 const Guid &aCurSnapshotId,
9143 Snapshot *aParentSnapshot)
9144{
9145 AssertReturn(!isSnapshotMachine(), E_FAIL);
9146 AssertReturn(!isSessionMachine(), E_FAIL);
9147
9148 HRESULT rc = S_OK;
9149
9150 Utf8Str strStateFile;
9151 if (!data.strStateFile.isEmpty())
9152 {
9153 /* optional */
9154 strStateFile = data.strStateFile;
9155 int vrc = calculateFullPath(strStateFile, strStateFile);
9156 if (RT_FAILURE(vrc))
9157 return setError(E_FAIL,
9158 tr("Invalid saved state file path '%s' (%Rrc)"),
9159 strStateFile.c_str(),
9160 vrc);
9161 }
9162
9163 /* create a snapshot machine object */
9164 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9165 pSnapshotMachine.createObject();
9166 rc = pSnapshotMachine->initFromSettings(this,
9167 data.hardware,
9168 &data.debugging,
9169 &data.autostart,
9170 data.storage,
9171 data.uuid.ref(),
9172 strStateFile);
9173 if (FAILED(rc)) return rc;
9174
9175 /* create a snapshot object */
9176 ComObjPtr<Snapshot> pSnapshot;
9177 pSnapshot.createObject();
9178 /* initialize the snapshot */
9179 rc = pSnapshot->init(mParent, // VirtualBox object
9180 data.uuid,
9181 data.strName,
9182 data.strDescription,
9183 data.timestamp,
9184 pSnapshotMachine,
9185 aParentSnapshot);
9186 if (FAILED(rc)) return rc;
9187
9188 /* memorize the first snapshot if necessary */
9189 if (!mData->mFirstSnapshot)
9190 mData->mFirstSnapshot = pSnapshot;
9191
9192 /* memorize the current snapshot when appropriate */
9193 if ( !mData->mCurrentSnapshot
9194 && pSnapshot->getId() == aCurSnapshotId
9195 )
9196 mData->mCurrentSnapshot = pSnapshot;
9197
9198 // now create the children
9199 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9200 it != data.llChildSnapshots.end();
9201 ++it)
9202 {
9203 const settings::Snapshot &childData = *it;
9204 // recurse
9205 rc = loadSnapshot(childData,
9206 aCurSnapshotId,
9207 pSnapshot); // parent = the one we created above
9208 if (FAILED(rc)) return rc;
9209 }
9210
9211 return rc;
9212}
9213
9214/**
9215 * Loads settings into mHWData.
9216 *
9217 * @param data Reference to the hardware settings.
9218 * @param pDbg Pointer to the debugging settings.
9219 * @param pAutostart Pointer to the autostart settings.
9220 */
9221HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9222 const settings::Autostart *pAutostart)
9223{
9224 AssertReturn(!isSessionMachine(), E_FAIL);
9225
9226 HRESULT rc = S_OK;
9227
9228 try
9229 {
9230 /* The hardware version attribute (optional). */
9231 mHWData->mHWVersion = data.strVersion;
9232 mHWData->mHardwareUUID = data.uuid;
9233
9234 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9235 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9236 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9237 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9238 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9239 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9240 mHWData->mPAEEnabled = data.fPAE;
9241 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9242 mHWData->mLongMode = data.enmLongMode;
9243 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9244 mHWData->mCPUCount = data.cCPUs;
9245 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9246 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9247
9248 // cpu
9249 if (mHWData->mCPUHotPlugEnabled)
9250 {
9251 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9252 it != data.llCpus.end();
9253 ++it)
9254 {
9255 const settings::Cpu &cpu = *it;
9256
9257 mHWData->mCPUAttached[cpu.ulId] = true;
9258 }
9259 }
9260
9261 // cpuid leafs
9262 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9263 it != data.llCpuIdLeafs.end();
9264 ++it)
9265 {
9266 const settings::CpuIdLeaf &leaf = *it;
9267
9268 switch (leaf.ulId)
9269 {
9270 case 0x0:
9271 case 0x1:
9272 case 0x2:
9273 case 0x3:
9274 case 0x4:
9275 case 0x5:
9276 case 0x6:
9277 case 0x7:
9278 case 0x8:
9279 case 0x9:
9280 case 0xA:
9281 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9282 break;
9283
9284 case 0x80000000:
9285 case 0x80000001:
9286 case 0x80000002:
9287 case 0x80000003:
9288 case 0x80000004:
9289 case 0x80000005:
9290 case 0x80000006:
9291 case 0x80000007:
9292 case 0x80000008:
9293 case 0x80000009:
9294 case 0x8000000A:
9295 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9296 break;
9297
9298 default:
9299 /* just ignore */
9300 break;
9301 }
9302 }
9303
9304 mHWData->mMemorySize = data.ulMemorySizeMB;
9305 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9306
9307 // boot order
9308 for (size_t i = 0;
9309 i < RT_ELEMENTS(mHWData->mBootOrder);
9310 i++)
9311 {
9312 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9313 if (it == data.mapBootOrder.end())
9314 mHWData->mBootOrder[i] = DeviceType_Null;
9315 else
9316 mHWData->mBootOrder[i] = it->second;
9317 }
9318
9319 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9320 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9321 mHWData->mMonitorCount = data.cMonitors;
9322 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9323 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9324 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9325 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9326 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9327 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9328 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9329 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9330 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9331 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9332 if (!data.strVideoCaptureFile.isEmpty())
9333 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9334 else
9335 mHWData->mVideoCaptureFile.setNull();
9336 mHWData->mFirmwareType = data.firmwareType;
9337 mHWData->mPointingHIDType = data.pointingHIDType;
9338 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9339 mHWData->mChipsetType = data.chipsetType;
9340 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9341 mHWData->mHPETEnabled = data.fHPETEnabled;
9342
9343 /* VRDEServer */
9344 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9345 if (FAILED(rc)) return rc;
9346
9347 /* BIOS */
9348 rc = mBIOSSettings->loadSettings(data.biosSettings);
9349 if (FAILED(rc)) return rc;
9350
9351 // Bandwidth control (must come before network adapters)
9352 rc = mBandwidthControl->loadSettings(data.ioSettings);
9353 if (FAILED(rc)) return rc;
9354
9355 /* Shared folders */
9356 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9357 it != data.usbSettings.llUSBControllers.end();
9358 ++it)
9359 {
9360 const settings::USBController &settingsCtrl = *it;
9361 ComObjPtr<USBController> newCtrl;
9362
9363 newCtrl.createObject();
9364 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9365 mUSBControllers->push_back(newCtrl);
9366 }
9367
9368 /* USB device filters */
9369 rc = mUSBDeviceFilters->loadSettings(data.usbSettings);
9370 if (FAILED(rc)) return rc;
9371
9372 // network adapters
9373 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9374 uint32_t oldCount = mNetworkAdapters.size();
9375 if (newCount > oldCount)
9376 {
9377 mNetworkAdapters.resize(newCount);
9378 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9379 {
9380 unconst(mNetworkAdapters[slot]).createObject();
9381 mNetworkAdapters[slot]->init(this, slot);
9382 }
9383 }
9384 else if (newCount < oldCount)
9385 mNetworkAdapters.resize(newCount);
9386 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9387 it != data.llNetworkAdapters.end();
9388 ++it)
9389 {
9390 const settings::NetworkAdapter &nic = *it;
9391
9392 /* slot unicity is guaranteed by XML Schema */
9393 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9394 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9395 if (FAILED(rc)) return rc;
9396 }
9397
9398 // serial ports
9399 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9400 it != data.llSerialPorts.end();
9401 ++it)
9402 {
9403 const settings::SerialPort &s = *it;
9404
9405 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9406 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9407 if (FAILED(rc)) return rc;
9408 }
9409
9410 // parallel ports (optional)
9411 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9412 it != data.llParallelPorts.end();
9413 ++it)
9414 {
9415 const settings::ParallelPort &p = *it;
9416
9417 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9418 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9419 if (FAILED(rc)) return rc;
9420 }
9421
9422 /* AudioAdapter */
9423 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9424 if (FAILED(rc)) return rc;
9425
9426 /* Shared folders */
9427 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9428 it != data.llSharedFolders.end();
9429 ++it)
9430 {
9431 const settings::SharedFolder &sf = *it;
9432
9433 ComObjPtr<SharedFolder> sharedFolder;
9434 /* Check for double entries. Not allowed! */
9435 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9436 if (SUCCEEDED(rc))
9437 return setError(VBOX_E_OBJECT_IN_USE,
9438 tr("Shared folder named '%s' already exists"),
9439 sf.strName.c_str());
9440
9441 /* Create the new shared folder. Don't break on error. This will be
9442 * reported when the machine starts. */
9443 sharedFolder.createObject();
9444 rc = sharedFolder->init(getMachine(),
9445 sf.strName,
9446 sf.strHostPath,
9447 RT_BOOL(sf.fWritable),
9448 RT_BOOL(sf.fAutoMount),
9449 false /* fFailOnError */);
9450 if (FAILED(rc)) return rc;
9451 mHWData->mSharedFolders.push_back(sharedFolder);
9452 }
9453
9454 // Clipboard
9455 mHWData->mClipboardMode = data.clipboardMode;
9456
9457 // drag'n'drop
9458 mHWData->mDragAndDropMode = data.dragAndDropMode;
9459
9460 // guest settings
9461 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9462
9463 // IO settings
9464 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9465 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9466
9467 // Host PCI devices
9468 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9469 it != data.pciAttachments.end();
9470 ++it)
9471 {
9472 const settings::HostPCIDeviceAttachment &hpda = *it;
9473 ComObjPtr<PCIDeviceAttachment> pda;
9474
9475 pda.createObject();
9476 pda->loadSettings(this, hpda);
9477 mHWData->mPCIDeviceAssignments.push_back(pda);
9478 }
9479
9480 /*
9481 * (The following isn't really real hardware, but it lives in HWData
9482 * for reasons of convenience.)
9483 */
9484
9485#ifdef VBOX_WITH_GUEST_PROPS
9486 /* Guest properties (optional) */
9487 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9488 it != data.llGuestProperties.end();
9489 ++it)
9490 {
9491 const settings::GuestProperty &prop = *it;
9492 uint32_t fFlags = guestProp::NILFLAG;
9493 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9494 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9495 mHWData->mGuestProperties[prop.strName] = property;
9496 }
9497
9498 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9499#endif /* VBOX_WITH_GUEST_PROPS defined */
9500
9501 rc = loadDebugging(pDbg);
9502 if (FAILED(rc))
9503 return rc;
9504
9505 mHWData->mAutostart = *pAutostart;
9506
9507 /* default frontend */
9508 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9509 }
9510 catch(std::bad_alloc &)
9511 {
9512 return E_OUTOFMEMORY;
9513 }
9514
9515 AssertComRC(rc);
9516 return rc;
9517}
9518
9519/**
9520 * Called from Machine::loadHardware() to load the debugging settings of the
9521 * machine.
9522 *
9523 * @param pDbg Pointer to the settings.
9524 */
9525HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9526{
9527 mHWData->mDebugging = *pDbg;
9528 /* no more processing currently required, this will probably change. */
9529 return S_OK;
9530}
9531
9532/**
9533 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9534 *
9535 * @param data
9536 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9537 * @param puuidSnapshot
9538 * @return
9539 */
9540HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9541 const Guid *puuidRegistry,
9542 const Guid *puuidSnapshot)
9543{
9544 AssertReturn(!isSessionMachine(), E_FAIL);
9545
9546 HRESULT rc = S_OK;
9547
9548 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9549 it != data.llStorageControllers.end();
9550 ++it)
9551 {
9552 const settings::StorageController &ctlData = *it;
9553
9554 ComObjPtr<StorageController> pCtl;
9555 /* Try to find one with the name first. */
9556 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9557 if (SUCCEEDED(rc))
9558 return setError(VBOX_E_OBJECT_IN_USE,
9559 tr("Storage controller named '%s' already exists"),
9560 ctlData.strName.c_str());
9561
9562 pCtl.createObject();
9563 rc = pCtl->init(this,
9564 ctlData.strName,
9565 ctlData.storageBus,
9566 ctlData.ulInstance,
9567 ctlData.fBootable);
9568 if (FAILED(rc)) return rc;
9569
9570 mStorageControllers->push_back(pCtl);
9571
9572 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9573 if (FAILED(rc)) return rc;
9574
9575 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9576 if (FAILED(rc)) return rc;
9577
9578 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9579 if (FAILED(rc)) return rc;
9580
9581 /* Set IDE emulation settings (only for AHCI controller). */
9582 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9583 {
9584 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9585 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9586 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9587 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9588 )
9589 return rc;
9590 }
9591
9592 /* Load the attached devices now. */
9593 rc = loadStorageDevices(pCtl,
9594 ctlData,
9595 puuidRegistry,
9596 puuidSnapshot);
9597 if (FAILED(rc)) return rc;
9598 }
9599
9600 return S_OK;
9601}
9602
9603/**
9604 * Called from loadStorageControllers for a controller's devices.
9605 *
9606 * @param aStorageController
9607 * @param data
9608 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9609 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9610 * @return
9611 */
9612HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9613 const settings::StorageController &data,
9614 const Guid *puuidRegistry,
9615 const Guid *puuidSnapshot)
9616{
9617 HRESULT rc = S_OK;
9618
9619 /* paranoia: detect duplicate attachments */
9620 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9621 it != data.llAttachedDevices.end();
9622 ++it)
9623 {
9624 const settings::AttachedDevice &ad = *it;
9625
9626 for (settings::AttachedDevicesList::const_iterator it2 = it;
9627 it2 != data.llAttachedDevices.end();
9628 ++it2)
9629 {
9630 if (it == it2)
9631 continue;
9632
9633 const settings::AttachedDevice &ad2 = *it2;
9634
9635 if ( ad.lPort == ad2.lPort
9636 && ad.lDevice == ad2.lDevice)
9637 {
9638 return setError(E_FAIL,
9639 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9640 aStorageController->getName().c_str(),
9641 ad.lPort,
9642 ad.lDevice,
9643 mUserData->s.strName.c_str());
9644 }
9645 }
9646 }
9647
9648 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9649 it != data.llAttachedDevices.end();
9650 ++it)
9651 {
9652 const settings::AttachedDevice &dev = *it;
9653 ComObjPtr<Medium> medium;
9654
9655 switch (dev.deviceType)
9656 {
9657 case DeviceType_Floppy:
9658 case DeviceType_DVD:
9659 if (dev.strHostDriveSrc.isNotEmpty())
9660 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9661 else
9662 rc = mParent->findRemoveableMedium(dev.deviceType,
9663 dev.uuid,
9664 false /* fRefresh */,
9665 false /* aSetError */,
9666 medium);
9667 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9668 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9669 rc = S_OK;
9670 break;
9671
9672 case DeviceType_HardDisk:
9673 {
9674 /* find a hard disk by UUID */
9675 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9676 if (FAILED(rc))
9677 {
9678 if (isSnapshotMachine())
9679 {
9680 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9681 // so the user knows that the bad disk is in a snapshot somewhere
9682 com::ErrorInfo info;
9683 return setError(E_FAIL,
9684 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9685 puuidSnapshot->raw(),
9686 info.getText().raw());
9687 }
9688 else
9689 return rc;
9690 }
9691
9692 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9693
9694 if (medium->getType() == MediumType_Immutable)
9695 {
9696 if (isSnapshotMachine())
9697 return setError(E_FAIL,
9698 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9699 "of the virtual machine '%s' ('%s')"),
9700 medium->getLocationFull().c_str(),
9701 dev.uuid.raw(),
9702 puuidSnapshot->raw(),
9703 mUserData->s.strName.c_str(),
9704 mData->m_strConfigFileFull.c_str());
9705
9706 return setError(E_FAIL,
9707 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9708 medium->getLocationFull().c_str(),
9709 dev.uuid.raw(),
9710 mUserData->s.strName.c_str(),
9711 mData->m_strConfigFileFull.c_str());
9712 }
9713
9714 if (medium->getType() == MediumType_MultiAttach)
9715 {
9716 if (isSnapshotMachine())
9717 return setError(E_FAIL,
9718 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9719 "of the virtual machine '%s' ('%s')"),
9720 medium->getLocationFull().c_str(),
9721 dev.uuid.raw(),
9722 puuidSnapshot->raw(),
9723 mUserData->s.strName.c_str(),
9724 mData->m_strConfigFileFull.c_str());
9725
9726 return setError(E_FAIL,
9727 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9728 medium->getLocationFull().c_str(),
9729 dev.uuid.raw(),
9730 mUserData->s.strName.c_str(),
9731 mData->m_strConfigFileFull.c_str());
9732 }
9733
9734 if ( !isSnapshotMachine()
9735 && medium->getChildren().size() != 0
9736 )
9737 return setError(E_FAIL,
9738 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9739 "because it has %d differencing child hard disks"),
9740 medium->getLocationFull().c_str(),
9741 dev.uuid.raw(),
9742 mUserData->s.strName.c_str(),
9743 mData->m_strConfigFileFull.c_str(),
9744 medium->getChildren().size());
9745
9746 if (findAttachment(mMediaData->mAttachments,
9747 medium))
9748 return setError(E_FAIL,
9749 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9750 medium->getLocationFull().c_str(),
9751 dev.uuid.raw(),
9752 mUserData->s.strName.c_str(),
9753 mData->m_strConfigFileFull.c_str());
9754
9755 break;
9756 }
9757
9758 default:
9759 return setError(E_FAIL,
9760 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9761 medium->getLocationFull().c_str(),
9762 mUserData->s.strName.c_str(),
9763 mData->m_strConfigFileFull.c_str());
9764 }
9765
9766 if (FAILED(rc))
9767 break;
9768
9769 /* Bandwidth groups are loaded at this point. */
9770 ComObjPtr<BandwidthGroup> pBwGroup;
9771
9772 if (!dev.strBwGroup.isEmpty())
9773 {
9774 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9775 if (FAILED(rc))
9776 return setError(E_FAIL,
9777 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9778 medium->getLocationFull().c_str(),
9779 dev.strBwGroup.c_str(),
9780 mUserData->s.strName.c_str(),
9781 mData->m_strConfigFileFull.c_str());
9782 pBwGroup->reference();
9783 }
9784
9785 const Bstr controllerName = aStorageController->getName();
9786 ComObjPtr<MediumAttachment> pAttachment;
9787 pAttachment.createObject();
9788 rc = pAttachment->init(this,
9789 medium,
9790 controllerName,
9791 dev.lPort,
9792 dev.lDevice,
9793 dev.deviceType,
9794 false,
9795 dev.fPassThrough,
9796 dev.fTempEject,
9797 dev.fNonRotational,
9798 dev.fDiscard,
9799 dev.fHotPluggable,
9800 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9801 if (FAILED(rc)) break;
9802
9803 /* associate the medium with this machine and snapshot */
9804 if (!medium.isNull())
9805 {
9806 AutoCaller medCaller(medium);
9807 if (FAILED(medCaller.rc())) return medCaller.rc();
9808 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9809
9810 if (isSnapshotMachine())
9811 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9812 else
9813 rc = medium->addBackReference(mData->mUuid);
9814 /* If the medium->addBackReference fails it sets an appropriate
9815 * error message, so no need to do any guesswork here. */
9816
9817 if (puuidRegistry)
9818 // caller wants registry ID to be set on all attached media (OVF import case)
9819 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9820 }
9821
9822 if (FAILED(rc))
9823 break;
9824
9825 /* back up mMediaData to let registeredInit() properly rollback on failure
9826 * (= limited accessibility) */
9827 setModified(IsModified_Storage);
9828 mMediaData.backup();
9829 mMediaData->mAttachments.push_back(pAttachment);
9830 }
9831
9832 return rc;
9833}
9834
9835/**
9836 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9837 *
9838 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9839 * @param aSnapshot where to return the found snapshot
9840 * @param aSetError true to set extended error info on failure
9841 */
9842HRESULT Machine::findSnapshotById(const Guid &aId,
9843 ComObjPtr<Snapshot> &aSnapshot,
9844 bool aSetError /* = false */)
9845{
9846 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9847
9848 if (!mData->mFirstSnapshot)
9849 {
9850 if (aSetError)
9851 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9852 return E_FAIL;
9853 }
9854
9855 if (aId.isZero())
9856 aSnapshot = mData->mFirstSnapshot;
9857 else
9858 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9859
9860 if (!aSnapshot)
9861 {
9862 if (aSetError)
9863 return setError(E_FAIL,
9864 tr("Could not find a snapshot with UUID {%s}"),
9865 aId.toString().c_str());
9866 return E_FAIL;
9867 }
9868
9869 return S_OK;
9870}
9871
9872/**
9873 * Returns the snapshot with the given name or fails of no such snapshot.
9874 *
9875 * @param aName snapshot name to find
9876 * @param aSnapshot where to return the found snapshot
9877 * @param aSetError true to set extended error info on failure
9878 */
9879HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9880 ComObjPtr<Snapshot> &aSnapshot,
9881 bool aSetError /* = false */)
9882{
9883 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9884
9885 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9886
9887 if (!mData->mFirstSnapshot)
9888 {
9889 if (aSetError)
9890 return setError(VBOX_E_OBJECT_NOT_FOUND,
9891 tr("This machine does not have any snapshots"));
9892 return VBOX_E_OBJECT_NOT_FOUND;
9893 }
9894
9895 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9896
9897 if (!aSnapshot)
9898 {
9899 if (aSetError)
9900 return setError(VBOX_E_OBJECT_NOT_FOUND,
9901 tr("Could not find a snapshot named '%s'"), strName.c_str());
9902 return VBOX_E_OBJECT_NOT_FOUND;
9903 }
9904
9905 return S_OK;
9906}
9907
9908/**
9909 * Returns a storage controller object with the given name.
9910 *
9911 * @param aName storage controller name to find
9912 * @param aStorageController where to return the found storage controller
9913 * @param aSetError true to set extended error info on failure
9914 */
9915HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9916 ComObjPtr<StorageController> &aStorageController,
9917 bool aSetError /* = false */)
9918{
9919 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9920
9921 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9922 it != mStorageControllers->end();
9923 ++it)
9924 {
9925 if ((*it)->getName() == aName)
9926 {
9927 aStorageController = (*it);
9928 return S_OK;
9929 }
9930 }
9931
9932 if (aSetError)
9933 return setError(VBOX_E_OBJECT_NOT_FOUND,
9934 tr("Could not find a storage controller named '%s'"),
9935 aName.c_str());
9936 return VBOX_E_OBJECT_NOT_FOUND;
9937}
9938
9939/**
9940 * Returns a USB controller object with the given name.
9941 *
9942 * @param aName USB controller name to find
9943 * @param aUSBController where to return the found USB controller
9944 * @param aSetError true to set extended error info on failure
9945 */
9946HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9947 ComObjPtr<USBController> &aUSBController,
9948 bool aSetError /* = false */)
9949{
9950 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9951
9952 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9953 it != mUSBControllers->end();
9954 ++it)
9955 {
9956 if ((*it)->getName() == aName)
9957 {
9958 aUSBController = (*it);
9959 return S_OK;
9960 }
9961 }
9962
9963 if (aSetError)
9964 return setError(VBOX_E_OBJECT_NOT_FOUND,
9965 tr("Could not find a storage controller named '%s'"),
9966 aName.c_str());
9967 return VBOX_E_OBJECT_NOT_FOUND;
9968}
9969
9970/**
9971 * Returns the number of USB controller instance of the given type.
9972 *
9973 * @param enmType USB controller type.
9974 */
9975ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9976{
9977 ULONG cCtrls = 0;
9978
9979 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9980 it != mUSBControllers->end();
9981 ++it)
9982 {
9983 if ((*it)->getControllerType() == enmType)
9984 cCtrls++;
9985 }
9986
9987 return cCtrls;
9988}
9989
9990HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9991 MediaData::AttachmentList &atts)
9992{
9993 AutoCaller autoCaller(this);
9994 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9995
9996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9997
9998 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9999 it != mMediaData->mAttachments.end();
10000 ++it)
10001 {
10002 const ComObjPtr<MediumAttachment> &pAtt = *it;
10003
10004 // should never happen, but deal with NULL pointers in the list.
10005 AssertStmt(!pAtt.isNull(), continue);
10006
10007 // getControllerName() needs caller+read lock
10008 AutoCaller autoAttCaller(pAtt);
10009 if (FAILED(autoAttCaller.rc()))
10010 {
10011 atts.clear();
10012 return autoAttCaller.rc();
10013 }
10014 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10015
10016 if (pAtt->getControllerName() == aName)
10017 atts.push_back(pAtt);
10018 }
10019
10020 return S_OK;
10021}
10022
10023/**
10024 * Helper for #saveSettings. Cares about renaming the settings directory and
10025 * file if the machine name was changed and about creating a new settings file
10026 * if this is a new machine.
10027 *
10028 * @note Must be never called directly but only from #saveSettings().
10029 */
10030HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
10031{
10032 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10033
10034 HRESULT rc = S_OK;
10035
10036 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10037
10038 /// @todo need to handle primary group change, too
10039
10040 /* attempt to rename the settings file if machine name is changed */
10041 if ( mUserData->s.fNameSync
10042 && mUserData.isBackedUp()
10043 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10044 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10045 )
10046 {
10047 bool dirRenamed = false;
10048 bool fileRenamed = false;
10049
10050 Utf8Str configFile, newConfigFile;
10051 Utf8Str configFilePrev, newConfigFilePrev;
10052 Utf8Str configDir, newConfigDir;
10053
10054 do
10055 {
10056 int vrc = VINF_SUCCESS;
10057
10058 Utf8Str name = mUserData.backedUpData()->s.strName;
10059 Utf8Str newName = mUserData->s.strName;
10060 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10061 if (group == "/")
10062 group.setNull();
10063 Utf8Str newGroup = mUserData->s.llGroups.front();
10064 if (newGroup == "/")
10065 newGroup.setNull();
10066
10067 configFile = mData->m_strConfigFileFull;
10068
10069 /* first, rename the directory if it matches the group and machine name */
10070 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
10071 group.c_str(), RTPATH_DELIMITER, name.c_str());
10072 /** @todo hack, make somehow use of ComposeMachineFilename */
10073 if (mUserData->s.fDirectoryIncludesUUID)
10074 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10075 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
10076 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10077 /** @todo hack, make somehow use of ComposeMachineFilename */
10078 if (mUserData->s.fDirectoryIncludesUUID)
10079 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10080 configDir = configFile;
10081 configDir.stripFilename();
10082 newConfigDir = configDir;
10083 if ( configDir.length() >= groupPlusName.length()
10084 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
10085 {
10086 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10087 Utf8Str newConfigBaseDir(newConfigDir);
10088 newConfigDir.append(newGroupPlusName);
10089 /* consistency: use \ if appropriate on the platform */
10090 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10091 /* new dir and old dir cannot be equal here because of 'if'
10092 * above and because name != newName */
10093 Assert(configDir != newConfigDir);
10094 if (!fSettingsFileIsNew)
10095 {
10096 /* perform real rename only if the machine is not new */
10097 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10098 if ( vrc == VERR_FILE_NOT_FOUND
10099 || vrc == VERR_PATH_NOT_FOUND)
10100 {
10101 /* create the parent directory, then retry renaming */
10102 Utf8Str parent(newConfigDir);
10103 parent.stripFilename();
10104 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10105 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10106 }
10107 if (RT_FAILURE(vrc))
10108 {
10109 rc = setError(E_FAIL,
10110 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10111 configDir.c_str(),
10112 newConfigDir.c_str(),
10113 vrc);
10114 break;
10115 }
10116 /* delete subdirectories which are no longer needed */
10117 Utf8Str dir(configDir);
10118 dir.stripFilename();
10119 while (dir != newConfigBaseDir && dir != ".")
10120 {
10121 vrc = RTDirRemove(dir.c_str());
10122 if (RT_FAILURE(vrc))
10123 break;
10124 dir.stripFilename();
10125 }
10126 dirRenamed = true;
10127 }
10128 }
10129
10130 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10131 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10132
10133 /* then try to rename the settings file itself */
10134 if (newConfigFile != configFile)
10135 {
10136 /* get the path to old settings file in renamed directory */
10137 configFile = Utf8StrFmt("%s%c%s",
10138 newConfigDir.c_str(),
10139 RTPATH_DELIMITER,
10140 RTPathFilename(configFile.c_str()));
10141 if (!fSettingsFileIsNew)
10142 {
10143 /* perform real rename only if the machine is not new */
10144 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10145 if (RT_FAILURE(vrc))
10146 {
10147 rc = setError(E_FAIL,
10148 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10149 configFile.c_str(),
10150 newConfigFile.c_str(),
10151 vrc);
10152 break;
10153 }
10154 fileRenamed = true;
10155 configFilePrev = configFile;
10156 configFilePrev += "-prev";
10157 newConfigFilePrev = newConfigFile;
10158 newConfigFilePrev += "-prev";
10159 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10160 }
10161 }
10162
10163 // update m_strConfigFileFull amd mConfigFile
10164 mData->m_strConfigFileFull = newConfigFile;
10165 // compute the relative path too
10166 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10167
10168 // store the old and new so that VirtualBox::saveSettings() can update
10169 // the media registry
10170 if ( mData->mRegistered
10171 && configDir != newConfigDir)
10172 {
10173 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10174
10175 if (pfNeedsGlobalSaveSettings)
10176 *pfNeedsGlobalSaveSettings = true;
10177 }
10178
10179 // in the saved state file path, replace the old directory with the new directory
10180 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10181 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10182
10183 // and do the same thing for the saved state file paths of all the online snapshots
10184 if (mData->mFirstSnapshot)
10185 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
10186 newConfigDir.c_str());
10187 }
10188 while (0);
10189
10190 if (FAILED(rc))
10191 {
10192 /* silently try to rename everything back */
10193 if (fileRenamed)
10194 {
10195 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10196 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10197 }
10198 if (dirRenamed)
10199 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10200 }
10201
10202 if (FAILED(rc)) return rc;
10203 }
10204
10205 if (fSettingsFileIsNew)
10206 {
10207 /* create a virgin config file */
10208 int vrc = VINF_SUCCESS;
10209
10210 /* ensure the settings directory exists */
10211 Utf8Str path(mData->m_strConfigFileFull);
10212 path.stripFilename();
10213 if (!RTDirExists(path.c_str()))
10214 {
10215 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10216 if (RT_FAILURE(vrc))
10217 {
10218 return setError(E_FAIL,
10219 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10220 path.c_str(),
10221 vrc);
10222 }
10223 }
10224
10225 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10226 path = Utf8Str(mData->m_strConfigFileFull);
10227 RTFILE f = NIL_RTFILE;
10228 vrc = RTFileOpen(&f, path.c_str(),
10229 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10230 if (RT_FAILURE(vrc))
10231 return setError(E_FAIL,
10232 tr("Could not create the settings file '%s' (%Rrc)"),
10233 path.c_str(),
10234 vrc);
10235 RTFileClose(f);
10236 }
10237
10238 return rc;
10239}
10240
10241/**
10242 * Saves and commits machine data, user data and hardware data.
10243 *
10244 * Note that on failure, the data remains uncommitted.
10245 *
10246 * @a aFlags may combine the following flags:
10247 *
10248 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10249 * Used when saving settings after an operation that makes them 100%
10250 * correspond to the settings from the current snapshot.
10251 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10252 * #isReallyModified() returns false. This is necessary for cases when we
10253 * change machine data directly, not through the backup()/commit() mechanism.
10254 * - SaveS_Force: settings will be saved without doing a deep compare of the
10255 * settings structures. This is used when this is called because snapshots
10256 * have changed to avoid the overhead of the deep compare.
10257 *
10258 * @note Must be called from under this object's write lock. Locks children for
10259 * writing.
10260 *
10261 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10262 * initialized to false and that will be set to true by this function if
10263 * the caller must invoke VirtualBox::saveSettings() because the global
10264 * settings have changed. This will happen if a machine rename has been
10265 * saved and the global machine and media registries will therefore need
10266 * updating.
10267 */
10268HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10269 int aFlags /*= 0*/)
10270{
10271 LogFlowThisFuncEnter();
10272
10273 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10274
10275 /* make sure child objects are unable to modify the settings while we are
10276 * saving them */
10277 ensureNoStateDependencies();
10278
10279 AssertReturn(!isSnapshotMachine(),
10280 E_FAIL);
10281
10282 HRESULT rc = S_OK;
10283 bool fNeedsWrite = false;
10284
10285 /* First, prepare to save settings. It will care about renaming the
10286 * settings directory and file if the machine name was changed and about
10287 * creating a new settings file if this is a new machine. */
10288 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10289 if (FAILED(rc)) return rc;
10290
10291 // keep a pointer to the current settings structures
10292 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10293 settings::MachineConfigFile *pNewConfig = NULL;
10294
10295 try
10296 {
10297 // make a fresh one to have everyone write stuff into
10298 pNewConfig = new settings::MachineConfigFile(NULL);
10299 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10300
10301 // now go and copy all the settings data from COM to the settings structures
10302 // (this calles saveSettings() on all the COM objects in the machine)
10303 copyMachineDataToSettings(*pNewConfig);
10304
10305 if (aFlags & SaveS_ResetCurStateModified)
10306 {
10307 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10308 mData->mCurrentStateModified = FALSE;
10309 fNeedsWrite = true; // always, no need to compare
10310 }
10311 else if (aFlags & SaveS_Force)
10312 {
10313 fNeedsWrite = true; // always, no need to compare
10314 }
10315 else
10316 {
10317 if (!mData->mCurrentStateModified)
10318 {
10319 // do a deep compare of the settings that we just saved with the settings
10320 // previously stored in the config file; this invokes MachineConfigFile::operator==
10321 // which does a deep compare of all the settings, which is expensive but less expensive
10322 // than writing out XML in vain
10323 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10324
10325 // could still be modified if any settings changed
10326 mData->mCurrentStateModified = fAnySettingsChanged;
10327
10328 fNeedsWrite = fAnySettingsChanged;
10329 }
10330 else
10331 fNeedsWrite = true;
10332 }
10333
10334 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10335
10336 if (fNeedsWrite)
10337 // now spit it all out!
10338 pNewConfig->write(mData->m_strConfigFileFull);
10339
10340 mData->pMachineConfigFile = pNewConfig;
10341 delete pOldConfig;
10342 commit();
10343
10344 // after saving settings, we are no longer different from the XML on disk
10345 mData->flModifications = 0;
10346 }
10347 catch (HRESULT err)
10348 {
10349 // we assume that error info is set by the thrower
10350 rc = err;
10351
10352 // restore old config
10353 delete pNewConfig;
10354 mData->pMachineConfigFile = pOldConfig;
10355 }
10356 catch (...)
10357 {
10358 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10359 }
10360
10361 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10362 {
10363 /* Fire the data change event, even on failure (since we've already
10364 * committed all data). This is done only for SessionMachines because
10365 * mutable Machine instances are always not registered (i.e. private
10366 * to the client process that creates them) and thus don't need to
10367 * inform callbacks. */
10368 if (isSessionMachine())
10369 mParent->onMachineDataChange(mData->mUuid);
10370 }
10371
10372 LogFlowThisFunc(("rc=%08X\n", rc));
10373 LogFlowThisFuncLeave();
10374 return rc;
10375}
10376
10377/**
10378 * Implementation for saving the machine settings into the given
10379 * settings::MachineConfigFile instance. This copies machine extradata
10380 * from the previous machine config file in the instance data, if any.
10381 *
10382 * This gets called from two locations:
10383 *
10384 * -- Machine::saveSettings(), during the regular XML writing;
10385 *
10386 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10387 * exported to OVF and we write the VirtualBox proprietary XML
10388 * into a <vbox:Machine> tag.
10389 *
10390 * This routine fills all the fields in there, including snapshots, *except*
10391 * for the following:
10392 *
10393 * -- fCurrentStateModified. There is some special logic associated with that.
10394 *
10395 * The caller can then call MachineConfigFile::write() or do something else
10396 * with it.
10397 *
10398 * Caller must hold the machine lock!
10399 *
10400 * This throws XML errors and HRESULT, so the caller must have a catch block!
10401 */
10402void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10403{
10404 // deep copy extradata
10405 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10406
10407 config.uuid = mData->mUuid;
10408
10409 // copy name, description, OS type, teleport, UTC etc.
10410 config.machineUserData = mUserData->s;
10411
10412 // Encode the Icon Override data from Machine and store on config userdata.
10413 com::SafeArray<BYTE> iconByte;
10414 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10415 ssize_t cbData = iconByte.size();
10416 if (cbData > 0)
10417 {
10418 ssize_t cchOut = RTBase64EncodedLength(cbData);
10419 Utf8Str strIconData;
10420 strIconData.reserve(cchOut+1);
10421 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10422 strIconData.mutableRaw(), strIconData.capacity(),
10423 NULL);
10424 if (RT_FAILURE(vrc))
10425 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10426 strIconData.jolt();
10427 config.machineUserData.ovIcon = strIconData;
10428 }
10429 else
10430 config.machineUserData.ovIcon.setNull();
10431
10432 if ( mData->mMachineState == MachineState_Saved
10433 || mData->mMachineState == MachineState_Restoring
10434 // when deleting a snapshot we may or may not have a saved state in the current state,
10435 // so let's not assert here please
10436 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10437 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10438 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10439 && (!mSSData->strStateFilePath.isEmpty())
10440 )
10441 )
10442 {
10443 Assert(!mSSData->strStateFilePath.isEmpty());
10444 /* try to make the file name relative to the settings file dir */
10445 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10446 }
10447 else
10448 {
10449 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10450 config.strStateFile.setNull();
10451 }
10452
10453 if (mData->mCurrentSnapshot)
10454 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10455 else
10456 config.uuidCurrentSnapshot.clear();
10457
10458 config.timeLastStateChange = mData->mLastStateChange;
10459 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10460 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10461
10462 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10463 if (FAILED(rc)) throw rc;
10464
10465 rc = saveStorageControllers(config.storageMachine);
10466 if (FAILED(rc)) throw rc;
10467
10468 // save machine's media registry if this is VirtualBox 4.0 or later
10469 if (config.canHaveOwnMediaRegistry())
10470 {
10471 // determine machine folder
10472 Utf8Str strMachineFolder = getSettingsFileFull();
10473 strMachineFolder.stripFilename();
10474 mParent->saveMediaRegistry(config.mediaRegistry,
10475 getId(), // only media with registry ID == machine UUID
10476 strMachineFolder);
10477 // this throws HRESULT
10478 }
10479
10480 // save snapshots
10481 rc = saveAllSnapshots(config);
10482 if (FAILED(rc)) throw rc;
10483}
10484
10485/**
10486 * Saves all snapshots of the machine into the given machine config file. Called
10487 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10488 * @param config
10489 * @return
10490 */
10491HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10492{
10493 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10494
10495 HRESULT rc = S_OK;
10496
10497 try
10498 {
10499 config.llFirstSnapshot.clear();
10500
10501 if (mData->mFirstSnapshot)
10502 {
10503 settings::Snapshot snapNew;
10504 config.llFirstSnapshot.push_back(snapNew);
10505
10506 // get reference to the fresh copy of the snapshot on the list and
10507 // work on that copy directly to avoid excessive copying later
10508 settings::Snapshot &snap = config.llFirstSnapshot.front();
10509
10510 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10511 if (FAILED(rc)) throw rc;
10512 }
10513
10514// if (mType == IsSessionMachine)
10515// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10516
10517 }
10518 catch (HRESULT err)
10519 {
10520 /* we assume that error info is set by the thrower */
10521 rc = err;
10522 }
10523 catch (...)
10524 {
10525 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10526 }
10527
10528 return rc;
10529}
10530
10531/**
10532 * Saves the VM hardware configuration. It is assumed that the
10533 * given node is empty.
10534 *
10535 * @param data Reference to the settings object for the hardware config.
10536 * @param pDbg Pointer to the settings object for the debugging config
10537 * which happens to live in mHWData.
10538 * @param pAutostart Pointer to the settings object for the autostart config
10539 * which happens to live in mHWData.
10540 */
10541HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10542 settings::Autostart *pAutostart)
10543{
10544 HRESULT rc = S_OK;
10545
10546 try
10547 {
10548 /* The hardware version attribute (optional).
10549 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10550 if ( mHWData->mHWVersion == "1"
10551 && mSSData->strStateFilePath.isEmpty()
10552 )
10553 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. */
10554
10555 data.strVersion = mHWData->mHWVersion;
10556 data.uuid = mHWData->mHardwareUUID;
10557
10558 // CPU
10559 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10560 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10561 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10562 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10563 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10564 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10565 data.fPAE = !!mHWData->mPAEEnabled;
10566 data.enmLongMode = mHWData->mLongMode;
10567 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10568 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10569
10570 /* Standard and Extended CPUID leafs. */
10571 data.llCpuIdLeafs.clear();
10572 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10573 {
10574 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10575 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10576 }
10577 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10578 {
10579 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10580 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10581 }
10582
10583 data.cCPUs = mHWData->mCPUCount;
10584 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10585 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10586
10587 data.llCpus.clear();
10588 if (data.fCpuHotPlug)
10589 {
10590 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10591 {
10592 if (mHWData->mCPUAttached[idx])
10593 {
10594 settings::Cpu cpu;
10595 cpu.ulId = idx;
10596 data.llCpus.push_back(cpu);
10597 }
10598 }
10599 }
10600
10601 // memory
10602 data.ulMemorySizeMB = mHWData->mMemorySize;
10603 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10604
10605 // firmware
10606 data.firmwareType = mHWData->mFirmwareType;
10607
10608 // HID
10609 data.pointingHIDType = mHWData->mPointingHIDType;
10610 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10611
10612 // chipset
10613 data.chipsetType = mHWData->mChipsetType;
10614
10615 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10616
10617 // HPET
10618 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10619
10620 // boot order
10621 data.mapBootOrder.clear();
10622 for (size_t i = 0;
10623 i < RT_ELEMENTS(mHWData->mBootOrder);
10624 ++i)
10625 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10626
10627 // display
10628 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10629 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10630 data.cMonitors = mHWData->mMonitorCount;
10631 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10632 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10633 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10634 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10635 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10636 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10637 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10638 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10639 {
10640 if (mHWData->maVideoCaptureScreens[i])
10641 ASMBitSet(&data.u64VideoCaptureScreens, i);
10642 else
10643 ASMBitClear(&data.u64VideoCaptureScreens, i);
10644 }
10645 /* store relative video capture file if possible */
10646 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10647
10648 /* VRDEServer settings (optional) */
10649 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10650 if (FAILED(rc)) throw rc;
10651
10652 /* BIOS (required) */
10653 rc = mBIOSSettings->saveSettings(data.biosSettings);
10654 if (FAILED(rc)) throw rc;
10655
10656 /* USB Controller (required) */
10657 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10658 it != mUSBControllers->end();
10659 ++it)
10660 {
10661 ComObjPtr<USBController> ctrl = *it;
10662 settings::USBController settingsCtrl;
10663
10664 settingsCtrl.strName = ctrl->getName();
10665 settingsCtrl.enmType = ctrl->getControllerType();
10666
10667 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10668 }
10669
10670 /* USB device filters (required) */
10671 rc = mUSBDeviceFilters->saveSettings(data.usbSettings);
10672 if (FAILED(rc)) throw rc;
10673
10674 /* Network adapters (required) */
10675 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10676 data.llNetworkAdapters.clear();
10677 /* Write out only the nominal number of network adapters for this
10678 * chipset type. Since Machine::commit() hasn't been called there
10679 * may be extra NIC settings in the vector. */
10680 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10681 {
10682 settings::NetworkAdapter nic;
10683 nic.ulSlot = slot;
10684 /* paranoia check... must not be NULL, but must not crash either. */
10685 if (mNetworkAdapters[slot])
10686 {
10687 rc = mNetworkAdapters[slot]->saveSettings(nic);
10688 if (FAILED(rc)) throw rc;
10689
10690 data.llNetworkAdapters.push_back(nic);
10691 }
10692 }
10693
10694 /* Serial ports */
10695 data.llSerialPorts.clear();
10696 for (ULONG slot = 0;
10697 slot < RT_ELEMENTS(mSerialPorts);
10698 ++slot)
10699 {
10700 settings::SerialPort s;
10701 s.ulSlot = slot;
10702 rc = mSerialPorts[slot]->saveSettings(s);
10703 if (FAILED(rc)) return rc;
10704
10705 data.llSerialPorts.push_back(s);
10706 }
10707
10708 /* Parallel ports */
10709 data.llParallelPorts.clear();
10710 for (ULONG slot = 0;
10711 slot < RT_ELEMENTS(mParallelPorts);
10712 ++slot)
10713 {
10714 settings::ParallelPort p;
10715 p.ulSlot = slot;
10716 rc = mParallelPorts[slot]->saveSettings(p);
10717 if (FAILED(rc)) return rc;
10718
10719 data.llParallelPorts.push_back(p);
10720 }
10721
10722 /* Audio adapter */
10723 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10724 if (FAILED(rc)) return rc;
10725
10726 /* Shared folders */
10727 data.llSharedFolders.clear();
10728 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10729 it != mHWData->mSharedFolders.end();
10730 ++it)
10731 {
10732 SharedFolder *pSF = *it;
10733 AutoCaller sfCaller(pSF);
10734 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10735 settings::SharedFolder sf;
10736 sf.strName = pSF->getName();
10737 sf.strHostPath = pSF->getHostPath();
10738 sf.fWritable = !!pSF->isWritable();
10739 sf.fAutoMount = !!pSF->isAutoMounted();
10740
10741 data.llSharedFolders.push_back(sf);
10742 }
10743
10744 // clipboard
10745 data.clipboardMode = mHWData->mClipboardMode;
10746
10747 // drag'n'drop
10748 data.dragAndDropMode = mHWData->mDragAndDropMode;
10749
10750 /* Guest */
10751 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10752
10753 // IO settings
10754 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10755 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10756
10757 /* BandwidthControl (required) */
10758 rc = mBandwidthControl->saveSettings(data.ioSettings);
10759 if (FAILED(rc)) throw rc;
10760
10761 /* Host PCI devices */
10762 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10763 it != mHWData->mPCIDeviceAssignments.end();
10764 ++it)
10765 {
10766 ComObjPtr<PCIDeviceAttachment> pda = *it;
10767 settings::HostPCIDeviceAttachment hpda;
10768
10769 rc = pda->saveSettings(hpda);
10770 if (FAILED(rc)) throw rc;
10771
10772 data.pciAttachments.push_back(hpda);
10773 }
10774
10775
10776 // guest properties
10777 data.llGuestProperties.clear();
10778#ifdef VBOX_WITH_GUEST_PROPS
10779 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10780 it != mHWData->mGuestProperties.end();
10781 ++it)
10782 {
10783 HWData::GuestProperty property = it->second;
10784
10785 /* Remove transient guest properties at shutdown unless we
10786 * are saving state */
10787 if ( ( mData->mMachineState == MachineState_PoweredOff
10788 || mData->mMachineState == MachineState_Aborted
10789 || mData->mMachineState == MachineState_Teleported)
10790 && ( property.mFlags & guestProp::TRANSIENT
10791 || property.mFlags & guestProp::TRANSRESET))
10792 continue;
10793 settings::GuestProperty prop;
10794 prop.strName = it->first;
10795 prop.strValue = property.strValue;
10796 prop.timestamp = property.mTimestamp;
10797 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10798 guestProp::writeFlags(property.mFlags, szFlags);
10799 prop.strFlags = szFlags;
10800
10801 data.llGuestProperties.push_back(prop);
10802 }
10803
10804 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10805 /* I presume this doesn't require a backup(). */
10806 mData->mGuestPropertiesModified = FALSE;
10807#endif /* VBOX_WITH_GUEST_PROPS defined */
10808
10809 *pDbg = mHWData->mDebugging;
10810 *pAutostart = mHWData->mAutostart;
10811
10812 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10813 }
10814 catch(std::bad_alloc &)
10815 {
10816 return E_OUTOFMEMORY;
10817 }
10818
10819 AssertComRC(rc);
10820 return rc;
10821}
10822
10823/**
10824 * Saves the storage controller configuration.
10825 *
10826 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10827 */
10828HRESULT Machine::saveStorageControllers(settings::Storage &data)
10829{
10830 data.llStorageControllers.clear();
10831
10832 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10833 it != mStorageControllers->end();
10834 ++it)
10835 {
10836 HRESULT rc;
10837 ComObjPtr<StorageController> pCtl = *it;
10838
10839 settings::StorageController ctl;
10840 ctl.strName = pCtl->getName();
10841 ctl.controllerType = pCtl->getControllerType();
10842 ctl.storageBus = pCtl->getStorageBus();
10843 ctl.ulInstance = pCtl->getInstance();
10844 ctl.fBootable = pCtl->getBootable();
10845
10846 /* Save the port count. */
10847 ULONG portCount;
10848 rc = pCtl->COMGETTER(PortCount)(&portCount);
10849 ComAssertComRCRet(rc, rc);
10850 ctl.ulPortCount = portCount;
10851
10852 /* Save fUseHostIOCache */
10853 BOOL fUseHostIOCache;
10854 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10855 ComAssertComRCRet(rc, rc);
10856 ctl.fUseHostIOCache = !!fUseHostIOCache;
10857
10858 /* Save IDE emulation settings. */
10859 if (ctl.controllerType == StorageControllerType_IntelAhci)
10860 {
10861 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10862 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10863 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10864 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10865 )
10866 ComAssertComRCRet(rc, rc);
10867 }
10868
10869 /* save the devices now. */
10870 rc = saveStorageDevices(pCtl, ctl);
10871 ComAssertComRCRet(rc, rc);
10872
10873 data.llStorageControllers.push_back(ctl);
10874 }
10875
10876 return S_OK;
10877}
10878
10879/**
10880 * Saves the hard disk configuration.
10881 */
10882HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10883 settings::StorageController &data)
10884{
10885 MediaData::AttachmentList atts;
10886
10887 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10888 if (FAILED(rc)) return rc;
10889
10890 data.llAttachedDevices.clear();
10891 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10892 it != atts.end();
10893 ++it)
10894 {
10895 settings::AttachedDevice dev;
10896
10897 MediumAttachment *pAttach = *it;
10898 Medium *pMedium = pAttach->getMedium();
10899
10900 dev.deviceType = pAttach->getType();
10901 dev.lPort = pAttach->getPort();
10902 dev.lDevice = pAttach->getDevice();
10903 dev.fPassThrough = pAttach->getPassthrough();
10904 dev.fHotPluggable = pAttach->getHotPluggable();
10905 if (pMedium)
10906 {
10907 if (pMedium->isHostDrive())
10908 dev.strHostDriveSrc = pMedium->getLocationFull();
10909 else
10910 dev.uuid = pMedium->getId();
10911 dev.fTempEject = pAttach->getTempEject();
10912 dev.fNonRotational = pAttach->getNonRotational();
10913 dev.fDiscard = pAttach->getDiscard();
10914 }
10915
10916 dev.strBwGroup = pAttach->getBandwidthGroup();
10917
10918 data.llAttachedDevices.push_back(dev);
10919 }
10920
10921 return S_OK;
10922}
10923
10924/**
10925 * Saves machine state settings as defined by aFlags
10926 * (SaveSTS_* values).
10927 *
10928 * @param aFlags Combination of SaveSTS_* flags.
10929 *
10930 * @note Locks objects for writing.
10931 */
10932HRESULT Machine::saveStateSettings(int aFlags)
10933{
10934 if (aFlags == 0)
10935 return S_OK;
10936
10937 AutoCaller autoCaller(this);
10938 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10939
10940 /* This object's write lock is also necessary to serialize file access
10941 * (prevent concurrent reads and writes) */
10942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10943
10944 HRESULT rc = S_OK;
10945
10946 Assert(mData->pMachineConfigFile);
10947
10948 try
10949 {
10950 if (aFlags & SaveSTS_CurStateModified)
10951 mData->pMachineConfigFile->fCurrentStateModified = true;
10952
10953 if (aFlags & SaveSTS_StateFilePath)
10954 {
10955 if (!mSSData->strStateFilePath.isEmpty())
10956 /* try to make the file name relative to the settings file dir */
10957 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10958 else
10959 mData->pMachineConfigFile->strStateFile.setNull();
10960 }
10961
10962 if (aFlags & SaveSTS_StateTimeStamp)
10963 {
10964 Assert( mData->mMachineState != MachineState_Aborted
10965 || mSSData->strStateFilePath.isEmpty());
10966
10967 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10968
10969 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10970//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10971 }
10972
10973 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10974 }
10975 catch (...)
10976 {
10977 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10978 }
10979
10980 return rc;
10981}
10982
10983/**
10984 * Ensures that the given medium is added to a media registry. If this machine
10985 * was created with 4.0 or later, then the machine registry is used. Otherwise
10986 * the global VirtualBox media registry is used.
10987 *
10988 * Caller must NOT hold machine lock, media tree or any medium locks!
10989 *
10990 * @param pMedium
10991 */
10992void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10993{
10994 /* Paranoia checks: do not hold machine or media tree locks. */
10995 AssertReturnVoid(!isWriteLockOnCurrentThread());
10996 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10997
10998 ComObjPtr<Medium> pBase;
10999 {
11000 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11001 pBase = pMedium->getBase();
11002 }
11003
11004 /* Paranoia checks: do not hold medium locks. */
11005 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11006 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11007
11008 // decide which medium registry to use now that the medium is attached:
11009 Guid uuid;
11010 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
11011 // machine XML is VirtualBox 4.0 or higher:
11012 uuid = getId(); // machine UUID
11013 else
11014 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
11015
11016 if (pMedium->addRegistry(uuid, false /* fRecurse */))
11017 mParent->markRegistryModified(uuid);
11018
11019 /* For more complex hard disk structures it can happen that the base
11020 * medium isn't yet associated with any medium registry. Do that now. */
11021 if (pMedium != pBase)
11022 {
11023 if (pBase->addRegistry(uuid, true /* fRecurse */))
11024 mParent->markRegistryModified(uuid);
11025 }
11026}
11027
11028/**
11029 * Creates differencing hard disks for all normal hard disks attached to this
11030 * machine and a new set of attachments to refer to created disks.
11031 *
11032 * Used when taking a snapshot or when deleting the current state. Gets called
11033 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11034 *
11035 * This method assumes that mMediaData contains the original hard disk attachments
11036 * it needs to create diffs for. On success, these attachments will be replaced
11037 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
11038 * called to delete created diffs which will also rollback mMediaData and restore
11039 * whatever was backed up before calling this method.
11040 *
11041 * Attachments with non-normal hard disks are left as is.
11042 *
11043 * If @a aOnline is @c false then the original hard disks that require implicit
11044 * diffs will be locked for reading. Otherwise it is assumed that they are
11045 * already locked for writing (when the VM was started). Note that in the latter
11046 * case it is responsibility of the caller to lock the newly created diffs for
11047 * writing if this method succeeds.
11048 *
11049 * @param aProgress Progress object to run (must contain at least as
11050 * many operations left as the number of hard disks
11051 * attached).
11052 * @param aOnline Whether the VM was online prior to this operation.
11053 *
11054 * @note The progress object is not marked as completed, neither on success nor
11055 * on failure. This is a responsibility of the caller.
11056 *
11057 * @note Locks this object and the media tree for writing.
11058 */
11059HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
11060 ULONG aWeight,
11061 bool aOnline)
11062{
11063 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11064
11065 AutoCaller autoCaller(this);
11066 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11067
11068 AutoMultiWriteLock2 alock(this->lockHandle(),
11069 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11070
11071 /* must be in a protective state because we release the lock below */
11072 AssertReturn( mData->mMachineState == MachineState_Saving
11073 || mData->mMachineState == MachineState_LiveSnapshotting
11074 || mData->mMachineState == MachineState_RestoringSnapshot
11075 || mData->mMachineState == MachineState_DeletingSnapshot
11076 , E_FAIL);
11077
11078 HRESULT rc = S_OK;
11079
11080 // use appropriate locked media map (online or offline)
11081 MediumLockListMap lockedMediaOffline;
11082 MediumLockListMap *lockedMediaMap;
11083 if (aOnline)
11084 lockedMediaMap = &mData->mSession.mLockedMedia;
11085 else
11086 lockedMediaMap = &lockedMediaOffline;
11087
11088 try
11089 {
11090 if (!aOnline)
11091 {
11092 /* lock all attached hard disks early to detect "in use"
11093 * situations before creating actual diffs */
11094 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11095 it != mMediaData->mAttachments.end();
11096 ++it)
11097 {
11098 MediumAttachment* pAtt = *it;
11099 if (pAtt->getType() == DeviceType_HardDisk)
11100 {
11101 Medium* pMedium = pAtt->getMedium();
11102 Assert(pMedium);
11103
11104 MediumLockList *pMediumLockList(new MediumLockList());
11105 alock.release();
11106 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11107 false /* fMediumLockWrite */,
11108 NULL,
11109 *pMediumLockList);
11110 alock.acquire();
11111 if (FAILED(rc))
11112 {
11113 delete pMediumLockList;
11114 throw rc;
11115 }
11116 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11117 if (FAILED(rc))
11118 {
11119 throw setError(rc,
11120 tr("Collecting locking information for all attached media failed"));
11121 }
11122 }
11123 }
11124
11125 /* Now lock all media. If this fails, nothing is locked. */
11126 alock.release();
11127 rc = lockedMediaMap->Lock();
11128 alock.acquire();
11129 if (FAILED(rc))
11130 {
11131 throw setError(rc,
11132 tr("Locking of attached media failed"));
11133 }
11134 }
11135
11136 /* remember the current list (note that we don't use backup() since
11137 * mMediaData may be already backed up) */
11138 MediaData::AttachmentList atts = mMediaData->mAttachments;
11139
11140 /* start from scratch */
11141 mMediaData->mAttachments.clear();
11142
11143 /* go through remembered attachments and create diffs for normal hard
11144 * disks and attach them */
11145 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11146 it != atts.end();
11147 ++it)
11148 {
11149 MediumAttachment* pAtt = *it;
11150
11151 DeviceType_T devType = pAtt->getType();
11152 Medium* pMedium = pAtt->getMedium();
11153
11154 if ( devType != DeviceType_HardDisk
11155 || pMedium == NULL
11156 || pMedium->getType() != MediumType_Normal)
11157 {
11158 /* copy the attachment as is */
11159
11160 /** @todo the progress object created in Console::TakeSnaphot
11161 * only expects operations for hard disks. Later other
11162 * device types need to show up in the progress as well. */
11163 if (devType == DeviceType_HardDisk)
11164 {
11165 if (pMedium == NULL)
11166 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11167 aWeight); // weight
11168 else
11169 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11170 pMedium->getBase()->getName().c_str()).raw(),
11171 aWeight); // weight
11172 }
11173
11174 mMediaData->mAttachments.push_back(pAtt);
11175 continue;
11176 }
11177
11178 /* need a diff */
11179 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11180 pMedium->getBase()->getName().c_str()).raw(),
11181 aWeight); // weight
11182
11183 Utf8Str strFullSnapshotFolder;
11184 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11185
11186 ComObjPtr<Medium> diff;
11187 diff.createObject();
11188 // store the diff in the same registry as the parent
11189 // (this cannot fail here because we can't create implicit diffs for
11190 // unregistered images)
11191 Guid uuidRegistryParent;
11192 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
11193 Assert(fInRegistry); NOREF(fInRegistry);
11194 rc = diff->init(mParent,
11195 pMedium->getPreferredDiffFormat(),
11196 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11197 uuidRegistryParent);
11198 if (FAILED(rc)) throw rc;
11199
11200 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11201 * the push_back? Looks like we're going to release medium with the
11202 * wrong kind of lock (general issue with if we fail anywhere at all)
11203 * and an orphaned VDI in the snapshots folder. */
11204
11205 /* update the appropriate lock list */
11206 MediumLockList *pMediumLockList;
11207 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11208 AssertComRCThrowRC(rc);
11209 if (aOnline)
11210 {
11211 alock.release();
11212 /* The currently attached medium will be read-only, change
11213 * the lock type to read. */
11214 rc = pMediumLockList->Update(pMedium, false);
11215 alock.acquire();
11216 AssertComRCThrowRC(rc);
11217 }
11218
11219 /* release the locks before the potentially lengthy operation */
11220 alock.release();
11221 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
11222 pMediumLockList,
11223 NULL /* aProgress */,
11224 true /* aWait */);
11225 alock.acquire();
11226 if (FAILED(rc)) throw rc;
11227
11228 /* actual lock list update is done in Medium::commitMedia */
11229
11230 rc = diff->addBackReference(mData->mUuid);
11231 AssertComRCThrowRC(rc);
11232
11233 /* add a new attachment */
11234 ComObjPtr<MediumAttachment> attachment;
11235 attachment.createObject();
11236 rc = attachment->init(this,
11237 diff,
11238 pAtt->getControllerName(),
11239 pAtt->getPort(),
11240 pAtt->getDevice(),
11241 DeviceType_HardDisk,
11242 true /* aImplicit */,
11243 false /* aPassthrough */,
11244 false /* aTempEject */,
11245 pAtt->getNonRotational(),
11246 pAtt->getDiscard(),
11247 pAtt->getHotPluggable(),
11248 pAtt->getBandwidthGroup());
11249 if (FAILED(rc)) throw rc;
11250
11251 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11252 AssertComRCThrowRC(rc);
11253 mMediaData->mAttachments.push_back(attachment);
11254 }
11255 }
11256 catch (HRESULT aRC) { rc = aRC; }
11257
11258 /* unlock all hard disks we locked when there is no VM */
11259 if (!aOnline)
11260 {
11261 ErrorInfoKeeper eik;
11262
11263 HRESULT rc1 = lockedMediaMap->Clear();
11264 AssertComRC(rc1);
11265 }
11266
11267 return rc;
11268}
11269
11270/**
11271 * Deletes implicit differencing hard disks created either by
11272 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11273 *
11274 * Note that to delete hard disks created by #AttachDevice() this method is
11275 * called from #fixupMedia() when the changes are rolled back.
11276 *
11277 * @note Locks this object and the media tree for writing.
11278 */
11279HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11280{
11281 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11282
11283 AutoCaller autoCaller(this);
11284 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11285
11286 AutoMultiWriteLock2 alock(this->lockHandle(),
11287 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11288
11289 /* We absolutely must have backed up state. */
11290 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11291
11292 /* Check if there are any implicitly created diff images. */
11293 bool fImplicitDiffs = false;
11294 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11295 it != mMediaData->mAttachments.end();
11296 ++it)
11297 {
11298 const ComObjPtr<MediumAttachment> &pAtt = *it;
11299 if (pAtt->isImplicit())
11300 {
11301 fImplicitDiffs = true;
11302 break;
11303 }
11304 }
11305 /* If there is nothing to do, leave early. This saves lots of image locking
11306 * effort. It also avoids a MachineStateChanged event without real reason.
11307 * This is important e.g. when loading a VM config, because there should be
11308 * no events. Otherwise API clients can become thoroughly confused for
11309 * inaccessible VMs (the code for loading VM configs uses this method for
11310 * cleanup if the config makes no sense), as they take such events as an
11311 * indication that the VM is alive, and they would force the VM config to
11312 * be reread, leading to an endless loop. */
11313 if (!fImplicitDiffs)
11314 return S_OK;
11315
11316 HRESULT rc = S_OK;
11317 MachineState_T oldState = mData->mMachineState;
11318
11319 /* will release the lock before the potentially lengthy operation,
11320 * so protect with the special state (unless already protected) */
11321 if ( oldState != MachineState_Saving
11322 && oldState != MachineState_LiveSnapshotting
11323 && oldState != MachineState_RestoringSnapshot
11324 && oldState != MachineState_DeletingSnapshot
11325 && oldState != MachineState_DeletingSnapshotOnline
11326 && oldState != MachineState_DeletingSnapshotPaused
11327 )
11328 setMachineState(MachineState_SettingUp);
11329
11330 // use appropriate locked media map (online or offline)
11331 MediumLockListMap lockedMediaOffline;
11332 MediumLockListMap *lockedMediaMap;
11333 if (aOnline)
11334 lockedMediaMap = &mData->mSession.mLockedMedia;
11335 else
11336 lockedMediaMap = &lockedMediaOffline;
11337
11338 try
11339 {
11340 if (!aOnline)
11341 {
11342 /* lock all attached hard disks early to detect "in use"
11343 * situations before deleting actual diffs */
11344 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11345 it != mMediaData->mAttachments.end();
11346 ++it)
11347 {
11348 MediumAttachment* pAtt = *it;
11349 if (pAtt->getType() == DeviceType_HardDisk)
11350 {
11351 Medium* pMedium = pAtt->getMedium();
11352 Assert(pMedium);
11353
11354 MediumLockList *pMediumLockList(new MediumLockList());
11355 alock.release();
11356 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11357 false /* fMediumLockWrite */,
11358 NULL,
11359 *pMediumLockList);
11360 alock.acquire();
11361
11362 if (FAILED(rc))
11363 {
11364 delete pMediumLockList;
11365 throw rc;
11366 }
11367
11368 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11369 if (FAILED(rc))
11370 throw rc;
11371 }
11372 }
11373
11374 if (FAILED(rc))
11375 throw rc;
11376 } // end of offline
11377
11378 /* Lock lists are now up to date and include implicitly created media */
11379
11380 /* Go through remembered attachments and delete all implicitly created
11381 * diffs and fix up the attachment information */
11382 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11383 MediaData::AttachmentList implicitAtts;
11384 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11385 it != mMediaData->mAttachments.end();
11386 ++it)
11387 {
11388 ComObjPtr<MediumAttachment> pAtt = *it;
11389 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11390 if (pMedium.isNull())
11391 continue;
11392
11393 // Implicit attachments go on the list for deletion and back references are removed.
11394 if (pAtt->isImplicit())
11395 {
11396 /* Deassociate and mark for deletion */
11397 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11398 rc = pMedium->removeBackReference(mData->mUuid);
11399 if (FAILED(rc))
11400 throw rc;
11401 implicitAtts.push_back(pAtt);
11402 continue;
11403 }
11404
11405 /* Was this medium attached before? */
11406 if (!findAttachment(oldAtts, pMedium))
11407 {
11408 /* no: de-associate */
11409 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11410 rc = pMedium->removeBackReference(mData->mUuid);
11411 if (FAILED(rc))
11412 throw rc;
11413 continue;
11414 }
11415 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11416 }
11417
11418 /* If there are implicit attachments to delete, throw away the lock
11419 * map contents (which will unlock all media) since the medium
11420 * attachments will be rolled back. Below we need to completely
11421 * recreate the lock map anyway since it is infinitely complex to
11422 * do this incrementally (would need reconstructing each attachment
11423 * change, which would be extremely hairy). */
11424 if (implicitAtts.size() != 0)
11425 {
11426 ErrorInfoKeeper eik;
11427
11428 HRESULT rc1 = lockedMediaMap->Clear();
11429 AssertComRC(rc1);
11430 }
11431
11432 /* rollback hard disk changes */
11433 mMediaData.rollback();
11434
11435 MultiResult mrc(S_OK);
11436
11437 // Delete unused implicit diffs.
11438 if (implicitAtts.size() != 0)
11439 {
11440 alock.release();
11441
11442 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11443 it != implicitAtts.end();
11444 ++it)
11445 {
11446 // Remove medium associated with this attachment.
11447 ComObjPtr<MediumAttachment> pAtt = *it;
11448 Assert(pAtt);
11449 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11450 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11451 Assert(pMedium);
11452
11453 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11454 // continue on delete failure, just collect error messages
11455 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11456 mrc = rc;
11457 }
11458
11459 alock.acquire();
11460
11461 /* if there is a VM recreate media lock map as mentioned above,
11462 * otherwise it is a waste of time and we leave things unlocked */
11463 if (aOnline)
11464 {
11465 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11466 /* must never be NULL, but better safe than sorry */
11467 if (!pMachine.isNull())
11468 {
11469 alock.release();
11470 rc = mData->mSession.mMachine->lockMedia();
11471 alock.acquire();
11472 if (FAILED(rc))
11473 throw rc;
11474 }
11475 }
11476 }
11477 }
11478 catch (HRESULT aRC) {rc = aRC;}
11479
11480 if (mData->mMachineState == MachineState_SettingUp)
11481 setMachineState(oldState);
11482
11483 /* unlock all hard disks we locked when there is no VM */
11484 if (!aOnline)
11485 {
11486 ErrorInfoKeeper eik;
11487
11488 HRESULT rc1 = lockedMediaMap->Clear();
11489 AssertComRC(rc1);
11490 }
11491
11492 return rc;
11493}
11494
11495
11496/**
11497 * Looks through the given list of media attachments for one with the given parameters
11498 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11499 * can be searched as well if needed.
11500 *
11501 * @param list
11502 * @param aControllerName
11503 * @param aControllerPort
11504 * @param aDevice
11505 * @return
11506 */
11507MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11508 IN_BSTR aControllerName,
11509 LONG aControllerPort,
11510 LONG aDevice)
11511{
11512 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11513 it != ll.end();
11514 ++it)
11515 {
11516 MediumAttachment *pAttach = *it;
11517 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11518 return pAttach;
11519 }
11520
11521 return NULL;
11522}
11523
11524/**
11525 * Looks through the given list of media attachments for one with the given parameters
11526 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11527 * can be searched as well if needed.
11528 *
11529 * @param list
11530 * @param aControllerName
11531 * @param aControllerPort
11532 * @param aDevice
11533 * @return
11534 */
11535MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11536 ComObjPtr<Medium> pMedium)
11537{
11538 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11539 it != ll.end();
11540 ++it)
11541 {
11542 MediumAttachment *pAttach = *it;
11543 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11544 if (pMediumThis == pMedium)
11545 return pAttach;
11546 }
11547
11548 return NULL;
11549}
11550
11551/**
11552 * Looks through the given list of media attachments for one with the given parameters
11553 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11554 * can be searched as well if needed.
11555 *
11556 * @param list
11557 * @param aControllerName
11558 * @param aControllerPort
11559 * @param aDevice
11560 * @return
11561 */
11562MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11563 Guid &id)
11564{
11565 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11566 it != ll.end();
11567 ++it)
11568 {
11569 MediumAttachment *pAttach = *it;
11570 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11571 if (pMediumThis->getId() == id)
11572 return pAttach;
11573 }
11574
11575 return NULL;
11576}
11577
11578/**
11579 * Main implementation for Machine::DetachDevice. This also gets called
11580 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11581 *
11582 * @param pAttach Medium attachment to detach.
11583 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11584 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11585 * @return
11586 */
11587HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11588 AutoWriteLock &writeLock,
11589 Snapshot *pSnapshot)
11590{
11591 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11592 DeviceType_T mediumType = pAttach->getType();
11593
11594 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11595
11596 if (pAttach->isImplicit())
11597 {
11598 /* attempt to implicitly delete the implicitly created diff */
11599
11600 /// @todo move the implicit flag from MediumAttachment to Medium
11601 /// and forbid any hard disk operation when it is implicit. Or maybe
11602 /// a special media state for it to make it even more simple.
11603
11604 Assert(mMediaData.isBackedUp());
11605
11606 /* will release the lock before the potentially lengthy operation, so
11607 * protect with the special state */
11608 MachineState_T oldState = mData->mMachineState;
11609 setMachineState(MachineState_SettingUp);
11610
11611 writeLock.release();
11612
11613 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11614 true /*aWait*/);
11615
11616 writeLock.acquire();
11617
11618 setMachineState(oldState);
11619
11620 if (FAILED(rc)) return rc;
11621 }
11622
11623 setModified(IsModified_Storage);
11624 mMediaData.backup();
11625 mMediaData->mAttachments.remove(pAttach);
11626
11627 if (!oldmedium.isNull())
11628 {
11629 // if this is from a snapshot, do not defer detachment to commitMedia()
11630 if (pSnapshot)
11631 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11632 // else if non-hard disk media, do not defer detachment to commitMedia() either
11633 else if (mediumType != DeviceType_HardDisk)
11634 oldmedium->removeBackReference(mData->mUuid);
11635 }
11636
11637 return S_OK;
11638}
11639
11640/**
11641 * Goes thru all media of the given list and
11642 *
11643 * 1) calls detachDevice() on each of them for this machine and
11644 * 2) adds all Medium objects found in the process to the given list,
11645 * depending on cleanupMode.
11646 *
11647 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11648 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11649 * media to the list.
11650 *
11651 * This gets called from Machine::Unregister, both for the actual Machine and
11652 * the SnapshotMachine objects that might be found in the snapshots.
11653 *
11654 * Requires caller and locking. The machine lock must be passed in because it
11655 * will be passed on to detachDevice which needs it for temporary unlocking.
11656 *
11657 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11658 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11659 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11660 * otherwise no media get added.
11661 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11662 * @return
11663 */
11664HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11665 Snapshot *pSnapshot,
11666 CleanupMode_T cleanupMode,
11667 MediaList &llMedia)
11668{
11669 Assert(isWriteLockOnCurrentThread());
11670
11671 HRESULT rc;
11672
11673 // make a temporary list because detachDevice invalidates iterators into
11674 // mMediaData->mAttachments
11675 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11676
11677 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11678 it != llAttachments2.end();
11679 ++it)
11680 {
11681 ComObjPtr<MediumAttachment> &pAttach = *it;
11682 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11683
11684 if (!pMedium.isNull())
11685 {
11686 AutoCaller mac(pMedium);
11687 if (FAILED(mac.rc())) return mac.rc();
11688 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11689 DeviceType_T devType = pMedium->getDeviceType();
11690 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11691 && devType == DeviceType_HardDisk)
11692 || (cleanupMode == CleanupMode_Full)
11693 )
11694 {
11695 llMedia.push_back(pMedium);
11696 ComObjPtr<Medium> pParent = pMedium->getParent();
11697 /*
11698 * Search for medias which are not attached to any machine, but
11699 * in the chain to an attached disk. Mediums are only consided
11700 * if they are:
11701 * - have only one child
11702 * - no references to any machines
11703 * - are of normal medium type
11704 */
11705 while (!pParent.isNull())
11706 {
11707 AutoCaller mac1(pParent);
11708 if (FAILED(mac1.rc())) return mac1.rc();
11709 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11710 if (pParent->getChildren().size() == 1)
11711 {
11712 if ( pParent->getMachineBackRefCount() == 0
11713 && pParent->getType() == MediumType_Normal
11714 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11715 llMedia.push_back(pParent);
11716 }
11717 else
11718 break;
11719 pParent = pParent->getParent();
11720 }
11721 }
11722 }
11723
11724 // real machine: then we need to use the proper method
11725 rc = detachDevice(pAttach, writeLock, pSnapshot);
11726
11727 if (FAILED(rc))
11728 return rc;
11729 }
11730
11731 return S_OK;
11732}
11733
11734/**
11735 * Perform deferred hard disk detachments.
11736 *
11737 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11738 * backed up).
11739 *
11740 * If @a aOnline is @c true then this method will also unlock the old hard disks
11741 * for which the new implicit diffs were created and will lock these new diffs for
11742 * writing.
11743 *
11744 * @param aOnline Whether the VM was online prior to this operation.
11745 *
11746 * @note Locks this object for writing!
11747 */
11748void Machine::commitMedia(bool aOnline /*= false*/)
11749{
11750 AutoCaller autoCaller(this);
11751 AssertComRCReturnVoid(autoCaller.rc());
11752
11753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11754
11755 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11756
11757 HRESULT rc = S_OK;
11758
11759 /* no attach/detach operations -- nothing to do */
11760 if (!mMediaData.isBackedUp())
11761 return;
11762
11763 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11764 bool fMediaNeedsLocking = false;
11765
11766 /* enumerate new attachments */
11767 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11768 it != mMediaData->mAttachments.end();
11769 ++it)
11770 {
11771 MediumAttachment *pAttach = *it;
11772
11773 pAttach->commit();
11774
11775 Medium* pMedium = pAttach->getMedium();
11776 bool fImplicit = pAttach->isImplicit();
11777
11778 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11779 (pMedium) ? pMedium->getName().c_str() : "NULL",
11780 fImplicit));
11781
11782 /** @todo convert all this Machine-based voodoo to MediumAttachment
11783 * based commit logic. */
11784 if (fImplicit)
11785 {
11786 /* convert implicit attachment to normal */
11787 pAttach->setImplicit(false);
11788
11789 if ( aOnline
11790 && pMedium
11791 && pAttach->getType() == DeviceType_HardDisk
11792 )
11793 {
11794 ComObjPtr<Medium> parent = pMedium->getParent();
11795 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11796
11797 /* update the appropriate lock list */
11798 MediumLockList *pMediumLockList;
11799 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11800 AssertComRC(rc);
11801 if (pMediumLockList)
11802 {
11803 /* unlock if there's a need to change the locking */
11804 if (!fMediaNeedsLocking)
11805 {
11806 rc = mData->mSession.mLockedMedia.Unlock();
11807 AssertComRC(rc);
11808 fMediaNeedsLocking = true;
11809 }
11810 rc = pMediumLockList->Update(parent, false);
11811 AssertComRC(rc);
11812 rc = pMediumLockList->Append(pMedium, true);
11813 AssertComRC(rc);
11814 }
11815 }
11816
11817 continue;
11818 }
11819
11820 if (pMedium)
11821 {
11822 /* was this medium attached before? */
11823 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11824 oldIt != oldAtts.end();
11825 ++oldIt)
11826 {
11827 MediumAttachment *pOldAttach = *oldIt;
11828 if (pOldAttach->getMedium() == pMedium)
11829 {
11830 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11831
11832 /* yes: remove from old to avoid de-association */
11833 oldAtts.erase(oldIt);
11834 break;
11835 }
11836 }
11837 }
11838 }
11839
11840 /* enumerate remaining old attachments and de-associate from the
11841 * current machine state */
11842 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11843 it != oldAtts.end();
11844 ++it)
11845 {
11846 MediumAttachment *pAttach = *it;
11847 Medium* pMedium = pAttach->getMedium();
11848
11849 /* Detach only hard disks, since DVD/floppy media is detached
11850 * instantly in MountMedium. */
11851 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11852 {
11853 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11854
11855 /* now de-associate from the current machine state */
11856 rc = pMedium->removeBackReference(mData->mUuid);
11857 AssertComRC(rc);
11858
11859 if (aOnline)
11860 {
11861 /* unlock since medium is not used anymore */
11862 MediumLockList *pMediumLockList;
11863 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11864 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11865 {
11866 /* this happens for online snapshots, there the attachment
11867 * is changing, but only to a diff image created under
11868 * the old one, so there is no separate lock list */
11869 Assert(!pMediumLockList);
11870 }
11871 else
11872 {
11873 AssertComRC(rc);
11874 if (pMediumLockList)
11875 {
11876 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11877 AssertComRC(rc);
11878 }
11879 }
11880 }
11881 }
11882 }
11883
11884 /* take media locks again so that the locking state is consistent */
11885 if (fMediaNeedsLocking)
11886 {
11887 Assert(aOnline);
11888 rc = mData->mSession.mLockedMedia.Lock();
11889 AssertComRC(rc);
11890 }
11891
11892 /* commit the hard disk changes */
11893 mMediaData.commit();
11894
11895 if (isSessionMachine())
11896 {
11897 /*
11898 * Update the parent machine to point to the new owner.
11899 * This is necessary because the stored parent will point to the
11900 * session machine otherwise and cause crashes or errors later
11901 * when the session machine gets invalid.
11902 */
11903 /** @todo Change the MediumAttachment class to behave like any other
11904 * class in this regard by creating peer MediumAttachment
11905 * objects for session machines and share the data with the peer
11906 * machine.
11907 */
11908 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11909 it != mMediaData->mAttachments.end();
11910 ++it)
11911 {
11912 (*it)->updateParentMachine(mPeer);
11913 }
11914
11915 /* attach new data to the primary machine and reshare it */
11916 mPeer->mMediaData.attach(mMediaData);
11917 }
11918
11919 return;
11920}
11921
11922/**
11923 * Perform deferred deletion of implicitly created diffs.
11924 *
11925 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11926 * backed up).
11927 *
11928 * @note Locks this object for writing!
11929 */
11930void Machine::rollbackMedia()
11931{
11932 AutoCaller autoCaller(this);
11933 AssertComRCReturnVoid(autoCaller.rc());
11934
11935 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11936 LogFlowThisFunc(("Entering rollbackMedia\n"));
11937
11938 HRESULT rc = S_OK;
11939
11940 /* no attach/detach operations -- nothing to do */
11941 if (!mMediaData.isBackedUp())
11942 return;
11943
11944 /* enumerate new attachments */
11945 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11946 it != mMediaData->mAttachments.end();
11947 ++it)
11948 {
11949 MediumAttachment *pAttach = *it;
11950 /* Fix up the backrefs for DVD/floppy media. */
11951 if (pAttach->getType() != DeviceType_HardDisk)
11952 {
11953 Medium* pMedium = pAttach->getMedium();
11954 if (pMedium)
11955 {
11956 rc = pMedium->removeBackReference(mData->mUuid);
11957 AssertComRC(rc);
11958 }
11959 }
11960
11961 (*it)->rollback();
11962
11963 pAttach = *it;
11964 /* Fix up the backrefs for DVD/floppy media. */
11965 if (pAttach->getType() != DeviceType_HardDisk)
11966 {
11967 Medium* pMedium = pAttach->getMedium();
11968 if (pMedium)
11969 {
11970 rc = pMedium->addBackReference(mData->mUuid);
11971 AssertComRC(rc);
11972 }
11973 }
11974 }
11975
11976 /** @todo convert all this Machine-based voodoo to MediumAttachment
11977 * based rollback logic. */
11978 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11979
11980 return;
11981}
11982
11983/**
11984 * Returns true if the settings file is located in the directory named exactly
11985 * as the machine; this means, among other things, that the machine directory
11986 * should be auto-renamed.
11987 *
11988 * @param aSettingsDir if not NULL, the full machine settings file directory
11989 * name will be assigned there.
11990 *
11991 * @note Doesn't lock anything.
11992 * @note Not thread safe (must be called from this object's lock).
11993 */
11994bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11995{
11996 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11997 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11998 if (aSettingsDir)
11999 *aSettingsDir = strMachineDirName;
12000 strMachineDirName.stripPath(); // vmname
12001 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12002 strConfigFileOnly.stripPath() // vmname.vbox
12003 .stripSuffix(); // vmname
12004 /** @todo hack, make somehow use of ComposeMachineFilename */
12005 if (mUserData->s.fDirectoryIncludesUUID)
12006 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
12007
12008 AssertReturn(!strMachineDirName.isEmpty(), false);
12009 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12010
12011 return strMachineDirName == strConfigFileOnly;
12012}
12013
12014/**
12015 * Discards all changes to machine settings.
12016 *
12017 * @param aNotify Whether to notify the direct session about changes or not.
12018 *
12019 * @note Locks objects for writing!
12020 */
12021void Machine::rollback(bool aNotify)
12022{
12023 AutoCaller autoCaller(this);
12024 AssertComRCReturn(autoCaller.rc(), (void)0);
12025
12026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12027
12028 if (!mStorageControllers.isNull())
12029 {
12030 if (mStorageControllers.isBackedUp())
12031 {
12032 /* unitialize all new devices (absent in the backed up list). */
12033 StorageControllerList::const_iterator it = mStorageControllers->begin();
12034 StorageControllerList *backedList = mStorageControllers.backedUpData();
12035 while (it != mStorageControllers->end())
12036 {
12037 if ( std::find(backedList->begin(), backedList->end(), *it)
12038 == backedList->end()
12039 )
12040 {
12041 (*it)->uninit();
12042 }
12043 ++it;
12044 }
12045
12046 /* restore the list */
12047 mStorageControllers.rollback();
12048 }
12049
12050 /* rollback any changes to devices after restoring the list */
12051 if (mData->flModifications & IsModified_Storage)
12052 {
12053 StorageControllerList::const_iterator it = mStorageControllers->begin();
12054 while (it != mStorageControllers->end())
12055 {
12056 (*it)->rollback();
12057 ++it;
12058 }
12059 }
12060 }
12061
12062 if (!mUSBControllers.isNull())
12063 {
12064 if (mUSBControllers.isBackedUp())
12065 {
12066 /* unitialize all new devices (absent in the backed up list). */
12067 USBControllerList::const_iterator it = mUSBControllers->begin();
12068 USBControllerList *backedList = mUSBControllers.backedUpData();
12069 while (it != mUSBControllers->end())
12070 {
12071 if ( std::find(backedList->begin(), backedList->end(), *it)
12072 == backedList->end()
12073 )
12074 {
12075 (*it)->uninit();
12076 }
12077 ++it;
12078 }
12079
12080 /* restore the list */
12081 mUSBControllers.rollback();
12082 }
12083
12084 /* rollback any changes to devices after restoring the list */
12085 if (mData->flModifications & IsModified_USB)
12086 {
12087 USBControllerList::const_iterator it = mUSBControllers->begin();
12088 while (it != mUSBControllers->end())
12089 {
12090 (*it)->rollback();
12091 ++it;
12092 }
12093 }
12094 }
12095
12096 mUserData.rollback();
12097
12098 mHWData.rollback();
12099
12100 if (mData->flModifications & IsModified_Storage)
12101 rollbackMedia();
12102
12103 if (mBIOSSettings)
12104 mBIOSSettings->rollback();
12105
12106 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12107 mVRDEServer->rollback();
12108
12109 if (mAudioAdapter)
12110 mAudioAdapter->rollback();
12111
12112 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12113 mUSBDeviceFilters->rollback();
12114
12115 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12116 mBandwidthControl->rollback();
12117
12118 if (!mHWData.isNull())
12119 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12120 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12121 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12122 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12123
12124 if (mData->flModifications & IsModified_NetworkAdapters)
12125 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12126 if ( mNetworkAdapters[slot]
12127 && mNetworkAdapters[slot]->isModified())
12128 {
12129 mNetworkAdapters[slot]->rollback();
12130 networkAdapters[slot] = mNetworkAdapters[slot];
12131 }
12132
12133 if (mData->flModifications & IsModified_SerialPorts)
12134 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12135 if ( mSerialPorts[slot]
12136 && mSerialPorts[slot]->isModified())
12137 {
12138 mSerialPorts[slot]->rollback();
12139 serialPorts[slot] = mSerialPorts[slot];
12140 }
12141
12142 if (mData->flModifications & IsModified_ParallelPorts)
12143 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12144 if ( mParallelPorts[slot]
12145 && mParallelPorts[slot]->isModified())
12146 {
12147 mParallelPorts[slot]->rollback();
12148 parallelPorts[slot] = mParallelPorts[slot];
12149 }
12150
12151 if (aNotify)
12152 {
12153 /* inform the direct session about changes */
12154
12155 ComObjPtr<Machine> that = this;
12156 uint32_t flModifications = mData->flModifications;
12157 alock.release();
12158
12159 if (flModifications & IsModified_SharedFolders)
12160 that->onSharedFolderChange();
12161
12162 if (flModifications & IsModified_VRDEServer)
12163 that->onVRDEServerChange(/* aRestart */ TRUE);
12164 if (flModifications & IsModified_USB)
12165 that->onUSBControllerChange();
12166
12167 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12168 if (networkAdapters[slot])
12169 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12170 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12171 if (serialPorts[slot])
12172 that->onSerialPortChange(serialPorts[slot]);
12173 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12174 if (parallelPorts[slot])
12175 that->onParallelPortChange(parallelPorts[slot]);
12176
12177 if (flModifications & IsModified_Storage)
12178 that->onStorageControllerChange();
12179
12180#if 0
12181 if (flModifications & IsModified_BandwidthControl)
12182 that->onBandwidthControlChange();
12183#endif
12184 }
12185}
12186
12187/**
12188 * Commits all the changes to machine settings.
12189 *
12190 * Note that this operation is supposed to never fail.
12191 *
12192 * @note Locks this object and children for writing.
12193 */
12194void Machine::commit()
12195{
12196 AutoCaller autoCaller(this);
12197 AssertComRCReturnVoid(autoCaller.rc());
12198
12199 AutoCaller peerCaller(mPeer);
12200 AssertComRCReturnVoid(peerCaller.rc());
12201
12202 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12203
12204 /*
12205 * use safe commit to ensure Snapshot machines (that share mUserData)
12206 * will still refer to a valid memory location
12207 */
12208 mUserData.commitCopy();
12209
12210 mHWData.commit();
12211
12212 if (mMediaData.isBackedUp())
12213 commitMedia(Global::IsOnline(mData->mMachineState));
12214
12215 mBIOSSettings->commit();
12216 mVRDEServer->commit();
12217 mAudioAdapter->commit();
12218 mUSBDeviceFilters->commit();
12219 mBandwidthControl->commit();
12220
12221 /* Since mNetworkAdapters is a list which might have been changed (resized)
12222 * without using the Backupable<> template we need to handle the copying
12223 * of the list entries manually, including the creation of peers for the
12224 * new objects. */
12225 bool commitNetworkAdapters = false;
12226 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12227 if (mPeer)
12228 {
12229 /* commit everything, even the ones which will go away */
12230 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12231 mNetworkAdapters[slot]->commit();
12232 /* copy over the new entries, creating a peer and uninit the original */
12233 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12234 for (size_t slot = 0; slot < newSize; slot++)
12235 {
12236 /* look if this adapter has a peer device */
12237 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
12238 if (!peer)
12239 {
12240 /* no peer means the adapter is a newly created one;
12241 * create a peer owning data this data share it with */
12242 peer.createObject();
12243 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12244 }
12245 mPeer->mNetworkAdapters[slot] = peer;
12246 }
12247 /* uninit any no longer needed network adapters */
12248 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12249 mNetworkAdapters[slot]->uninit();
12250 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12251 {
12252 if (mPeer->mNetworkAdapters[slot])
12253 mPeer->mNetworkAdapters[slot]->uninit();
12254 }
12255 /* Keep the original network adapter count until this point, so that
12256 * discarding a chipset type change will not lose settings. */
12257 mNetworkAdapters.resize(newSize);
12258 mPeer->mNetworkAdapters.resize(newSize);
12259 }
12260 else
12261 {
12262 /* we have no peer (our parent is the newly created machine);
12263 * just commit changes to the network adapters */
12264 commitNetworkAdapters = true;
12265 }
12266 if (commitNetworkAdapters)
12267 {
12268 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12269 mNetworkAdapters[slot]->commit();
12270 }
12271
12272 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12273 mSerialPorts[slot]->commit();
12274 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12275 mParallelPorts[slot]->commit();
12276
12277 bool commitStorageControllers = false;
12278
12279 if (mStorageControllers.isBackedUp())
12280 {
12281 mStorageControllers.commit();
12282
12283 if (mPeer)
12284 {
12285 /* Commit all changes to new controllers (this will reshare data with
12286 * peers for those who have peers) */
12287 StorageControllerList *newList = new StorageControllerList();
12288 StorageControllerList::const_iterator it = mStorageControllers->begin();
12289 while (it != mStorageControllers->end())
12290 {
12291 (*it)->commit();
12292
12293 /* look if this controller has a peer device */
12294 ComObjPtr<StorageController> peer = (*it)->getPeer();
12295 if (!peer)
12296 {
12297 /* no peer means the device is a newly created one;
12298 * create a peer owning data this device share it with */
12299 peer.createObject();
12300 peer->init(mPeer, *it, true /* aReshare */);
12301 }
12302 else
12303 {
12304 /* remove peer from the old list */
12305 mPeer->mStorageControllers->remove(peer);
12306 }
12307 /* and add it to the new list */
12308 newList->push_back(peer);
12309
12310 ++it;
12311 }
12312
12313 /* uninit old peer's controllers that are left */
12314 it = mPeer->mStorageControllers->begin();
12315 while (it != mPeer->mStorageControllers->end())
12316 {
12317 (*it)->uninit();
12318 ++it;
12319 }
12320
12321 /* attach new list of controllers to our peer */
12322 mPeer->mStorageControllers.attach(newList);
12323 }
12324 else
12325 {
12326 /* we have no peer (our parent is the newly created machine);
12327 * just commit changes to devices */
12328 commitStorageControllers = true;
12329 }
12330 }
12331 else
12332 {
12333 /* the list of controllers itself is not changed,
12334 * just commit changes to controllers themselves */
12335 commitStorageControllers = true;
12336 }
12337
12338 if (commitStorageControllers)
12339 {
12340 StorageControllerList::const_iterator it = mStorageControllers->begin();
12341 while (it != mStorageControllers->end())
12342 {
12343 (*it)->commit();
12344 ++it;
12345 }
12346 }
12347
12348 bool commitUSBControllers = false;
12349
12350 if (mUSBControllers.isBackedUp())
12351 {
12352 mUSBControllers.commit();
12353
12354 if (mPeer)
12355 {
12356 /* Commit all changes to new controllers (this will reshare data with
12357 * peers for those who have peers) */
12358 USBControllerList *newList = new USBControllerList();
12359 USBControllerList::const_iterator it = mUSBControllers->begin();
12360 while (it != mUSBControllers->end())
12361 {
12362 (*it)->commit();
12363
12364 /* look if this controller has a peer device */
12365 ComObjPtr<USBController> peer = (*it)->getPeer();
12366 if (!peer)
12367 {
12368 /* no peer means the device is a newly created one;
12369 * create a peer owning data this device share it with */
12370 peer.createObject();
12371 peer->init(mPeer, *it, true /* aReshare */);
12372 }
12373 else
12374 {
12375 /* remove peer from the old list */
12376 mPeer->mUSBControllers->remove(peer);
12377 }
12378 /* and add it to the new list */
12379 newList->push_back(peer);
12380
12381 ++it;
12382 }
12383
12384 /* uninit old peer's controllers that are left */
12385 it = mPeer->mUSBControllers->begin();
12386 while (it != mPeer->mUSBControllers->end())
12387 {
12388 (*it)->uninit();
12389 ++it;
12390 }
12391
12392 /* attach new list of controllers to our peer */
12393 mPeer->mUSBControllers.attach(newList);
12394 }
12395 else
12396 {
12397 /* we have no peer (our parent is the newly created machine);
12398 * just commit changes to devices */
12399 commitUSBControllers = true;
12400 }
12401 }
12402 else
12403 {
12404 /* the list of controllers itself is not changed,
12405 * just commit changes to controllers themselves */
12406 commitUSBControllers = true;
12407 }
12408
12409 if (commitUSBControllers)
12410 {
12411 USBControllerList::const_iterator it = mUSBControllers->begin();
12412 while (it != mUSBControllers->end())
12413 {
12414 (*it)->commit();
12415 ++it;
12416 }
12417 }
12418
12419 if (isSessionMachine())
12420 {
12421 /* attach new data to the primary machine and reshare it */
12422 mPeer->mUserData.attach(mUserData);
12423 mPeer->mHWData.attach(mHWData);
12424 /* mMediaData is reshared by fixupMedia */
12425 // mPeer->mMediaData.attach(mMediaData);
12426 Assert(mPeer->mMediaData.data() == mMediaData.data());
12427 }
12428}
12429
12430/**
12431 * Copies all the hardware data from the given machine.
12432 *
12433 * Currently, only called when the VM is being restored from a snapshot. In
12434 * particular, this implies that the VM is not running during this method's
12435 * call.
12436 *
12437 * @note This method must be called from under this object's lock.
12438 *
12439 * @note This method doesn't call #commit(), so all data remains backed up and
12440 * unsaved.
12441 */
12442void Machine::copyFrom(Machine *aThat)
12443{
12444 AssertReturnVoid(!isSnapshotMachine());
12445 AssertReturnVoid(aThat->isSnapshotMachine());
12446
12447 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12448
12449 mHWData.assignCopy(aThat->mHWData);
12450
12451 // create copies of all shared folders (mHWData after attaching a copy
12452 // contains just references to original objects)
12453 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12454 it != mHWData->mSharedFolders.end();
12455 ++it)
12456 {
12457 ComObjPtr<SharedFolder> folder;
12458 folder.createObject();
12459 HRESULT rc = folder->initCopy(getMachine(), *it);
12460 AssertComRC(rc);
12461 *it = folder;
12462 }
12463
12464 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12465 mVRDEServer->copyFrom(aThat->mVRDEServer);
12466 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12467 mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters);
12468 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12469
12470 /* create private copies of all controllers */
12471 mStorageControllers.backup();
12472 mStorageControllers->clear();
12473 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12474 it != aThat->mStorageControllers->end();
12475 ++it)
12476 {
12477 ComObjPtr<StorageController> ctrl;
12478 ctrl.createObject();
12479 ctrl->initCopy(this, *it);
12480 mStorageControllers->push_back(ctrl);
12481 }
12482
12483 /* create private copies of all USB controllers */
12484 mUSBControllers.backup();
12485 mUSBControllers->clear();
12486 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12487 it != aThat->mUSBControllers->end();
12488 ++it)
12489 {
12490 ComObjPtr<USBController> ctrl;
12491 ctrl.createObject();
12492 ctrl->initCopy(this, *it);
12493 mUSBControllers->push_back(ctrl);
12494 }
12495
12496 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12497 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12498 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12499 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12500 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12501 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12502 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12503}
12504
12505/**
12506 * Returns whether the given storage controller is hotplug capable.
12507 *
12508 * @returns true if the controller supports hotplugging
12509 * false otherwise.
12510 * @param enmCtrlType The controller type to check for.
12511 */
12512bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12513{
12514 switch (enmCtrlType)
12515 {
12516 case StorageControllerType_IntelAhci:
12517 case StorageControllerType_USB:
12518 return true;
12519 case StorageControllerType_LsiLogic:
12520 case StorageControllerType_LsiLogicSas:
12521 case StorageControllerType_BusLogic:
12522 case StorageControllerType_PIIX3:
12523 case StorageControllerType_PIIX4:
12524 case StorageControllerType_ICH6:
12525 case StorageControllerType_I82078:
12526 default:
12527 return false;
12528 }
12529}
12530
12531#ifdef VBOX_WITH_RESOURCE_USAGE_API
12532
12533void Machine::getDiskList(MediaList &list)
12534{
12535 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12536 it != mMediaData->mAttachments.end();
12537 ++it)
12538 {
12539 MediumAttachment* pAttach = *it;
12540 /* just in case */
12541 AssertStmt(pAttach, continue);
12542
12543 AutoCaller localAutoCallerA(pAttach);
12544 if (FAILED(localAutoCallerA.rc())) continue;
12545
12546 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12547
12548 if (pAttach->getType() == DeviceType_HardDisk)
12549 list.push_back(pAttach->getMedium());
12550 }
12551}
12552
12553void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12554{
12555 AssertReturnVoid(isWriteLockOnCurrentThread());
12556 AssertPtrReturnVoid(aCollector);
12557
12558 pm::CollectorHAL *hal = aCollector->getHAL();
12559 /* Create sub metrics */
12560 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12561 "Percentage of processor time spent in user mode by the VM process.");
12562 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12563 "Percentage of processor time spent in kernel mode by the VM process.");
12564 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12565 "Size of resident portion of VM process in memory.");
12566 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12567 "Actual size of all VM disks combined.");
12568 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12569 "Network receive rate.");
12570 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12571 "Network transmit rate.");
12572 /* Create and register base metrics */
12573 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12574 cpuLoadUser, cpuLoadKernel);
12575 aCollector->registerBaseMetric(cpuLoad);
12576 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12577 ramUsageUsed);
12578 aCollector->registerBaseMetric(ramUsage);
12579 MediaList disks;
12580 getDiskList(disks);
12581 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12582 diskUsageUsed);
12583 aCollector->registerBaseMetric(diskUsage);
12584
12585 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12586 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12587 new pm::AggregateAvg()));
12588 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12589 new pm::AggregateMin()));
12590 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12591 new pm::AggregateMax()));
12592 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12593 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12594 new pm::AggregateAvg()));
12595 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12596 new pm::AggregateMin()));
12597 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12598 new pm::AggregateMax()));
12599
12600 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12601 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12602 new pm::AggregateAvg()));
12603 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12604 new pm::AggregateMin()));
12605 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12606 new pm::AggregateMax()));
12607
12608 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12609 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12610 new pm::AggregateAvg()));
12611 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12612 new pm::AggregateMin()));
12613 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12614 new pm::AggregateMax()));
12615
12616
12617 /* Guest metrics collector */
12618 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12619 aCollector->registerGuest(mCollectorGuest);
12620 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12621 this, __PRETTY_FUNCTION__, mCollectorGuest));
12622
12623 /* Create sub metrics */
12624 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12625 "Percentage of processor time spent in user mode as seen by the guest.");
12626 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12627 "Percentage of processor time spent in kernel mode as seen by the guest.");
12628 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12629 "Percentage of processor time spent idling as seen by the guest.");
12630
12631 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12632 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12633 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12634 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12635 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12636 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12637
12638 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12639
12640 /* Create and register base metrics */
12641 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12642 machineNetRx, machineNetTx);
12643 aCollector->registerBaseMetric(machineNetRate);
12644
12645 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12646 guestLoadUser, guestLoadKernel, guestLoadIdle);
12647 aCollector->registerBaseMetric(guestCpuLoad);
12648
12649 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12650 guestMemTotal, guestMemFree,
12651 guestMemBalloon, guestMemShared,
12652 guestMemCache, guestPagedTotal);
12653 aCollector->registerBaseMetric(guestCpuMem);
12654
12655 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12656 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12657 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12658 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12659
12660 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12661 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12662 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12663 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12664
12665 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12666 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12667 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12668 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12669
12670 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12671 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12672 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12673 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12674
12675 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12676 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12677 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12678 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12679
12680 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12681 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12682 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12683 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12684
12685 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12686 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12687 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12688 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12689
12690 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12691 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12692 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12693 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12694
12695 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12696 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12697 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12698 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12699
12700 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12701 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12702 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12703 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12704
12705 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12706 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12707 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12708 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12709}
12710
12711void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12712{
12713 AssertReturnVoid(isWriteLockOnCurrentThread());
12714
12715 if (aCollector)
12716 {
12717 aCollector->unregisterMetricsFor(aMachine);
12718 aCollector->unregisterBaseMetricsFor(aMachine);
12719 }
12720}
12721
12722#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12723
12724
12725////////////////////////////////////////////////////////////////////////////////
12726
12727DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12728
12729HRESULT SessionMachine::FinalConstruct()
12730{
12731 LogFlowThisFunc(("\n"));
12732
12733 mClientToken = NULL;
12734
12735 return BaseFinalConstruct();
12736}
12737
12738void SessionMachine::FinalRelease()
12739{
12740 LogFlowThisFunc(("\n"));
12741
12742 Assert(!mClientToken);
12743 /* paranoia, should not hang around any more */
12744 if (mClientToken)
12745 {
12746 delete mClientToken;
12747 mClientToken = NULL;
12748 }
12749
12750 uninit(Uninit::Unexpected);
12751
12752 BaseFinalRelease();
12753}
12754
12755/**
12756 * @note Must be called only by Machine::LockMachine() from its own write lock.
12757 */
12758HRESULT SessionMachine::init(Machine *aMachine)
12759{
12760 LogFlowThisFuncEnter();
12761 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12762
12763 AssertReturn(aMachine, E_INVALIDARG);
12764
12765 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12766
12767 /* Enclose the state transition NotReady->InInit->Ready */
12768 AutoInitSpan autoInitSpan(this);
12769 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12770
12771 HRESULT rc = S_OK;
12772
12773 /* create the machine client token */
12774 try
12775 {
12776 mClientToken = new ClientToken(aMachine, this);
12777 if (!mClientToken->isReady())
12778 {
12779 delete mClientToken;
12780 mClientToken = NULL;
12781 rc = E_FAIL;
12782 }
12783 }
12784 catch (std::bad_alloc &)
12785 {
12786 rc = E_OUTOFMEMORY;
12787 }
12788 if (FAILED(rc))
12789 return rc;
12790
12791 /* memorize the peer Machine */
12792 unconst(mPeer) = aMachine;
12793 /* share the parent pointer */
12794 unconst(mParent) = aMachine->mParent;
12795
12796 /* take the pointers to data to share */
12797 mData.share(aMachine->mData);
12798 mSSData.share(aMachine->mSSData);
12799
12800 mUserData.share(aMachine->mUserData);
12801 mHWData.share(aMachine->mHWData);
12802 mMediaData.share(aMachine->mMediaData);
12803
12804 mStorageControllers.allocate();
12805 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12806 it != aMachine->mStorageControllers->end();
12807 ++it)
12808 {
12809 ComObjPtr<StorageController> ctl;
12810 ctl.createObject();
12811 ctl->init(this, *it);
12812 mStorageControllers->push_back(ctl);
12813 }
12814
12815 mUSBControllers.allocate();
12816 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12817 it != aMachine->mUSBControllers->end();
12818 ++it)
12819 {
12820 ComObjPtr<USBController> ctl;
12821 ctl.createObject();
12822 ctl->init(this, *it);
12823 mUSBControllers->push_back(ctl);
12824 }
12825
12826 unconst(mBIOSSettings).createObject();
12827 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12828 /* create another VRDEServer object that will be mutable */
12829 unconst(mVRDEServer).createObject();
12830 mVRDEServer->init(this, aMachine->mVRDEServer);
12831 /* create another audio adapter object that will be mutable */
12832 unconst(mAudioAdapter).createObject();
12833 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12834 /* create a list of serial ports that will be mutable */
12835 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12836 {
12837 unconst(mSerialPorts[slot]).createObject();
12838 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12839 }
12840 /* create a list of parallel ports that will be mutable */
12841 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12842 {
12843 unconst(mParallelPorts[slot]).createObject();
12844 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12845 }
12846
12847 /* create another USB device filters object that will be mutable */
12848 unconst(mUSBDeviceFilters).createObject();
12849 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12850
12851 /* create a list of network adapters that will be mutable */
12852 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12853 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12854 {
12855 unconst(mNetworkAdapters[slot]).createObject();
12856 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12857
12858 NetworkAttachmentType_T type;
12859 HRESULT hrc;
12860 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12861 if ( SUCCEEDED(hrc)
12862 && type == NetworkAttachmentType_NATNetwork)
12863 {
12864 Bstr name;
12865 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12866 if (SUCCEEDED(hrc))
12867 {
12868 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12869 mUserData->s.strName.c_str(), name.raw()));
12870 aMachine->lockHandle()->unlockWrite();
12871 mParent->natNetworkRefInc(name.raw());
12872#ifdef RT_LOCK_STRICT
12873 aMachine->lockHandle()->lockWrite(RT_SRC_POS);
12874#else
12875 aMachine->lockHandle()->lockWrite();
12876#endif
12877 }
12878 }
12879 }
12880
12881 /* create another bandwidth control object that will be mutable */
12882 unconst(mBandwidthControl).createObject();
12883 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12884
12885 /* default is to delete saved state on Saved -> PoweredOff transition */
12886 mRemoveSavedState = true;
12887
12888 /* Confirm a successful initialization when it's the case */
12889 autoInitSpan.setSucceeded();
12890
12891 LogFlowThisFuncLeave();
12892 return rc;
12893}
12894
12895/**
12896 * Uninitializes this session object. If the reason is other than
12897 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12898 * or the client watcher code.
12899 *
12900 * @param aReason uninitialization reason
12901 *
12902 * @note Locks mParent + this object for writing.
12903 */
12904void SessionMachine::uninit(Uninit::Reason aReason)
12905{
12906 LogFlowThisFuncEnter();
12907 LogFlowThisFunc(("reason=%d\n", aReason));
12908
12909 /*
12910 * Strongly reference ourselves to prevent this object deletion after
12911 * mData->mSession.mMachine.setNull() below (which can release the last
12912 * reference and call the destructor). Important: this must be done before
12913 * accessing any members (and before AutoUninitSpan that does it as well).
12914 * This self reference will be released as the very last step on return.
12915 */
12916 ComObjPtr<SessionMachine> selfRef = this;
12917
12918 /* Enclose the state transition Ready->InUninit->NotReady */
12919 AutoUninitSpan autoUninitSpan(this);
12920 if (autoUninitSpan.uninitDone())
12921 {
12922 LogFlowThisFunc(("Already uninitialized\n"));
12923 LogFlowThisFuncLeave();
12924 return;
12925 }
12926
12927 if (autoUninitSpan.initFailed())
12928 {
12929 /* We've been called by init() because it's failed. It's not really
12930 * necessary (nor it's safe) to perform the regular uninit sequence
12931 * below, the following is enough.
12932 */
12933 LogFlowThisFunc(("Initialization failed.\n"));
12934 /* destroy the machine client token */
12935 if (mClientToken)
12936 {
12937 delete mClientToken;
12938 mClientToken = NULL;
12939 }
12940 uninitDataAndChildObjects();
12941 mData.free();
12942 unconst(mParent) = NULL;
12943 unconst(mPeer) = NULL;
12944 LogFlowThisFuncLeave();
12945 return;
12946 }
12947
12948 MachineState_T lastState;
12949 {
12950 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12951 lastState = mData->mMachineState;
12952 }
12953 NOREF(lastState);
12954
12955#ifdef VBOX_WITH_USB
12956 // release all captured USB devices, but do this before requesting the locks below
12957 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12958 {
12959 /* Console::captureUSBDevices() is called in the VM process only after
12960 * setting the machine state to Starting or Restoring.
12961 * Console::detachAllUSBDevices() will be called upon successful
12962 * termination. So, we need to release USB devices only if there was
12963 * an abnormal termination of a running VM.
12964 *
12965 * This is identical to SessionMachine::DetachAllUSBDevices except
12966 * for the aAbnormal argument. */
12967 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
12968 AssertComRC(rc);
12969 NOREF(rc);
12970
12971 USBProxyService *service = mParent->host()->usbProxyService();
12972 if (service)
12973 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12974 }
12975#endif /* VBOX_WITH_USB */
12976
12977 // we need to lock this object in uninit() because the lock is shared
12978 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12979 // and others need mParent lock, and USB needs host lock.
12980 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12981
12982#ifdef VBOX_WITH_RESOURCE_USAGE_API
12983 /*
12984 * It is safe to call Machine::unregisterMetrics() here because
12985 * PerformanceCollector::samplerCallback no longer accesses guest methods
12986 * holding the lock.
12987 */
12988 unregisterMetrics(mParent->performanceCollector(), mPeer);
12989 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12990 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12991 this, __PRETTY_FUNCTION__, mCollectorGuest));
12992 if (mCollectorGuest)
12993 {
12994 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12995 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12996 mCollectorGuest = NULL;
12997 }
12998#endif
12999
13000 if (aReason == Uninit::Abnormal)
13001 {
13002 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
13003 Global::IsOnlineOrTransient(lastState)));
13004
13005 /* reset the state to Aborted */
13006 if (mData->mMachineState != MachineState_Aborted)
13007 setMachineState(MachineState_Aborted);
13008 }
13009
13010 // any machine settings modified?
13011 if (mData->flModifications)
13012 {
13013 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
13014 rollback(false /* aNotify */);
13015 }
13016
13017 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
13018 || !mConsoleTaskData.mSnapshot);
13019 if (!mConsoleTaskData.strStateFilePath.isEmpty())
13020 {
13021 LogWarningThisFunc(("canceling failed save state request!\n"));
13022 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
13023 }
13024 else if (!mConsoleTaskData.mSnapshot.isNull())
13025 {
13026 LogWarningThisFunc(("canceling untaken snapshot!\n"));
13027
13028 /* delete all differencing hard disks created (this will also attach
13029 * their parents back by rolling back mMediaData) */
13030 rollbackMedia();
13031
13032 // delete the saved state file (it might have been already created)
13033 // AFTER killing the snapshot so that releaseSavedStateFile() won't
13034 // think it's still in use
13035 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
13036 mConsoleTaskData.mSnapshot->uninit();
13037 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
13038 }
13039
13040 if (!mData->mSession.mType.isEmpty())
13041 {
13042 /* mType is not null when this machine's process has been started by
13043 * Machine::LaunchVMProcess(), therefore it is our child. We
13044 * need to queue the PID to reap the process (and avoid zombies on
13045 * Linux). */
13046 Assert(mData->mSession.mPID != NIL_RTPROCESS);
13047 mParent->addProcessToReap(mData->mSession.mPID);
13048 }
13049
13050 mData->mSession.mPID = NIL_RTPROCESS;
13051
13052 if (aReason == Uninit::Unexpected)
13053 {
13054 /* Uninitialization didn't come from #checkForDeath(), so tell the
13055 * client watcher thread to update the set of machines that have open
13056 * sessions. */
13057 mParent->updateClientWatcher();
13058 }
13059
13060 /* uninitialize all remote controls */
13061 if (mData->mSession.mRemoteControls.size())
13062 {
13063 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13064 mData->mSession.mRemoteControls.size()));
13065
13066 Data::Session::RemoteControlList::iterator it =
13067 mData->mSession.mRemoteControls.begin();
13068 while (it != mData->mSession.mRemoteControls.end())
13069 {
13070 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13071 HRESULT rc = (*it)->Uninitialize();
13072 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13073 if (FAILED(rc))
13074 LogWarningThisFunc(("Forgot to close the remote session?\n"));
13075 ++it;
13076 }
13077 mData->mSession.mRemoteControls.clear();
13078 }
13079
13080 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
13081 {
13082 NetworkAttachmentType_T type;
13083 HRESULT hrc;
13084
13085 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13086 if ( SUCCEEDED(hrc)
13087 && type == NetworkAttachmentType_NATNetwork)
13088 {
13089 Bstr name;
13090 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13091 if (SUCCEEDED(hrc))
13092 {
13093 multilock.release();
13094 LogRel(("VM '%s' stops using NAT network '%ls'\n",
13095 mUserData->s.strName.c_str(), name.raw()));
13096 mParent->natNetworkRefDec(name.raw());
13097 multilock.acquire();
13098 }
13099 }
13100 }
13101
13102 /*
13103 * An expected uninitialization can come only from #checkForDeath().
13104 * Otherwise it means that something's gone really wrong (for example,
13105 * the Session implementation has released the VirtualBox reference
13106 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13107 * etc). However, it's also possible, that the client releases the IPC
13108 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13109 * but the VirtualBox release event comes first to the server process.
13110 * This case is practically possible, so we should not assert on an
13111 * unexpected uninit, just log a warning.
13112 */
13113
13114 if ((aReason == Uninit::Unexpected))
13115 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13116
13117 if (aReason != Uninit::Normal)
13118 {
13119 mData->mSession.mDirectControl.setNull();
13120 }
13121 else
13122 {
13123 /* this must be null here (see #OnSessionEnd()) */
13124 Assert(mData->mSession.mDirectControl.isNull());
13125 Assert(mData->mSession.mState == SessionState_Unlocking);
13126 Assert(!mData->mSession.mProgress.isNull());
13127 }
13128 if (mData->mSession.mProgress)
13129 {
13130 if (aReason == Uninit::Normal)
13131 mData->mSession.mProgress->notifyComplete(S_OK);
13132 else
13133 mData->mSession.mProgress->notifyComplete(E_FAIL,
13134 COM_IIDOF(ISession),
13135 getComponentName(),
13136 tr("The VM session was aborted"));
13137 mData->mSession.mProgress.setNull();
13138 }
13139
13140 /* remove the association between the peer machine and this session machine */
13141 Assert( (SessionMachine*)mData->mSession.mMachine == this
13142 || aReason == Uninit::Unexpected);
13143
13144 /* reset the rest of session data */
13145 mData->mSession.mMachine.setNull();
13146 mData->mSession.mState = SessionState_Unlocked;
13147 mData->mSession.mType.setNull();
13148
13149 /* destroy the machine client token before leaving the exclusive lock */
13150 if (mClientToken)
13151 {
13152 delete mClientToken;
13153 mClientToken = NULL;
13154 }
13155
13156 /* fire an event */
13157 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13158
13159 uninitDataAndChildObjects();
13160
13161 /* free the essential data structure last */
13162 mData.free();
13163
13164 /* release the exclusive lock before setting the below two to NULL */
13165 multilock.release();
13166
13167 unconst(mParent) = NULL;
13168 unconst(mPeer) = NULL;
13169
13170 LogFlowThisFuncLeave();
13171}
13172
13173// util::Lockable interface
13174////////////////////////////////////////////////////////////////////////////////
13175
13176/**
13177 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13178 * with the primary Machine instance (mPeer).
13179 */
13180RWLockHandle *SessionMachine::lockHandle() const
13181{
13182 AssertReturn(mPeer != NULL, NULL);
13183 return mPeer->lockHandle();
13184}
13185
13186// IInternalMachineControl methods
13187////////////////////////////////////////////////////////////////////////////////
13188
13189/**
13190 * Passes collected guest statistics to performance collector object
13191 */
13192STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13193 ULONG aCpuKernel, ULONG aCpuIdle,
13194 ULONG aMemTotal, ULONG aMemFree,
13195 ULONG aMemBalloon, ULONG aMemShared,
13196 ULONG aMemCache, ULONG aPageTotal,
13197 ULONG aAllocVMM, ULONG aFreeVMM,
13198 ULONG aBalloonedVMM, ULONG aSharedVMM,
13199 ULONG aVmNetRx, ULONG aVmNetTx)
13200{
13201#ifdef VBOX_WITH_RESOURCE_USAGE_API
13202 if (mCollectorGuest)
13203 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13204 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13205 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13206 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13207
13208 return S_OK;
13209#else
13210 NOREF(aValidStats);
13211 NOREF(aCpuUser);
13212 NOREF(aCpuKernel);
13213 NOREF(aCpuIdle);
13214 NOREF(aMemTotal);
13215 NOREF(aMemFree);
13216 NOREF(aMemBalloon);
13217 NOREF(aMemShared);
13218 NOREF(aMemCache);
13219 NOREF(aPageTotal);
13220 NOREF(aAllocVMM);
13221 NOREF(aFreeVMM);
13222 NOREF(aBalloonedVMM);
13223 NOREF(aSharedVMM);
13224 NOREF(aVmNetRx);
13225 NOREF(aVmNetTx);
13226 return E_NOTIMPL;
13227#endif
13228}
13229
13230/**
13231 * @note Locks this object for writing.
13232 */
13233STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13234{
13235 AutoCaller autoCaller(this);
13236 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13237
13238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13239
13240 mRemoveSavedState = aRemove;
13241
13242 return S_OK;
13243}
13244
13245/**
13246 * @note Locks the same as #setMachineState() does.
13247 */
13248STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13249{
13250 return setMachineState(aMachineState);
13251}
13252
13253/**
13254 * @note Locks this object for writing.
13255 */
13256STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13257{
13258 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13259 AutoCaller autoCaller(this);
13260 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13261
13262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13263
13264 if (mData->mSession.mState != SessionState_Locked)
13265 return VBOX_E_INVALID_OBJECT_STATE;
13266
13267 if (!mData->mSession.mProgress.isNull())
13268 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13269
13270 LogFlowThisFunc(("returns S_OK.\n"));
13271 return S_OK;
13272}
13273
13274/**
13275 * @note Locks this object for writing.
13276 */
13277STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13278{
13279 AutoCaller autoCaller(this);
13280 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13281
13282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13283
13284 if (mData->mSession.mState != SessionState_Locked)
13285 return VBOX_E_INVALID_OBJECT_STATE;
13286
13287 /* Finalize the LaunchVMProcess progress object. */
13288 if (mData->mSession.mProgress)
13289 {
13290 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13291 mData->mSession.mProgress.setNull();
13292 }
13293
13294 if (SUCCEEDED((HRESULT)iResult))
13295 {
13296#ifdef VBOX_WITH_RESOURCE_USAGE_API
13297 /* The VM has been powered up successfully, so it makes sense
13298 * now to offer the performance metrics for a running machine
13299 * object. Doing it earlier wouldn't be safe. */
13300 registerMetrics(mParent->performanceCollector(), mPeer,
13301 mData->mSession.mPID);
13302#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13303 }
13304
13305 return S_OK;
13306}
13307
13308/**
13309 * @note Locks this object for writing.
13310 */
13311STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13312{
13313 LogFlowThisFuncEnter();
13314
13315 CheckComArgOutPointerValid(aProgress);
13316
13317 AutoCaller autoCaller(this);
13318 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13319
13320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13321
13322 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13323 E_FAIL);
13324
13325 /* create a progress object to track operation completion */
13326 ComObjPtr<Progress> pProgress;
13327 pProgress.createObject();
13328 pProgress->init(getVirtualBox(),
13329 static_cast<IMachine *>(this) /* aInitiator */,
13330 Bstr(tr("Stopping the virtual machine")).raw(),
13331 FALSE /* aCancelable */);
13332
13333 /* fill in the console task data */
13334 mConsoleTaskData.mLastState = mData->mMachineState;
13335 mConsoleTaskData.mProgress = pProgress;
13336
13337 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13338 setMachineState(MachineState_Stopping);
13339
13340 pProgress.queryInterfaceTo(aProgress);
13341
13342 return S_OK;
13343}
13344
13345/**
13346 * @note Locks this object for writing.
13347 */
13348STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13349{
13350 LogFlowThisFuncEnter();
13351
13352 AutoCaller autoCaller(this);
13353 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13354
13355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13356
13357 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13358 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13359 && mConsoleTaskData.mLastState != MachineState_Null,
13360 E_FAIL);
13361
13362 /*
13363 * On failure, set the state to the state we had when BeginPoweringDown()
13364 * was called (this is expected by Console::PowerDown() and the associated
13365 * task). On success the VM process already changed the state to
13366 * MachineState_PoweredOff, so no need to do anything.
13367 */
13368 if (FAILED(iResult))
13369 setMachineState(mConsoleTaskData.mLastState);
13370
13371 /* notify the progress object about operation completion */
13372 Assert(mConsoleTaskData.mProgress);
13373 if (SUCCEEDED(iResult))
13374 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13375 else
13376 {
13377 Utf8Str strErrMsg(aErrMsg);
13378 if (strErrMsg.length())
13379 mConsoleTaskData.mProgress->notifyComplete(iResult,
13380 COM_IIDOF(ISession),
13381 getComponentName(),
13382 strErrMsg.c_str());
13383 else
13384 mConsoleTaskData.mProgress->notifyComplete(iResult);
13385 }
13386
13387 /* clear out the temporary saved state data */
13388 mConsoleTaskData.mLastState = MachineState_Null;
13389 mConsoleTaskData.mProgress.setNull();
13390
13391 LogFlowThisFuncLeave();
13392 return S_OK;
13393}
13394
13395
13396/**
13397 * Goes through the USB filters of the given machine to see if the given
13398 * device matches any filter or not.
13399 *
13400 * @note Locks the same as USBController::hasMatchingFilter() does.
13401 */
13402STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13403 BOOL *aMatched,
13404 ULONG *aMaskedIfs)
13405{
13406 LogFlowThisFunc(("\n"));
13407
13408 CheckComArgNotNull(aUSBDevice);
13409 CheckComArgOutPointerValid(aMatched);
13410
13411 AutoCaller autoCaller(this);
13412 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13413
13414#ifdef VBOX_WITH_USB
13415 *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13416#else
13417 NOREF(aUSBDevice);
13418 NOREF(aMaskedIfs);
13419 *aMatched = FALSE;
13420#endif
13421
13422 return S_OK;
13423}
13424
13425/**
13426 * @note Locks the same as Host::captureUSBDevice() does.
13427 */
13428STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13429{
13430 LogFlowThisFunc(("\n"));
13431
13432 AutoCaller autoCaller(this);
13433 AssertComRCReturnRC(autoCaller.rc());
13434
13435#ifdef VBOX_WITH_USB
13436 /* if captureDeviceForVM() fails, it must have set extended error info */
13437 clearError();
13438 MultiResult rc = mParent->host()->checkUSBProxyService();
13439 if (FAILED(rc)) return rc;
13440
13441 USBProxyService *service = mParent->host()->usbProxyService();
13442 AssertReturn(service, E_FAIL);
13443 return service->captureDeviceForVM(this, Guid(aId).ref());
13444#else
13445 NOREF(aId);
13446 return E_NOTIMPL;
13447#endif
13448}
13449
13450/**
13451 * @note Locks the same as Host::detachUSBDevice() does.
13452 */
13453STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13454{
13455 LogFlowThisFunc(("\n"));
13456
13457 AutoCaller autoCaller(this);
13458 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13459
13460#ifdef VBOX_WITH_USB
13461 USBProxyService *service = mParent->host()->usbProxyService();
13462 AssertReturn(service, E_FAIL);
13463 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13464#else
13465 NOREF(aId);
13466 NOREF(aDone);
13467 return E_NOTIMPL;
13468#endif
13469}
13470
13471/**
13472 * Inserts all machine filters to the USB proxy service and then calls
13473 * Host::autoCaptureUSBDevices().
13474 *
13475 * Called by Console from the VM process upon VM startup.
13476 *
13477 * @note Locks what called methods lock.
13478 */
13479STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13480{
13481 LogFlowThisFunc(("\n"));
13482
13483 AutoCaller autoCaller(this);
13484 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13485
13486#ifdef VBOX_WITH_USB
13487 HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);
13488 AssertComRC(rc);
13489 NOREF(rc);
13490
13491 USBProxyService *service = mParent->host()->usbProxyService();
13492 AssertReturn(service, E_FAIL);
13493 return service->autoCaptureDevicesForVM(this);
13494#else
13495 return S_OK;
13496#endif
13497}
13498
13499/**
13500 * Removes all machine filters from the USB proxy service and then calls
13501 * Host::detachAllUSBDevices().
13502 *
13503 * Called by Console from the VM process upon normal VM termination or by
13504 * SessionMachine::uninit() upon abnormal VM termination (from under the
13505 * Machine/SessionMachine lock).
13506 *
13507 * @note Locks what called methods lock.
13508 */
13509STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13510{
13511 LogFlowThisFunc(("\n"));
13512
13513 AutoCaller autoCaller(this);
13514 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13515
13516#ifdef VBOX_WITH_USB
13517 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
13518 AssertComRC(rc);
13519 NOREF(rc);
13520
13521 USBProxyService *service = mParent->host()->usbProxyService();
13522 AssertReturn(service, E_FAIL);
13523 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13524#else
13525 NOREF(aDone);
13526 return S_OK;
13527#endif
13528}
13529
13530/**
13531 * @note Locks this object for writing.
13532 */
13533STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13534 IProgress **aProgress)
13535{
13536 LogFlowThisFuncEnter();
13537
13538 AssertReturn(aSession, E_INVALIDARG);
13539 AssertReturn(aProgress, E_INVALIDARG);
13540
13541 AutoCaller autoCaller(this);
13542
13543 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13544 /*
13545 * We don't assert below because it might happen that a non-direct session
13546 * informs us it is closed right after we've been uninitialized -- it's ok.
13547 */
13548 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13549
13550 /* get IInternalSessionControl interface */
13551 ComPtr<IInternalSessionControl> control(aSession);
13552
13553 ComAssertRet(!control.isNull(), E_INVALIDARG);
13554
13555 /* Creating a Progress object requires the VirtualBox lock, and
13556 * thus locking it here is required by the lock order rules. */
13557 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13558
13559 if (control == mData->mSession.mDirectControl)
13560 {
13561 ComAssertRet(aProgress, E_POINTER);
13562
13563 /* The direct session is being normally closed by the client process
13564 * ----------------------------------------------------------------- */
13565
13566 /* go to the closing state (essential for all open*Session() calls and
13567 * for #checkForDeath()) */
13568 Assert(mData->mSession.mState == SessionState_Locked);
13569 mData->mSession.mState = SessionState_Unlocking;
13570
13571 /* set direct control to NULL to release the remote instance */
13572 mData->mSession.mDirectControl.setNull();
13573 LogFlowThisFunc(("Direct control is set to NULL\n"));
13574
13575 if (mData->mSession.mProgress)
13576 {
13577 /* finalize the progress, someone might wait if a frontend
13578 * closes the session before powering on the VM. */
13579 mData->mSession.mProgress->notifyComplete(E_FAIL,
13580 COM_IIDOF(ISession),
13581 getComponentName(),
13582 tr("The VM session was closed before any attempt to power it on"));
13583 mData->mSession.mProgress.setNull();
13584 }
13585
13586 /* Create the progress object the client will use to wait until
13587 * #checkForDeath() is called to uninitialize this session object after
13588 * it releases the IPC semaphore.
13589 * Note! Because we're "reusing" mProgress here, this must be a proxy
13590 * object just like for LaunchVMProcess. */
13591 Assert(mData->mSession.mProgress.isNull());
13592 ComObjPtr<ProgressProxy> progress;
13593 progress.createObject();
13594 ComPtr<IUnknown> pPeer(mPeer);
13595 progress->init(mParent, pPeer,
13596 Bstr(tr("Closing session")).raw(),
13597 FALSE /* aCancelable */);
13598 progress.queryInterfaceTo(aProgress);
13599 mData->mSession.mProgress = progress;
13600 }
13601 else
13602 {
13603 /* the remote session is being normally closed */
13604 Data::Session::RemoteControlList::iterator it =
13605 mData->mSession.mRemoteControls.begin();
13606 while (it != mData->mSession.mRemoteControls.end())
13607 {
13608 if (control == *it)
13609 break;
13610 ++it;
13611 }
13612 BOOL found = it != mData->mSession.mRemoteControls.end();
13613 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13614 E_INVALIDARG);
13615 // This MUST be erase(it), not remove(*it) as the latter triggers a
13616 // very nasty use after free due to the place where the value "lives".
13617 mData->mSession.mRemoteControls.erase(it);
13618 }
13619
13620 /* signal the client watcher thread, because the client is going away */
13621 mParent->updateClientWatcher();
13622
13623 LogFlowThisFuncLeave();
13624 return S_OK;
13625}
13626
13627/**
13628 * @note Locks this object for writing.
13629 */
13630STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13631{
13632 LogFlowThisFuncEnter();
13633
13634 CheckComArgOutPointerValid(aProgress);
13635 CheckComArgOutPointerValid(aStateFilePath);
13636
13637 AutoCaller autoCaller(this);
13638 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13639
13640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13641
13642 AssertReturn( mData->mMachineState == MachineState_Paused
13643 && mConsoleTaskData.mLastState == MachineState_Null
13644 && mConsoleTaskData.strStateFilePath.isEmpty(),
13645 E_FAIL);
13646
13647 /* create a progress object to track operation completion */
13648 ComObjPtr<Progress> pProgress;
13649 pProgress.createObject();
13650 pProgress->init(getVirtualBox(),
13651 static_cast<IMachine *>(this) /* aInitiator */,
13652 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13653 FALSE /* aCancelable */);
13654
13655 Utf8Str strStateFilePath;
13656 /* stateFilePath is null when the machine is not running */
13657 if (mData->mMachineState == MachineState_Paused)
13658 composeSavedStateFilename(strStateFilePath);
13659
13660 /* fill in the console task data */
13661 mConsoleTaskData.mLastState = mData->mMachineState;
13662 mConsoleTaskData.strStateFilePath = strStateFilePath;
13663 mConsoleTaskData.mProgress = pProgress;
13664
13665 /* set the state to Saving (this is expected by Console::SaveState()) */
13666 setMachineState(MachineState_Saving);
13667
13668 strStateFilePath.cloneTo(aStateFilePath);
13669 pProgress.queryInterfaceTo(aProgress);
13670
13671 return S_OK;
13672}
13673
13674/**
13675 * @note Locks mParent + this object for writing.
13676 */
13677STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13678{
13679 LogFlowThisFunc(("\n"));
13680
13681 AutoCaller autoCaller(this);
13682 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13683
13684 /* endSavingState() need mParent lock */
13685 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13686
13687 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13688 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13689 && mConsoleTaskData.mLastState != MachineState_Null
13690 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13691 E_FAIL);
13692
13693 /*
13694 * On failure, set the state to the state we had when BeginSavingState()
13695 * was called (this is expected by Console::SaveState() and the associated
13696 * task). On success the VM process already changed the state to
13697 * MachineState_Saved, so no need to do anything.
13698 */
13699 if (FAILED(iResult))
13700 setMachineState(mConsoleTaskData.mLastState);
13701
13702 return endSavingState(iResult, aErrMsg);
13703}
13704
13705/**
13706 * @note Locks this object for writing.
13707 */
13708STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13709{
13710 LogFlowThisFunc(("\n"));
13711
13712 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13713
13714 AutoCaller autoCaller(this);
13715 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13716
13717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13718
13719 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13720 || mData->mMachineState == MachineState_Teleported
13721 || mData->mMachineState == MachineState_Aborted
13722 , E_FAIL); /** @todo setError. */
13723
13724 Utf8Str stateFilePathFull = aSavedStateFile;
13725 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13726 if (RT_FAILURE(vrc))
13727 return setError(VBOX_E_FILE_ERROR,
13728 tr("Invalid saved state file path '%ls' (%Rrc)"),
13729 aSavedStateFile,
13730 vrc);
13731
13732 mSSData->strStateFilePath = stateFilePathFull;
13733
13734 /* The below setMachineState() will detect the state transition and will
13735 * update the settings file */
13736
13737 return setMachineState(MachineState_Saved);
13738}
13739
13740STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13741 ComSafeArrayOut(BSTR, aValues),
13742 ComSafeArrayOut(LONG64, aTimestamps),
13743 ComSafeArrayOut(BSTR, aFlags))
13744{
13745 LogFlowThisFunc(("\n"));
13746
13747#ifdef VBOX_WITH_GUEST_PROPS
13748 using namespace guestProp;
13749
13750 AutoCaller autoCaller(this);
13751 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13752
13753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13754
13755 CheckComArgOutSafeArrayPointerValid(aNames);
13756 CheckComArgOutSafeArrayPointerValid(aValues);
13757 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13758 CheckComArgOutSafeArrayPointerValid(aFlags);
13759
13760 size_t cEntries = mHWData->mGuestProperties.size();
13761 com::SafeArray<BSTR> names(cEntries);
13762 com::SafeArray<BSTR> values(cEntries);
13763 com::SafeArray<LONG64> timestamps(cEntries);
13764 com::SafeArray<BSTR> flags(cEntries);
13765 unsigned i = 0;
13766 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13767 it != mHWData->mGuestProperties.end();
13768 ++it)
13769 {
13770 char szFlags[MAX_FLAGS_LEN + 1];
13771 it->first.cloneTo(&names[i]);
13772 it->second.strValue.cloneTo(&values[i]);
13773 timestamps[i] = it->second.mTimestamp;
13774 /* If it is NULL, keep it NULL. */
13775 if (it->second.mFlags)
13776 {
13777 writeFlags(it->second.mFlags, szFlags);
13778 Bstr(szFlags).cloneTo(&flags[i]);
13779 }
13780 else
13781 flags[i] = NULL;
13782 ++i;
13783 }
13784 names.detachTo(ComSafeArrayOutArg(aNames));
13785 values.detachTo(ComSafeArrayOutArg(aValues));
13786 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13787 flags.detachTo(ComSafeArrayOutArg(aFlags));
13788 return S_OK;
13789#else
13790 ReturnComNotImplemented();
13791#endif
13792}
13793
13794STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13795 IN_BSTR aValue,
13796 LONG64 aTimestamp,
13797 IN_BSTR aFlags)
13798{
13799 LogFlowThisFunc(("\n"));
13800
13801#ifdef VBOX_WITH_GUEST_PROPS
13802 using namespace guestProp;
13803
13804 CheckComArgStrNotEmptyOrNull(aName);
13805 CheckComArgNotNull(aValue);
13806 CheckComArgNotNull(aFlags);
13807
13808 try
13809 {
13810 /*
13811 * Convert input up front.
13812 */
13813 Utf8Str utf8Name(aName);
13814 uint32_t fFlags = NILFLAG;
13815 if (aFlags)
13816 {
13817 Utf8Str utf8Flags(aFlags);
13818 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13819 AssertRCReturn(vrc, E_INVALIDARG);
13820 }
13821
13822 /*
13823 * Now grab the object lock, validate the state and do the update.
13824 */
13825 AutoCaller autoCaller(this);
13826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13827
13828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13829
13830 switch (mData->mMachineState)
13831 {
13832 case MachineState_Paused:
13833 case MachineState_Running:
13834 case MachineState_Teleporting:
13835 case MachineState_TeleportingPausedVM:
13836 case MachineState_LiveSnapshotting:
13837 case MachineState_DeletingSnapshotOnline:
13838 case MachineState_DeletingSnapshotPaused:
13839 case MachineState_Saving:
13840 case MachineState_Stopping:
13841 break;
13842
13843 default:
13844 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13845 VBOX_E_INVALID_VM_STATE);
13846 }
13847
13848 setModified(IsModified_MachineData);
13849 mHWData.backup();
13850
13851 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13852 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13853 if (it != mHWData->mGuestProperties.end())
13854 {
13855 if (!fDelete)
13856 {
13857 it->second.strValue = aValue;
13858 it->second.mTimestamp = aTimestamp;
13859 it->second.mFlags = fFlags;
13860 }
13861 else
13862 mHWData->mGuestProperties.erase(it);
13863
13864 mData->mGuestPropertiesModified = TRUE;
13865 }
13866 else if (!fDelete)
13867 {
13868 HWData::GuestProperty prop;
13869 prop.strValue = aValue;
13870 prop.mTimestamp = aTimestamp;
13871 prop.mFlags = fFlags;
13872
13873 mHWData->mGuestProperties[utf8Name] = prop;
13874 mData->mGuestPropertiesModified = TRUE;
13875 }
13876
13877 /*
13878 * Send a callback notification if appropriate
13879 */
13880 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13881 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13882 RTSTR_MAX,
13883 utf8Name.c_str(),
13884 RTSTR_MAX, NULL)
13885 )
13886 {
13887 alock.release();
13888
13889 mParent->onGuestPropertyChange(mData->mUuid,
13890 aName,
13891 aValue,
13892 aFlags);
13893 }
13894 }
13895 catch (...)
13896 {
13897 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13898 }
13899 return S_OK;
13900#else
13901 ReturnComNotImplemented();
13902#endif
13903}
13904
13905STDMETHODIMP SessionMachine::LockMedia()
13906{
13907 AutoCaller autoCaller(this);
13908 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13909
13910 AutoMultiWriteLock2 alock(this->lockHandle(),
13911 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13912
13913 AssertReturn( mData->mMachineState == MachineState_Starting
13914 || mData->mMachineState == MachineState_Restoring
13915 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13916
13917 clearError();
13918 alock.release();
13919 return lockMedia();
13920}
13921
13922STDMETHODIMP SessionMachine::UnlockMedia()
13923{
13924 unlockMedia();
13925 return S_OK;
13926}
13927
13928STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13929 IMediumAttachment **aNewAttachment)
13930{
13931 CheckComArgNotNull(aAttachment);
13932 CheckComArgOutPointerValid(aNewAttachment);
13933
13934 AutoCaller autoCaller(this);
13935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13936
13937 // request the host lock first, since might be calling Host methods for getting host drives;
13938 // next, protect the media tree all the while we're in here, as well as our member variables
13939 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13940 this->lockHandle(),
13941 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13942
13943 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13944
13945 Bstr ctrlName;
13946 LONG lPort;
13947 LONG lDevice;
13948 bool fTempEject;
13949 {
13950 AutoCaller autoAttachCaller(this);
13951 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13952
13953 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13954
13955 /* Need to query the details first, as the IMediumAttachment reference
13956 * might be to the original settings, which we are going to change. */
13957 ctrlName = pAttach->getControllerName();
13958 lPort = pAttach->getPort();
13959 lDevice = pAttach->getDevice();
13960 fTempEject = pAttach->getTempEject();
13961 }
13962
13963 if (!fTempEject)
13964 {
13965 /* Remember previously mounted medium. The medium before taking the
13966 * backup is not necessarily the same thing. */
13967 ComObjPtr<Medium> oldmedium;
13968 oldmedium = pAttach->getMedium();
13969
13970 setModified(IsModified_Storage);
13971 mMediaData.backup();
13972
13973 // The backup operation makes the pAttach reference point to the
13974 // old settings. Re-get the correct reference.
13975 pAttach = findAttachment(mMediaData->mAttachments,
13976 ctrlName.raw(),
13977 lPort,
13978 lDevice);
13979
13980 {
13981 AutoCaller autoAttachCaller(this);
13982 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13983
13984 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13985 if (!oldmedium.isNull())
13986 oldmedium->removeBackReference(mData->mUuid);
13987
13988 pAttach->updateMedium(NULL);
13989 pAttach->updateEjected();
13990 }
13991
13992 setModified(IsModified_Storage);
13993 }
13994 else
13995 {
13996 {
13997 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13998 pAttach->updateEjected();
13999 }
14000 }
14001
14002 pAttach.queryInterfaceTo(aNewAttachment);
14003
14004 return S_OK;
14005}
14006
14007// public methods only for internal purposes
14008/////////////////////////////////////////////////////////////////////////////
14009
14010#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14011/**
14012 * Called from the client watcher thread to check for expected or unexpected
14013 * death of the client process that has a direct session to this machine.
14014 *
14015 * On Win32 and on OS/2, this method is called only when we've got the
14016 * mutex (i.e. the client has either died or terminated normally) so it always
14017 * returns @c true (the client is terminated, the session machine is
14018 * uninitialized).
14019 *
14020 * On other platforms, the method returns @c true if the client process has
14021 * terminated normally or abnormally and the session machine was uninitialized,
14022 * and @c false if the client process is still alive.
14023 *
14024 * @note Locks this object for writing.
14025 */
14026bool SessionMachine::checkForDeath()
14027{
14028 Uninit::Reason reason;
14029 bool terminated = false;
14030
14031 /* Enclose autoCaller with a block because calling uninit() from under it
14032 * will deadlock. */
14033 {
14034 AutoCaller autoCaller(this);
14035 if (!autoCaller.isOk())
14036 {
14037 /* return true if not ready, to cause the client watcher to exclude
14038 * the corresponding session from watching */
14039 LogFlowThisFunc(("Already uninitialized!\n"));
14040 return true;
14041 }
14042
14043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14044
14045 /* Determine the reason of death: if the session state is Closing here,
14046 * everything is fine. Otherwise it means that the client did not call
14047 * OnSessionEnd() before it released the IPC semaphore. This may happen
14048 * either because the client process has abnormally terminated, or
14049 * because it simply forgot to call ISession::Close() before exiting. We
14050 * threat the latter also as an abnormal termination (see
14051 * Session::uninit() for details). */
14052 reason = mData->mSession.mState == SessionState_Unlocking ?
14053 Uninit::Normal :
14054 Uninit::Abnormal;
14055
14056 if (mClientToken)
14057 terminated = mClientToken->release();
14058 } /* AutoCaller block */
14059
14060 if (terminated)
14061 uninit(reason);
14062
14063 return terminated;
14064}
14065
14066void SessionMachine::getTokenId(Utf8Str &strTokenId)
14067{
14068 LogFlowThisFunc(("\n"));
14069
14070 strTokenId.setNull();
14071
14072 AutoCaller autoCaller(this);
14073 AssertComRCReturnVoid(autoCaller.rc());
14074
14075 Assert(mClientToken);
14076 if (mClientToken)
14077 mClientToken->getId(strTokenId);
14078}
14079#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14080IToken *SessionMachine::getToken()
14081{
14082 LogFlowThisFunc(("\n"));
14083
14084 AutoCaller autoCaller(this);
14085 AssertComRCReturn(autoCaller.rc(), NULL);
14086
14087 Assert(mClientToken);
14088 if (mClientToken)
14089 return mClientToken->getToken();
14090 else
14091 return NULL;
14092}
14093#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14094
14095Machine::ClientToken *SessionMachine::getClientToken()
14096{
14097 LogFlowThisFunc(("\n"));
14098
14099 AutoCaller autoCaller(this);
14100 AssertComRCReturn(autoCaller.rc(), NULL);
14101
14102 return mClientToken;
14103}
14104
14105
14106/**
14107 * @note Locks this object for reading.
14108 */
14109HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
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->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14127}
14128
14129/**
14130 * @note Locks this object for reading.
14131 */
14132HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14133 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
14134{
14135 LogFlowThisFunc(("\n"));
14136
14137 AutoCaller autoCaller(this);
14138 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14139
14140 ComPtr<IInternalSessionControl> directControl;
14141 {
14142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14143 directControl = mData->mSession.mDirectControl;
14144 }
14145
14146 /* ignore notifications sent after #OnSessionEnd() is called */
14147 if (!directControl)
14148 return S_OK;
14149 /*
14150 * instead acting like callback we ask IVirtualBox deliver corresponding event
14151 */
14152
14153 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14154 return S_OK;
14155}
14156
14157/**
14158 * @note Locks this object for reading.
14159 */
14160HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14161{
14162 LogFlowThisFunc(("\n"));
14163
14164 AutoCaller autoCaller(this);
14165 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14166
14167 ComPtr<IInternalSessionControl> directControl;
14168 {
14169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14170 directControl = mData->mSession.mDirectControl;
14171 }
14172
14173 /* ignore notifications sent after #OnSessionEnd() is called */
14174 if (!directControl)
14175 return S_OK;
14176
14177 return directControl->OnSerialPortChange(serialPort);
14178}
14179
14180/**
14181 * @note Locks this object for reading.
14182 */
14183HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14184{
14185 LogFlowThisFunc(("\n"));
14186
14187 AutoCaller autoCaller(this);
14188 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14189
14190 ComPtr<IInternalSessionControl> directControl;
14191 {
14192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14193 directControl = mData->mSession.mDirectControl;
14194 }
14195
14196 /* ignore notifications sent after #OnSessionEnd() is called */
14197 if (!directControl)
14198 return S_OK;
14199
14200 return directControl->OnParallelPortChange(parallelPort);
14201}
14202
14203/**
14204 * @note Locks this object for reading.
14205 */
14206HRESULT SessionMachine::onStorageControllerChange()
14207{
14208 LogFlowThisFunc(("\n"));
14209
14210 AutoCaller autoCaller(this);
14211 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14212
14213 ComPtr<IInternalSessionControl> directControl;
14214 {
14215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14216 directControl = mData->mSession.mDirectControl;
14217 }
14218
14219 /* ignore notifications sent after #OnSessionEnd() is called */
14220 if (!directControl)
14221 return S_OK;
14222
14223 return directControl->OnStorageControllerChange();
14224}
14225
14226/**
14227 * @note Locks this object for reading.
14228 */
14229HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14230{
14231 LogFlowThisFunc(("\n"));
14232
14233 AutoCaller autoCaller(this);
14234 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14235
14236 ComPtr<IInternalSessionControl> directControl;
14237 {
14238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14239 directControl = mData->mSession.mDirectControl;
14240 }
14241
14242 /* ignore notifications sent after #OnSessionEnd() is called */
14243 if (!directControl)
14244 return S_OK;
14245
14246 return directControl->OnMediumChange(aAttachment, aForce);
14247}
14248
14249/**
14250 * @note Locks this object for reading.
14251 */
14252HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14253{
14254 LogFlowThisFunc(("\n"));
14255
14256 AutoCaller autoCaller(this);
14257 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14258
14259 ComPtr<IInternalSessionControl> directControl;
14260 {
14261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14262 directControl = mData->mSession.mDirectControl;
14263 }
14264
14265 /* ignore notifications sent after #OnSessionEnd() is called */
14266 if (!directControl)
14267 return S_OK;
14268
14269 return directControl->OnCPUChange(aCPU, aRemove);
14270}
14271
14272HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14273{
14274 LogFlowThisFunc(("\n"));
14275
14276 AutoCaller autoCaller(this);
14277 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14278
14279 ComPtr<IInternalSessionControl> directControl;
14280 {
14281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14282 directControl = mData->mSession.mDirectControl;
14283 }
14284
14285 /* ignore notifications sent after #OnSessionEnd() is called */
14286 if (!directControl)
14287 return S_OK;
14288
14289 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14290}
14291
14292/**
14293 * @note Locks this object for reading.
14294 */
14295HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14296{
14297 LogFlowThisFunc(("\n"));
14298
14299 AutoCaller autoCaller(this);
14300 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14301
14302 ComPtr<IInternalSessionControl> directControl;
14303 {
14304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14305 directControl = mData->mSession.mDirectControl;
14306 }
14307
14308 /* ignore notifications sent after #OnSessionEnd() is called */
14309 if (!directControl)
14310 return S_OK;
14311
14312 return directControl->OnVRDEServerChange(aRestart);
14313}
14314
14315/**
14316 * @note Locks this object for reading.
14317 */
14318HRESULT SessionMachine::onVideoCaptureChange()
14319{
14320 LogFlowThisFunc(("\n"));
14321
14322 AutoCaller autoCaller(this);
14323 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14324
14325 ComPtr<IInternalSessionControl> directControl;
14326 {
14327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14328 directControl = mData->mSession.mDirectControl;
14329 }
14330
14331 /* ignore notifications sent after #OnSessionEnd() is called */
14332 if (!directControl)
14333 return S_OK;
14334
14335 return directControl->OnVideoCaptureChange();
14336}
14337
14338/**
14339 * @note Locks this object for reading.
14340 */
14341HRESULT SessionMachine::onUSBControllerChange()
14342{
14343 LogFlowThisFunc(("\n"));
14344
14345 AutoCaller autoCaller(this);
14346 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14347
14348 ComPtr<IInternalSessionControl> directControl;
14349 {
14350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14351 directControl = mData->mSession.mDirectControl;
14352 }
14353
14354 /* ignore notifications sent after #OnSessionEnd() is called */
14355 if (!directControl)
14356 return S_OK;
14357
14358 return directControl->OnUSBControllerChange();
14359}
14360
14361/**
14362 * @note Locks this object for reading.
14363 */
14364HRESULT SessionMachine::onSharedFolderChange()
14365{
14366 LogFlowThisFunc(("\n"));
14367
14368 AutoCaller autoCaller(this);
14369 AssertComRCReturnRC(autoCaller.rc());
14370
14371 ComPtr<IInternalSessionControl> directControl;
14372 {
14373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14374 directControl = mData->mSession.mDirectControl;
14375 }
14376
14377 /* ignore notifications sent after #OnSessionEnd() is called */
14378 if (!directControl)
14379 return S_OK;
14380
14381 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14382}
14383
14384/**
14385 * @note Locks this object for reading.
14386 */
14387HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14388{
14389 LogFlowThisFunc(("\n"));
14390
14391 AutoCaller autoCaller(this);
14392 AssertComRCReturnRC(autoCaller.rc());
14393
14394 ComPtr<IInternalSessionControl> directControl;
14395 {
14396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14397 directControl = mData->mSession.mDirectControl;
14398 }
14399
14400 /* ignore notifications sent after #OnSessionEnd() is called */
14401 if (!directControl)
14402 return S_OK;
14403
14404 return directControl->OnClipboardModeChange(aClipboardMode);
14405}
14406
14407/**
14408 * @note Locks this object for reading.
14409 */
14410HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14411{
14412 LogFlowThisFunc(("\n"));
14413
14414 AutoCaller autoCaller(this);
14415 AssertComRCReturnRC(autoCaller.rc());
14416
14417 ComPtr<IInternalSessionControl> directControl;
14418 {
14419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14420 directControl = mData->mSession.mDirectControl;
14421 }
14422
14423 /* ignore notifications sent after #OnSessionEnd() is called */
14424 if (!directControl)
14425 return S_OK;
14426
14427 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14428}
14429
14430/**
14431 * @note Locks this object for reading.
14432 */
14433HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14434{
14435 LogFlowThisFunc(("\n"));
14436
14437 AutoCaller autoCaller(this);
14438 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14439
14440 ComPtr<IInternalSessionControl> directControl;
14441 {
14442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14443 directControl = mData->mSession.mDirectControl;
14444 }
14445
14446 /* ignore notifications sent after #OnSessionEnd() is called */
14447 if (!directControl)
14448 return S_OK;
14449
14450 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14451}
14452
14453/**
14454 * @note Locks this object for reading.
14455 */
14456HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14457{
14458 LogFlowThisFunc(("\n"));
14459
14460 AutoCaller autoCaller(this);
14461 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14462
14463 ComPtr<IInternalSessionControl> directControl;
14464 {
14465 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14466 directControl = mData->mSession.mDirectControl;
14467 }
14468
14469 /* ignore notifications sent after #OnSessionEnd() is called */
14470 if (!directControl)
14471 return S_OK;
14472
14473 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14474}
14475
14476/**
14477 * Returns @c true if this machine's USB controller reports it has a matching
14478 * filter for the given USB device and @c false otherwise.
14479 *
14480 * @note locks this object for reading.
14481 */
14482bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14483{
14484 AutoCaller autoCaller(this);
14485 /* silently return if not ready -- this method may be called after the
14486 * direct machine session has been called */
14487 if (!autoCaller.isOk())
14488 return false;
14489
14490#ifdef VBOX_WITH_USB
14491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14492
14493 switch (mData->mMachineState)
14494 {
14495 case MachineState_Starting:
14496 case MachineState_Restoring:
14497 case MachineState_TeleportingIn:
14498 case MachineState_Paused:
14499 case MachineState_Running:
14500 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14501 * elsewhere... */
14502 alock.release();
14503 return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs);
14504 default: break;
14505 }
14506#else
14507 NOREF(aDevice);
14508 NOREF(aMaskedIfs);
14509#endif
14510 return false;
14511}
14512
14513/**
14514 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14515 */
14516HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14517 IVirtualBoxErrorInfo *aError,
14518 ULONG aMaskedIfs)
14519{
14520 LogFlowThisFunc(("\n"));
14521
14522 AutoCaller autoCaller(this);
14523
14524 /* This notification may happen after the machine object has been
14525 * uninitialized (the session was closed), so don't assert. */
14526 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14527
14528 ComPtr<IInternalSessionControl> directControl;
14529 {
14530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14531 directControl = mData->mSession.mDirectControl;
14532 }
14533
14534 /* fail on notifications sent after #OnSessionEnd() is called, it is
14535 * expected by the caller */
14536 if (!directControl)
14537 return E_FAIL;
14538
14539 /* No locks should be held at this point. */
14540 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14541 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14542
14543 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14544}
14545
14546/**
14547 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14548 */
14549HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14550 IVirtualBoxErrorInfo *aError)
14551{
14552 LogFlowThisFunc(("\n"));
14553
14554 AutoCaller autoCaller(this);
14555
14556 /* This notification may happen after the machine object has been
14557 * uninitialized (the session was closed), so don't assert. */
14558 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14559
14560 ComPtr<IInternalSessionControl> directControl;
14561 {
14562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14563 directControl = mData->mSession.mDirectControl;
14564 }
14565
14566 /* fail on notifications sent after #OnSessionEnd() is called, it is
14567 * expected by the caller */
14568 if (!directControl)
14569 return E_FAIL;
14570
14571 /* No locks should be held at this point. */
14572 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14573 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14574
14575 return directControl->OnUSBDeviceDetach(aId, aError);
14576}
14577
14578// protected methods
14579/////////////////////////////////////////////////////////////////////////////
14580
14581/**
14582 * Helper method to finalize saving the state.
14583 *
14584 * @note Must be called from under this object's lock.
14585 *
14586 * @param aRc S_OK if the snapshot has been taken successfully
14587 * @param aErrMsg human readable error message for failure
14588 *
14589 * @note Locks mParent + this objects for writing.
14590 */
14591HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14592{
14593 LogFlowThisFuncEnter();
14594
14595 AutoCaller autoCaller(this);
14596 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14597
14598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14599
14600 HRESULT rc = S_OK;
14601
14602 if (SUCCEEDED(aRc))
14603 {
14604 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14605
14606 /* save all VM settings */
14607 rc = saveSettings(NULL);
14608 // no need to check whether VirtualBox.xml needs saving also since
14609 // we can't have a name change pending at this point
14610 }
14611 else
14612 {
14613 // delete the saved state file (it might have been already created);
14614 // we need not check whether this is shared with a snapshot here because
14615 // we certainly created this saved state file here anew
14616 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14617 }
14618
14619 /* notify the progress object about operation completion */
14620 Assert(mConsoleTaskData.mProgress);
14621 if (SUCCEEDED(aRc))
14622 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14623 else
14624 {
14625 if (aErrMsg.length())
14626 mConsoleTaskData.mProgress->notifyComplete(aRc,
14627 COM_IIDOF(ISession),
14628 getComponentName(),
14629 aErrMsg.c_str());
14630 else
14631 mConsoleTaskData.mProgress->notifyComplete(aRc);
14632 }
14633
14634 /* clear out the temporary saved state data */
14635 mConsoleTaskData.mLastState = MachineState_Null;
14636 mConsoleTaskData.strStateFilePath.setNull();
14637 mConsoleTaskData.mProgress.setNull();
14638
14639 LogFlowThisFuncLeave();
14640 return rc;
14641}
14642
14643/**
14644 * Deletes the given file if it is no longer in use by either the current machine state
14645 * (if the machine is "saved") or any of the machine's snapshots.
14646 *
14647 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14648 * but is different for each SnapshotMachine. When calling this, the order of calling this
14649 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14650 * is therefore critical. I know, it's all rather messy.
14651 *
14652 * @param strStateFile
14653 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14654 */
14655void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14656 Snapshot *pSnapshotToIgnore)
14657{
14658 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14659 if ( (strStateFile.isNotEmpty())
14660 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14661 )
14662 // ... and it must also not be shared with other snapshots
14663 if ( !mData->mFirstSnapshot
14664 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14665 // this checks the SnapshotMachine's state file paths
14666 )
14667 RTFileDelete(strStateFile.c_str());
14668}
14669
14670/**
14671 * Locks the attached media.
14672 *
14673 * All attached hard disks are locked for writing and DVD/floppy are locked for
14674 * reading. Parents of attached hard disks (if any) are locked for reading.
14675 *
14676 * This method also performs accessibility check of all media it locks: if some
14677 * media is inaccessible, the method will return a failure and a bunch of
14678 * extended error info objects per each inaccessible medium.
14679 *
14680 * Note that this method is atomic: if it returns a success, all media are
14681 * locked as described above; on failure no media is locked at all (all
14682 * succeeded individual locks will be undone).
14683 *
14684 * The caller is responsible for doing the necessary state sanity checks.
14685 *
14686 * The locks made by this method must be undone by calling #unlockMedia() when
14687 * no more needed.
14688 */
14689HRESULT SessionMachine::lockMedia()
14690{
14691 AutoCaller autoCaller(this);
14692 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14693
14694 AutoMultiWriteLock2 alock(this->lockHandle(),
14695 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14696
14697 /* bail out if trying to lock things with already set up locking */
14698 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14699
14700 MultiResult mrc(S_OK);
14701
14702 /* Collect locking information for all medium objects attached to the VM. */
14703 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14704 it != mMediaData->mAttachments.end();
14705 ++it)
14706 {
14707 MediumAttachment* pAtt = *it;
14708 DeviceType_T devType = pAtt->getType();
14709 Medium *pMedium = pAtt->getMedium();
14710
14711 MediumLockList *pMediumLockList(new MediumLockList());
14712 // There can be attachments without a medium (floppy/dvd), and thus
14713 // it's impossible to create a medium lock list. It still makes sense
14714 // to have the empty medium lock list in the map in case a medium is
14715 // attached later.
14716 if (pMedium != NULL)
14717 {
14718 MediumType_T mediumType = pMedium->getType();
14719 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14720 || mediumType == MediumType_Shareable;
14721 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14722
14723 alock.release();
14724 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14725 !fIsReadOnlyLock /* fMediumLockWrite */,
14726 NULL,
14727 *pMediumLockList);
14728 alock.acquire();
14729 if (FAILED(mrc))
14730 {
14731 delete pMediumLockList;
14732 mData->mSession.mLockedMedia.Clear();
14733 break;
14734 }
14735 }
14736
14737 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14738 if (FAILED(rc))
14739 {
14740 mData->mSession.mLockedMedia.Clear();
14741 mrc = setError(rc,
14742 tr("Collecting locking information for all attached media failed"));
14743 break;
14744 }
14745 }
14746
14747 if (SUCCEEDED(mrc))
14748 {
14749 /* Now lock all media. If this fails, nothing is locked. */
14750 alock.release();
14751 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14752 alock.acquire();
14753 if (FAILED(rc))
14754 {
14755 mrc = setError(rc,
14756 tr("Locking of attached media failed"));
14757 }
14758 }
14759
14760 return mrc;
14761}
14762
14763/**
14764 * Undoes the locks made by by #lockMedia().
14765 */
14766void SessionMachine::unlockMedia()
14767{
14768 AutoCaller autoCaller(this);
14769 AssertComRCReturnVoid(autoCaller.rc());
14770
14771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14772
14773 /* we may be holding important error info on the current thread;
14774 * preserve it */
14775 ErrorInfoKeeper eik;
14776
14777 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14778 AssertComRC(rc);
14779}
14780
14781/**
14782 * Helper to change the machine state (reimplementation).
14783 *
14784 * @note Locks this object for writing.
14785 * @note This method must not call saveSettings or SaveSettings, otherwise
14786 * it can cause crashes in random places due to unexpectedly committing
14787 * the current settings. The caller is responsible for that. The call
14788 * to saveStateSettings is fine, because this method does not commit.
14789 */
14790HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14791{
14792 LogFlowThisFuncEnter();
14793 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14794
14795 AutoCaller autoCaller(this);
14796 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14797
14798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14799
14800 MachineState_T oldMachineState = mData->mMachineState;
14801
14802 AssertMsgReturn(oldMachineState != aMachineState,
14803 ("oldMachineState=%s, aMachineState=%s\n",
14804 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14805 E_FAIL);
14806
14807 HRESULT rc = S_OK;
14808
14809 int stsFlags = 0;
14810 bool deleteSavedState = false;
14811
14812 /* detect some state transitions */
14813
14814 if ( ( oldMachineState == MachineState_Saved
14815 && aMachineState == MachineState_Restoring)
14816 || ( ( oldMachineState == MachineState_PoweredOff
14817 || oldMachineState == MachineState_Teleported
14818 || oldMachineState == MachineState_Aborted
14819 )
14820 && ( aMachineState == MachineState_TeleportingIn
14821 || aMachineState == MachineState_Starting
14822 )
14823 )
14824 )
14825 {
14826 /* The EMT thread is about to start */
14827
14828 /* Nothing to do here for now... */
14829
14830 /// @todo NEWMEDIA don't let mDVDDrive and other children
14831 /// change anything when in the Starting/Restoring state
14832 }
14833 else if ( ( oldMachineState == MachineState_Running
14834 || oldMachineState == MachineState_Paused
14835 || oldMachineState == MachineState_Teleporting
14836 || oldMachineState == MachineState_LiveSnapshotting
14837 || oldMachineState == MachineState_Stuck
14838 || oldMachineState == MachineState_Starting
14839 || oldMachineState == MachineState_Stopping
14840 || oldMachineState == MachineState_Saving
14841 || oldMachineState == MachineState_Restoring
14842 || oldMachineState == MachineState_TeleportingPausedVM
14843 || oldMachineState == MachineState_TeleportingIn
14844 )
14845 && ( aMachineState == MachineState_PoweredOff
14846 || aMachineState == MachineState_Saved
14847 || aMachineState == MachineState_Teleported
14848 || aMachineState == MachineState_Aborted
14849 )
14850 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14851 * snapshot */
14852 && ( mConsoleTaskData.mSnapshot.isNull()
14853 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14854 )
14855 )
14856 {
14857 /* The EMT thread has just stopped, unlock attached media. Note that as
14858 * opposed to locking that is done from Console, we do unlocking here
14859 * because the VM process may have aborted before having a chance to
14860 * properly unlock all media it locked. */
14861
14862 unlockMedia();
14863 }
14864
14865 if (oldMachineState == MachineState_Restoring)
14866 {
14867 if (aMachineState != MachineState_Saved)
14868 {
14869 /*
14870 * delete the saved state file once the machine has finished
14871 * restoring from it (note that Console sets the state from
14872 * Restoring to Saved if the VM couldn't restore successfully,
14873 * to give the user an ability to fix an error and retry --
14874 * we keep the saved state file in this case)
14875 */
14876 deleteSavedState = true;
14877 }
14878 }
14879 else if ( oldMachineState == MachineState_Saved
14880 && ( aMachineState == MachineState_PoweredOff
14881 || aMachineState == MachineState_Aborted
14882 || aMachineState == MachineState_Teleported
14883 )
14884 )
14885 {
14886 /*
14887 * delete the saved state after Console::ForgetSavedState() is called
14888 * or if the VM process (owning a direct VM session) crashed while the
14889 * VM was Saved
14890 */
14891
14892 /// @todo (dmik)
14893 // Not sure that deleting the saved state file just because of the
14894 // client death before it attempted to restore the VM is a good
14895 // thing. But when it crashes we need to go to the Aborted state
14896 // which cannot have the saved state file associated... The only
14897 // way to fix this is to make the Aborted condition not a VM state
14898 // but a bool flag: i.e., when a crash occurs, set it to true and
14899 // change the state to PoweredOff or Saved depending on the
14900 // saved state presence.
14901
14902 deleteSavedState = true;
14903 mData->mCurrentStateModified = TRUE;
14904 stsFlags |= SaveSTS_CurStateModified;
14905 }
14906
14907 if ( aMachineState == MachineState_Starting
14908 || aMachineState == MachineState_Restoring
14909 || aMachineState == MachineState_TeleportingIn
14910 )
14911 {
14912 /* set the current state modified flag to indicate that the current
14913 * state is no more identical to the state in the
14914 * current snapshot */
14915 if (!mData->mCurrentSnapshot.isNull())
14916 {
14917 mData->mCurrentStateModified = TRUE;
14918 stsFlags |= SaveSTS_CurStateModified;
14919 }
14920 }
14921
14922 if (deleteSavedState)
14923 {
14924 if (mRemoveSavedState)
14925 {
14926 Assert(!mSSData->strStateFilePath.isEmpty());
14927
14928 // it is safe to delete the saved state file if ...
14929 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14930 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14931 // ... none of the snapshots share the saved state file
14932 )
14933 RTFileDelete(mSSData->strStateFilePath.c_str());
14934 }
14935
14936 mSSData->strStateFilePath.setNull();
14937 stsFlags |= SaveSTS_StateFilePath;
14938 }
14939
14940 /* redirect to the underlying peer machine */
14941 mPeer->setMachineState(aMachineState);
14942
14943 if ( aMachineState == MachineState_PoweredOff
14944 || aMachineState == MachineState_Teleported
14945 || aMachineState == MachineState_Aborted
14946 || aMachineState == MachineState_Saved)
14947 {
14948 /* the machine has stopped execution
14949 * (or the saved state file was adopted) */
14950 stsFlags |= SaveSTS_StateTimeStamp;
14951 }
14952
14953 if ( ( oldMachineState == MachineState_PoweredOff
14954 || oldMachineState == MachineState_Aborted
14955 || oldMachineState == MachineState_Teleported
14956 )
14957 && aMachineState == MachineState_Saved)
14958 {
14959 /* the saved state file was adopted */
14960 Assert(!mSSData->strStateFilePath.isEmpty());
14961 stsFlags |= SaveSTS_StateFilePath;
14962 }
14963
14964#ifdef VBOX_WITH_GUEST_PROPS
14965 if ( aMachineState == MachineState_PoweredOff
14966 || aMachineState == MachineState_Aborted
14967 || aMachineState == MachineState_Teleported)
14968 {
14969 /* Make sure any transient guest properties get removed from the
14970 * property store on shutdown. */
14971
14972 HWData::GuestPropertyMap::const_iterator it;
14973 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14974 if (!fNeedsSaving)
14975 for (it = mHWData->mGuestProperties.begin();
14976 it != mHWData->mGuestProperties.end(); ++it)
14977 if ( (it->second.mFlags & guestProp::TRANSIENT)
14978 || (it->second.mFlags & guestProp::TRANSRESET))
14979 {
14980 fNeedsSaving = true;
14981 break;
14982 }
14983 if (fNeedsSaving)
14984 {
14985 mData->mCurrentStateModified = TRUE;
14986 stsFlags |= SaveSTS_CurStateModified;
14987 }
14988 }
14989#endif
14990
14991 rc = saveStateSettings(stsFlags);
14992
14993 if ( ( oldMachineState != MachineState_PoweredOff
14994 && oldMachineState != MachineState_Aborted
14995 && oldMachineState != MachineState_Teleported
14996 )
14997 && ( aMachineState == MachineState_PoweredOff
14998 || aMachineState == MachineState_Aborted
14999 || aMachineState == MachineState_Teleported
15000 )
15001 )
15002 {
15003 /* we've been shut down for any reason */
15004 /* no special action so far */
15005 }
15006
15007 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15008 LogFlowThisFuncLeave();
15009 return rc;
15010}
15011
15012/**
15013 * Sends the current machine state value to the VM process.
15014 *
15015 * @note Locks this object for reading, then calls a client process.
15016 */
15017HRESULT SessionMachine::updateMachineStateOnClient()
15018{
15019 AutoCaller autoCaller(this);
15020 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15021
15022 ComPtr<IInternalSessionControl> directControl;
15023 {
15024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15025 AssertReturn(!!mData, E_FAIL);
15026 directControl = mData->mSession.mDirectControl;
15027
15028 /* directControl may be already set to NULL here in #OnSessionEnd()
15029 * called too early by the direct session process while there is still
15030 * some operation (like deleting the snapshot) in progress. The client
15031 * process in this case is waiting inside Session::close() for the
15032 * "end session" process object to complete, while #uninit() called by
15033 * #checkForDeath() on the Watcher thread is waiting for the pending
15034 * operation to complete. For now, we accept this inconsistent behavior
15035 * and simply do nothing here. */
15036
15037 if (mData->mSession.mState == SessionState_Unlocking)
15038 return S_OK;
15039
15040 AssertReturn(!directControl.isNull(), E_FAIL);
15041 }
15042
15043 return directControl->UpdateMachineState(mData->mMachineState);
15044}
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