VirtualBox

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

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

Main: Added a TripleFaultReset switch.

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

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