VirtualBox

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

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

Main/VPX: introduced onVideoCaptureChange event

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 490.6 KB
Line 
1/* $Id: MachineImpl.cpp 46465 2013-06-10 14:11:26Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76#include <iprt/base64.h>
77
78#include <VBox/com/array.h>
79#include <VBox/com/list.h>
80
81#include <VBox/err.h>
82#include <VBox/param.h>
83#include <VBox/settings.h>
84#include <VBox/vmm/ssm.h>
85
86#ifdef VBOX_WITH_GUEST_PROPS
87# include <VBox/HostServices/GuestPropertySvc.h>
88# include <VBox/com/array.h>
89#endif
90
91#include "VBox/com/MultiResult.h"
92
93#include <algorithm>
94
95#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
96# define HOSTSUFF_EXE ".exe"
97#else /* !RT_OS_WINDOWS */
98# define HOSTSUFF_EXE ""
99#endif /* !RT_OS_WINDOWS */
100
101// defines / prototypes
102/////////////////////////////////////////////////////////////////////////////
103
104/////////////////////////////////////////////////////////////////////////////
105// Machine::Data structure
106/////////////////////////////////////////////////////////////////////////////
107
108Machine::Data::Data()
109{
110 mRegistered = FALSE;
111 pMachineConfigFile = NULL;
112 /* Contains hints on what has changed when the user is using the VM (config
113 * changes, running the VM, ...). This is used to decide if a config needs
114 * to be written to disk. */
115 flModifications = 0;
116 /* VM modification usually also trigger setting the current state to
117 * "Modified". Although this is not always the case. An e.g. is the VM
118 * initialization phase or when snapshot related data is changed. The
119 * actually behavior is controlled by the following flag. */
120 m_fAllowStateModification = false;
121 mAccessible = FALSE;
122 /* mUuid is initialized in Machine::init() */
123
124 mMachineState = MachineState_PoweredOff;
125 RTTimeNow(&mLastStateChange);
126
127 mMachineStateDeps = 0;
128 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
129 mMachineStateChangePending = 0;
130
131 mCurrentStateModified = TRUE;
132 mGuestPropertiesModified = FALSE;
133
134 mSession.mPID = NIL_RTPROCESS;
135 mSession.mState = SessionState_Unlocked;
136}
137
138Machine::Data::~Data()
139{
140 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
141 {
142 RTSemEventMultiDestroy(mMachineStateDepsSem);
143 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
144 }
145 if (pMachineConfigFile)
146 {
147 delete pMachineConfigFile;
148 pMachineConfigFile = NULL;
149 }
150}
151
152/////////////////////////////////////////////////////////////////////////////
153// Machine::HWData structure
154/////////////////////////////////////////////////////////////////////////////
155
156Machine::HWData::HWData()
157{
158 /* default values for a newly created machine */
159 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
160 mMemorySize = 128;
161 mCPUCount = 1;
162 mCPUHotPlugEnabled = false;
163 mMemoryBalloonSize = 0;
164 mPageFusionEnabled = false;
165 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
166 mVRAMSize = 8;
167 mAccelerate3DEnabled = false;
168 mAccelerate2DVideoEnabled = false;
169 mMonitorCount = 1;
170 mVideoCaptureFile = "Test.webm";
171 mVideoCaptureWidth = 1024;
172 mVideoCaptureHeight = 768;
173 mVideoCaptureRate = 512;
174 mVideoCaptureFps = 25;
175 mVideoCaptureEnabled = false;
176 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
177 maVideoCaptureScreens[i] = true;
178
179 mHWVirtExEnabled = true;
180 mHWVirtExNestedPagingEnabled = true;
181#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
182 mHWVirtExLargePagesEnabled = true;
183#else
184 /* Not supported on 32 bits hosts. */
185 mHWVirtExLargePagesEnabled = false;
186#endif
187 mHWVirtExVPIDEnabled = true;
188 mHWVirtExUXEnabled = true;
189 mHWVirtExForceEnabled = false;
190#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
191 mHWVirtExExclusive = false;
192#else
193 mHWVirtExExclusive = true;
194#endif
195#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
196 mPAEEnabled = true;
197#else
198 mPAEEnabled = false;
199#endif
200 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
201 mSyntheticCpu = false;
202 mHPETEnabled = false;
203
204 /* default boot order: floppy - DVD - HDD */
205 mBootOrder[0] = DeviceType_Floppy;
206 mBootOrder[1] = DeviceType_DVD;
207 mBootOrder[2] = DeviceType_HardDisk;
208 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
209 mBootOrder[i] = DeviceType_Null;
210
211 mClipboardMode = ClipboardMode_Disabled;
212 mDragAndDropMode = DragAndDropMode_Disabled;
213 mGuestPropertyNotificationPatterns = "";
214
215 mFirmwareType = FirmwareType_BIOS;
216 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
217 mPointingHIDType = PointingHIDType_PS2Mouse;
218 mChipsetType = ChipsetType_PIIX3;
219 mEmulatedUSBWebcamEnabled = FALSE;
220 mEmulatedUSBCardReaderEnabled = FALSE;
221
222 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
223 mCPUAttached[i] = false;
224
225 mIOCacheEnabled = true;
226 mIOCacheSize = 5; /* 5MB */
227
228 /* Maximum CPU execution cap by default. */
229 mCpuExecutionCap = 100;
230}
231
232Machine::HWData::~HWData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine::HDData structure
238/////////////////////////////////////////////////////////////////////////////
239
240Machine::MediaData::MediaData()
241{
242}
243
244Machine::MediaData::~MediaData()
245{
246}
247
248/////////////////////////////////////////////////////////////////////////////
249// Machine class
250/////////////////////////////////////////////////////////////////////////////
251
252// constructor / destructor
253/////////////////////////////////////////////////////////////////////////////
254
255Machine::Machine()
256 : mCollectorGuest(NULL),
257 mPeer(NULL),
258 mParent(NULL),
259 mSerialPorts(),
260 mParallelPorts(),
261 uRegistryNeedsSaving(0)
262{}
263
264Machine::~Machine()
265{}
266
267HRESULT Machine::FinalConstruct()
268{
269 LogFlowThisFunc(("\n"));
270 return BaseFinalConstruct();
271}
272
273void Machine::FinalRelease()
274{
275 LogFlowThisFunc(("\n"));
276 uninit();
277 BaseFinalRelease();
278}
279
280/**
281 * Initializes a new machine instance; this init() variant creates a new, empty machine.
282 * This gets called from VirtualBox::CreateMachine().
283 *
284 * @param aParent Associated parent object
285 * @param strConfigFile Local file system path to the VM settings file (can
286 * be relative to the VirtualBox config directory).
287 * @param strName name for the machine
288 * @param llGroups list of groups for the machine
289 * @param aOsType OS Type of this machine or NULL.
290 * @param aId UUID for the new machine.
291 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
292 *
293 * @return Success indicator. if not S_OK, the machine object is invalid
294 */
295HRESULT Machine::init(VirtualBox *aParent,
296 const Utf8Str &strConfigFile,
297 const Utf8Str &strName,
298 const StringsList &llGroups,
299 GuestOSType *aOsType,
300 const Guid &aId,
301 bool fForceOverwrite,
302 bool fDirectoryIncludesUUID)
303{
304 LogFlowThisFuncEnter();
305 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
306
307 /* Enclose the state transition NotReady->InInit->Ready */
308 AutoInitSpan autoInitSpan(this);
309 AssertReturn(autoInitSpan.isOk(), E_FAIL);
310
311 HRESULT rc = initImpl(aParent, strConfigFile);
312 if (FAILED(rc)) return rc;
313
314 rc = tryCreateMachineConfigFile(fForceOverwrite);
315 if (FAILED(rc)) return rc;
316
317 if (SUCCEEDED(rc))
318 {
319 // create an empty machine config
320 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
321
322 rc = initDataAndChildObjects();
323 }
324
325 if (SUCCEEDED(rc))
326 {
327 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
328 mData->mAccessible = TRUE;
329
330 unconst(mData->mUuid) = aId;
331
332 mUserData->s.strName = strName;
333
334 mUserData->s.llGroups = llGroups;
335
336 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
337 // the "name sync" flag determines whether the machine directory gets renamed along
338 // with the machine file; say so if the settings file name is the same as the
339 // settings file parent directory (machine directory)
340 mUserData->s.fNameSync = isInOwnDir();
341
342 // initialize the default snapshots folder
343 rc = COMSETTER(SnapshotFolder)(NULL);
344 AssertComRC(rc);
345
346 if (aOsType)
347 {
348 /* Store OS type */
349 mUserData->s.strOsType = aOsType->id();
350
351 /* Apply BIOS defaults */
352 mBIOSSettings->applyDefaults(aOsType);
353
354 /* Apply network adapters defaults */
355 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
356 mNetworkAdapters[slot]->applyDefaults(aOsType);
357
358 /* Apply serial port defaults */
359 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
360 mSerialPorts[slot]->applyDefaults(aOsType);
361
362 /* Let the OS type select 64-bit ness. */
363 mHWData->mLongMode = aOsType->is64Bit()
364 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
365 }
366
367 /* At this point the changing of the current state modification
368 * flag is allowed. */
369 allowStateModification();
370
371 /* commit all changes made during the initialization */
372 commit();
373 }
374
375 /* Confirm a successful initialization when it's the case */
376 if (SUCCEEDED(rc))
377 {
378 if (mData->mAccessible)
379 autoInitSpan.setSucceeded();
380 else
381 autoInitSpan.setLimited();
382 }
383
384 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
385 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
386 mData->mRegistered,
387 mData->mAccessible,
388 rc));
389
390 LogFlowThisFuncLeave();
391
392 return rc;
393}
394
395/**
396 * Initializes a new instance with data from machine XML (formerly Init_Registered).
397 * Gets called in two modes:
398 *
399 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
400 * UUID is specified and we mark the machine as "registered";
401 *
402 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
403 * and the machine remains unregistered until RegisterMachine() is called.
404 *
405 * @param aParent Associated parent object
406 * @param aConfigFile Local file system path to the VM settings file (can
407 * be relative to the VirtualBox config directory).
408 * @param aId UUID of the machine or NULL (see above).
409 *
410 * @return Success indicator. if not S_OK, the machine object is invalid
411 */
412HRESULT Machine::initFromSettings(VirtualBox *aParent,
413 const Utf8Str &strConfigFile,
414 const Guid *aId)
415{
416 LogFlowThisFuncEnter();
417 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
418
419 /* Enclose the state transition NotReady->InInit->Ready */
420 AutoInitSpan autoInitSpan(this);
421 AssertReturn(autoInitSpan.isOk(), E_FAIL);
422
423 HRESULT rc = initImpl(aParent, strConfigFile);
424 if (FAILED(rc)) return rc;
425
426 if (aId)
427 {
428 // loading a registered VM:
429 unconst(mData->mUuid) = *aId;
430 mData->mRegistered = TRUE;
431 // now load the settings from XML:
432 rc = registeredInit();
433 // this calls initDataAndChildObjects() and loadSettings()
434 }
435 else
436 {
437 // opening an unregistered VM (VirtualBox::OpenMachine()):
438 rc = initDataAndChildObjects();
439
440 if (SUCCEEDED(rc))
441 {
442 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
443 mData->mAccessible = TRUE;
444
445 try
446 {
447 // load and parse machine XML; this will throw on XML or logic errors
448 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
449
450 // reject VM UUID duplicates, they can happen if someone
451 // tries to register an already known VM config again
452 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
453 true /* fPermitInaccessible */,
454 false /* aDoSetError */,
455 NULL) != VBOX_E_OBJECT_NOT_FOUND)
456 {
457 throw setError(E_FAIL,
458 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
459 mData->m_strConfigFile.c_str());
460 }
461
462 // use UUID from machine config
463 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
464
465 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
466 NULL /* puuidRegistry */);
467 if (FAILED(rc)) throw rc;
468
469 /* At this point the changing of the current state modification
470 * flag is allowed. */
471 allowStateModification();
472
473 commit();
474 }
475 catch (HRESULT err)
476 {
477 /* we assume that error info is set by the thrower */
478 rc = err;
479 }
480 catch (...)
481 {
482 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
483 }
484 }
485 }
486
487 /* Confirm a successful initialization when it's the case */
488 if (SUCCEEDED(rc))
489 {
490 if (mData->mAccessible)
491 autoInitSpan.setSucceeded();
492 else
493 {
494 autoInitSpan.setLimited();
495
496 // uninit media from this machine's media registry, or else
497 // reloading the settings will fail
498 mParent->unregisterMachineMedia(getId());
499 }
500 }
501
502 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
503 "rc=%08X\n",
504 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
505 mData->mRegistered, mData->mAccessible, rc));
506
507 LogFlowThisFuncLeave();
508
509 return rc;
510}
511
512/**
513 * Initializes a new instance from a machine config that is already in memory
514 * (import OVF case). Since we are importing, the UUID in the machine
515 * config is ignored and we always generate a fresh one.
516 *
517 * @param strName Name for the new machine; this overrides what is specified in config and is used
518 * for the settings file as well.
519 * @param config Machine configuration loaded and parsed from XML.
520 *
521 * @return Success indicator. if not S_OK, the machine object is invalid
522 */
523HRESULT Machine::init(VirtualBox *aParent,
524 const Utf8Str &strName,
525 const settings::MachineConfigFile &config)
526{
527 LogFlowThisFuncEnter();
528
529 /* Enclose the state transition NotReady->InInit->Ready */
530 AutoInitSpan autoInitSpan(this);
531 AssertReturn(autoInitSpan.isOk(), E_FAIL);
532
533 Utf8Str strConfigFile;
534 aParent->getDefaultMachineFolder(strConfigFile);
535 strConfigFile.append(RTPATH_DELIMITER);
536 strConfigFile.append(strName);
537 strConfigFile.append(RTPATH_DELIMITER);
538 strConfigFile.append(strName);
539 strConfigFile.append(".vbox");
540
541 HRESULT rc = initImpl(aParent, strConfigFile);
542 if (FAILED(rc)) return rc;
543
544 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
545 if (FAILED(rc)) return rc;
546
547 rc = initDataAndChildObjects();
548
549 if (SUCCEEDED(rc))
550 {
551 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
552 mData->mAccessible = TRUE;
553
554 // create empty machine config for instance data
555 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
556
557 // generate fresh UUID, ignore machine config
558 unconst(mData->mUuid).create();
559
560 rc = loadMachineDataFromSettings(config,
561 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
562
563 // override VM name as well, it may be different
564 mUserData->s.strName = strName;
565
566 if (SUCCEEDED(rc))
567 {
568 /* At this point the changing of the current state modification
569 * flag is allowed. */
570 allowStateModification();
571
572 /* commit all changes made during the initialization */
573 commit();
574 }
575 }
576
577 /* Confirm a successful initialization when it's the case */
578 if (SUCCEEDED(rc))
579 {
580 if (mData->mAccessible)
581 autoInitSpan.setSucceeded();
582 else
583 {
584 autoInitSpan.setLimited();
585
586 // uninit media from this machine's media registry, or else
587 // reloading the settings will fail
588 mParent->unregisterMachineMedia(getId());
589 }
590 }
591
592 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
593 "rc=%08X\n",
594 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
595 mData->mRegistered, mData->mAccessible, rc));
596
597 LogFlowThisFuncLeave();
598
599 return rc;
600}
601
602/**
603 * Shared code between the various init() implementations.
604 * @param aParent
605 * @return
606 */
607HRESULT Machine::initImpl(VirtualBox *aParent,
608 const Utf8Str &strConfigFile)
609{
610 LogFlowThisFuncEnter();
611
612 AssertReturn(aParent, E_INVALIDARG);
613 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
614
615 HRESULT rc = S_OK;
616
617 /* share the parent weakly */
618 unconst(mParent) = aParent;
619
620 /* allocate the essential machine data structure (the rest will be
621 * allocated later by initDataAndChildObjects() */
622 mData.allocate();
623
624 /* memorize the config file name (as provided) */
625 mData->m_strConfigFile = strConfigFile;
626
627 /* get the full file name */
628 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
629 if (RT_FAILURE(vrc1))
630 return setError(VBOX_E_FILE_ERROR,
631 tr("Invalid machine settings file name '%s' (%Rrc)"),
632 strConfigFile.c_str(),
633 vrc1);
634
635 LogFlowThisFuncLeave();
636
637 return rc;
638}
639
640/**
641 * Tries to create a machine settings file in the path stored in the machine
642 * instance data. Used when a new machine is created to fail gracefully if
643 * the settings file could not be written (e.g. because machine dir is read-only).
644 * @return
645 */
646HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
647{
648 HRESULT rc = S_OK;
649
650 // when we create a new machine, we must be able to create the settings file
651 RTFILE f = NIL_RTFILE;
652 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
653 if ( RT_SUCCESS(vrc)
654 || vrc == VERR_SHARING_VIOLATION
655 )
656 {
657 if (RT_SUCCESS(vrc))
658 RTFileClose(f);
659 if (!fForceOverwrite)
660 rc = setError(VBOX_E_FILE_ERROR,
661 tr("Machine settings file '%s' already exists"),
662 mData->m_strConfigFileFull.c_str());
663 else
664 {
665 /* try to delete the config file, as otherwise the creation
666 * of a new settings file will fail. */
667 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
668 if (RT_FAILURE(vrc2))
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Could not delete the existing settings file '%s' (%Rrc)"),
671 mData->m_strConfigFileFull.c_str(), vrc2);
672 }
673 }
674 else if ( vrc != VERR_FILE_NOT_FOUND
675 && vrc != VERR_PATH_NOT_FOUND
676 )
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Invalid machine settings file name '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(),
680 vrc);
681 return rc;
682}
683
684/**
685 * Initializes the registered machine by loading the settings file.
686 * This method is separated from #init() in order to make it possible to
687 * retry the operation after VirtualBox startup instead of refusing to
688 * startup the whole VirtualBox server in case if the settings file of some
689 * registered VM is invalid or inaccessible.
690 *
691 * @note Must be always called from this object's write lock
692 * (unless called from #init() that doesn't need any locking).
693 * @note Locks the mUSBController method for writing.
694 * @note Subclasses must not call this method.
695 */
696HRESULT Machine::registeredInit()
697{
698 AssertReturn(!isSessionMachine(), E_FAIL);
699 AssertReturn(!isSnapshotMachine(), E_FAIL);
700 AssertReturn(mData->mUuid.isValid(), E_FAIL);
701 AssertReturn(!mData->mAccessible, E_FAIL);
702
703 HRESULT rc = initDataAndChildObjects();
704
705 if (SUCCEEDED(rc))
706 {
707 /* Temporarily reset the registered flag in order to let setters
708 * potentially called from loadSettings() succeed (isMutable() used in
709 * all setters will return FALSE for a Machine instance if mRegistered
710 * is TRUE). */
711 mData->mRegistered = FALSE;
712
713 try
714 {
715 // load and parse machine XML; this will throw on XML or logic errors
716 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
717
718 if (mData->mUuid != mData->pMachineConfigFile->uuid)
719 throw setError(E_FAIL,
720 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
721 mData->pMachineConfigFile->uuid.raw(),
722 mData->m_strConfigFileFull.c_str(),
723 mData->mUuid.toString().c_str(),
724 mParent->settingsFilePath().c_str());
725
726 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
727 NULL /* const Guid *puuidRegistry */);
728 if (FAILED(rc)) throw rc;
729 }
730 catch (HRESULT err)
731 {
732 /* we assume that error info is set by the thrower */
733 rc = err;
734 }
735 catch (...)
736 {
737 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
738 }
739
740 /* Restore the registered flag (even on failure) */
741 mData->mRegistered = TRUE;
742 }
743
744 if (SUCCEEDED(rc))
745 {
746 /* Set mAccessible to TRUE only if we successfully locked and loaded
747 * the settings file */
748 mData->mAccessible = TRUE;
749
750 /* commit all changes made during loading the settings file */
751 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
752 /// @todo r=klaus for some reason the settings loading logic backs up
753 // the settings, and therefore a commit is needed. Should probably be changed.
754 }
755 else
756 {
757 /* If the machine is registered, then, instead of returning a
758 * failure, we mark it as inaccessible and set the result to
759 * success to give it a try later */
760
761 /* fetch the current error info */
762 mData->mAccessError = com::ErrorInfo();
763 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
764 mData->mUuid.raw(),
765 mData->mAccessError.getText().raw()));
766
767 /* rollback all changes */
768 rollback(false /* aNotify */);
769
770 // uninit media from this machine's media registry, or else
771 // reloading the settings will fail
772 mParent->unregisterMachineMedia(getId());
773
774 /* uninitialize the common part to make sure all data is reset to
775 * default (null) values */
776 uninitDataAndChildObjects();
777
778 rc = S_OK;
779 }
780
781 return rc;
782}
783
784/**
785 * Uninitializes the instance.
786 * Called either from FinalRelease() or by the parent when it gets destroyed.
787 *
788 * @note The caller of this method must make sure that this object
789 * a) doesn't have active callers on the current thread and b) is not locked
790 * by the current thread; otherwise uninit() will hang either a) due to
791 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
792 * a dead-lock caused by this thread waiting for all callers on the other
793 * threads are done but preventing them from doing so by holding a lock.
794 */
795void Machine::uninit()
796{
797 LogFlowThisFuncEnter();
798
799 Assert(!isWriteLockOnCurrentThread());
800
801 Assert(!uRegistryNeedsSaving);
802 if (uRegistryNeedsSaving)
803 {
804 AutoCaller autoCaller(this);
805 if (SUCCEEDED(autoCaller.rc()))
806 {
807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
808 saveSettings(NULL, Machine::SaveS_Force);
809 }
810 }
811
812 /* Enclose the state transition Ready->InUninit->NotReady */
813 AutoUninitSpan autoUninitSpan(this);
814 if (autoUninitSpan.uninitDone())
815 return;
816
817 Assert(!isSnapshotMachine());
818 Assert(!isSessionMachine());
819 Assert(!!mData);
820
821 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
822 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
823
824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
825
826 if (!mData->mSession.mMachine.isNull())
827 {
828 /* Theoretically, this can only happen if the VirtualBox server has been
829 * terminated while there were clients running that owned open direct
830 * sessions. Since in this case we are definitely called by
831 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
832 * won't happen on the client watcher thread (because it does
833 * VirtualBox::addCaller() for the duration of the
834 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
835 * cannot happen until the VirtualBox caller is released). This is
836 * important, because SessionMachine::uninit() cannot correctly operate
837 * after we return from this method (it expects the Machine instance is
838 * still valid). We'll call it ourselves below.
839 */
840 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
841 (SessionMachine*)mData->mSession.mMachine));
842
843 if (Global::IsOnlineOrTransient(mData->mMachineState))
844 {
845 LogWarningThisFunc(("Setting state to Aborted!\n"));
846 /* set machine state using SessionMachine reimplementation */
847 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
848 }
849
850 /*
851 * Uninitialize SessionMachine using public uninit() to indicate
852 * an unexpected uninitialization.
853 */
854 mData->mSession.mMachine->uninit();
855 /* SessionMachine::uninit() must set mSession.mMachine to null */
856 Assert(mData->mSession.mMachine.isNull());
857 }
858
859 // uninit media from this machine's media registry, if they're still there
860 Guid uuidMachine(getId());
861
862 /* the lock is no more necessary (SessionMachine is uninitialized) */
863 alock.release();
864
865 /* XXX This will fail with
866 * "cannot be closed because it is still attached to 1 virtual machines"
867 * because at this point we did not call uninitDataAndChildObjects() yet
868 * and therefore also removeBackReference() for all these mediums was not called! */
869
870 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
871 mParent->unregisterMachineMedia(uuidMachine);
872
873 // has machine been modified?
874 if (mData->flModifications)
875 {
876 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
877 rollback(false /* aNotify */);
878 }
879
880 if (mData->mAccessible)
881 uninitDataAndChildObjects();
882
883 /* free the essential data structure last */
884 mData.free();
885
886 LogFlowThisFuncLeave();
887}
888
889// IMachine properties
890/////////////////////////////////////////////////////////////////////////////
891
892STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
893{
894 CheckComArgOutPointerValid(aParent);
895
896 AutoLimitedCaller autoCaller(this);
897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
898
899 /* mParent is constant during life time, no need to lock */
900 ComObjPtr<VirtualBox> pVirtualBox(mParent);
901 pVirtualBox.queryInterfaceTo(aParent);
902
903 return S_OK;
904}
905
906STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
907{
908 CheckComArgOutPointerValid(aAccessible);
909
910 AutoLimitedCaller autoCaller(this);
911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
912
913 LogFlowThisFunc(("ENTER\n"));
914
915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
916
917 HRESULT rc = S_OK;
918
919 if (!mData->mAccessible)
920 {
921 /* try to initialize the VM once more if not accessible */
922
923 AutoReinitSpan autoReinitSpan(this);
924 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
925
926#ifdef DEBUG
927 LogFlowThisFunc(("Dumping media backreferences\n"));
928 mParent->dumpAllBackRefs();
929#endif
930
931 if (mData->pMachineConfigFile)
932 {
933 // reset the XML file to force loadSettings() (called from registeredInit())
934 // to parse it again; the file might have changed
935 delete mData->pMachineConfigFile;
936 mData->pMachineConfigFile = NULL;
937 }
938
939 rc = registeredInit();
940
941 if (SUCCEEDED(rc) && mData->mAccessible)
942 {
943 autoReinitSpan.setSucceeded();
944
945 /* make sure interesting parties will notice the accessibility
946 * state change */
947 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
948 mParent->onMachineDataChange(mData->mUuid);
949 }
950 }
951
952 if (SUCCEEDED(rc))
953 *aAccessible = mData->mAccessible;
954
955 LogFlowThisFuncLeave();
956
957 return rc;
958}
959
960STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
961{
962 CheckComArgOutPointerValid(aAccessError);
963
964 AutoLimitedCaller autoCaller(this);
965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
966
967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
968
969 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
970 {
971 /* return shortly */
972 aAccessError = NULL;
973 return S_OK;
974 }
975
976 HRESULT rc = S_OK;
977
978 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
979 rc = errorInfo.createObject();
980 if (SUCCEEDED(rc))
981 {
982 errorInfo->init(mData->mAccessError.getResultCode(),
983 mData->mAccessError.getInterfaceID().ref(),
984 Utf8Str(mData->mAccessError.getComponent()).c_str(),
985 Utf8Str(mData->mAccessError.getText()));
986 rc = errorInfo.queryInterfaceTo(aAccessError);
987 }
988
989 return rc;
990}
991
992STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
993{
994 CheckComArgOutPointerValid(aName);
995
996 AutoCaller autoCaller(this);
997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
998
999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 mUserData->s.strName.cloneTo(aName);
1002
1003 return S_OK;
1004}
1005
1006STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1007{
1008 CheckComArgStrNotEmptyOrNull(aName);
1009
1010 AutoCaller autoCaller(this);
1011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1012
1013 // prohibit setting a UUID only as the machine name, or else it can
1014 // never be found by findMachine()
1015 Guid test(aName);
1016
1017 if (test.isValid())
1018 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1019
1020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 HRESULT rc = checkStateDependency(MutableStateDep);
1023 if (FAILED(rc)) return rc;
1024
1025 setModified(IsModified_MachineData);
1026 mUserData.backup();
1027 mUserData->s.strName = aName;
1028
1029 return S_OK;
1030}
1031
1032STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1033{
1034 CheckComArgOutPointerValid(aDescription);
1035
1036 AutoCaller autoCaller(this);
1037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1038
1039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 mUserData->s.strDescription.cloneTo(aDescription);
1042
1043 return S_OK;
1044}
1045
1046STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1047{
1048 AutoCaller autoCaller(this);
1049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1050
1051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1052
1053 // this can be done in principle in any state as it doesn't affect the VM
1054 // significantly, but play safe by not messing around while complex
1055 // activities are going on
1056 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1057 if (FAILED(rc)) return rc;
1058
1059 setModified(IsModified_MachineData);
1060 mUserData.backup();
1061 mUserData->s.strDescription = aDescription;
1062
1063 return S_OK;
1064}
1065
1066STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1067{
1068 CheckComArgOutPointerValid(aId);
1069
1070 AutoLimitedCaller autoCaller(this);
1071 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1072
1073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1074
1075 mData->mUuid.toUtf16().cloneTo(aId);
1076
1077 return S_OK;
1078}
1079
1080STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1081{
1082 CheckComArgOutSafeArrayPointerValid(aGroups);
1083
1084 AutoCaller autoCaller(this);
1085 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1086
1087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1088 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1089 size_t i = 0;
1090 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1091 it != mUserData->s.llGroups.end();
1092 ++it, i++)
1093 {
1094 Bstr tmp = *it;
1095 tmp.cloneTo(&groups[i]);
1096 }
1097 groups.detachTo(ComSafeArrayOutArg(aGroups));
1098
1099 return S_OK;
1100}
1101
1102STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1103{
1104 AutoCaller autoCaller(this);
1105 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1106
1107 StringsList llGroups;
1108 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1109 if (FAILED(rc))
1110 return rc;
1111
1112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1113
1114 // changing machine groups is possible while the VM is offline
1115 rc = checkStateDependency(OfflineStateDep);
1116 if (FAILED(rc)) return rc;
1117
1118 setModified(IsModified_MachineData);
1119 mUserData.backup();
1120 mUserData->s.llGroups = llGroups;
1121
1122 return S_OK;
1123}
1124
1125STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1126{
1127 CheckComArgOutPointerValid(aOSTypeId);
1128
1129 AutoCaller autoCaller(this);
1130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1131
1132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1133
1134 mUserData->s.strOsType.cloneTo(aOSTypeId);
1135
1136 return S_OK;
1137}
1138
1139STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1140{
1141 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1142
1143 AutoCaller autoCaller(this);
1144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1145
1146 /* look up the object by Id to check it is valid */
1147 ComPtr<IGuestOSType> guestOSType;
1148 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1149 if (FAILED(rc)) return rc;
1150
1151 /* when setting, always use the "etalon" value for consistency -- lookup
1152 * by ID is case-insensitive and the input value may have different case */
1153 Bstr osTypeId;
1154 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1155 if (FAILED(rc)) return rc;
1156
1157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1158
1159 rc = checkStateDependency(MutableStateDep);
1160 if (FAILED(rc)) return rc;
1161
1162 setModified(IsModified_MachineData);
1163 mUserData.backup();
1164 mUserData->s.strOsType = osTypeId;
1165
1166 return S_OK;
1167}
1168
1169
1170STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1171{
1172 CheckComArgOutPointerValid(aFirmwareType);
1173
1174 AutoCaller autoCaller(this);
1175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1176
1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 *aFirmwareType = mHWData->mFirmwareType;
1180
1181 return S_OK;
1182}
1183
1184STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1185{
1186 AutoCaller autoCaller(this);
1187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 setModified(IsModified_MachineData);
1194 mHWData.backup();
1195 mHWData->mFirmwareType = aFirmwareType;
1196
1197 return S_OK;
1198}
1199
1200STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1201{
1202 CheckComArgOutPointerValid(aKeyboardHIDType);
1203
1204 AutoCaller autoCaller(this);
1205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1206
1207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1208
1209 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1210
1211 return S_OK;
1212}
1213
1214STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1215{
1216 AutoCaller autoCaller(this);
1217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1219
1220 HRESULT rc = checkStateDependency(MutableStateDep);
1221 if (FAILED(rc)) return rc;
1222
1223 setModified(IsModified_MachineData);
1224 mHWData.backup();
1225 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1226
1227 return S_OK;
1228}
1229
1230STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1231{
1232 CheckComArgOutPointerValid(aPointingHIDType);
1233
1234 AutoCaller autoCaller(this);
1235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1236
1237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1238
1239 *aPointingHIDType = mHWData->mPointingHIDType;
1240
1241 return S_OK;
1242}
1243
1244STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1245{
1246 AutoCaller autoCaller(this);
1247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1249
1250 HRESULT rc = checkStateDependency(MutableStateDep);
1251 if (FAILED(rc)) return rc;
1252
1253 setModified(IsModified_MachineData);
1254 mHWData.backup();
1255 mHWData->mPointingHIDType = aPointingHIDType;
1256
1257 return S_OK;
1258}
1259
1260STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1261{
1262 CheckComArgOutPointerValid(aChipsetType);
1263
1264 AutoCaller autoCaller(this);
1265 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1266
1267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1268
1269 *aChipsetType = mHWData->mChipsetType;
1270
1271 return S_OK;
1272}
1273
1274STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1275{
1276 AutoCaller autoCaller(this);
1277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1279
1280 HRESULT rc = checkStateDependency(MutableStateDep);
1281 if (FAILED(rc)) return rc;
1282
1283 if (aChipsetType != mHWData->mChipsetType)
1284 {
1285 setModified(IsModified_MachineData);
1286 mHWData.backup();
1287 mHWData->mChipsetType = aChipsetType;
1288
1289 // Resize network adapter array, to be finalized on commit/rollback.
1290 // We must not throw away entries yet, otherwise settings are lost
1291 // without a way to roll back.
1292 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1293 size_t oldCount = mNetworkAdapters.size();
1294 if (newCount > oldCount)
1295 {
1296 mNetworkAdapters.resize(newCount);
1297 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1298 {
1299 unconst(mNetworkAdapters[slot]).createObject();
1300 mNetworkAdapters[slot]->init(this, slot);
1301 }
1302 }
1303 }
1304
1305 return S_OK;
1306}
1307
1308STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1309{
1310 CheckComArgOutPointerValid(aHWVersion);
1311
1312 AutoCaller autoCaller(this);
1313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1314
1315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1316
1317 mHWData->mHWVersion.cloneTo(aHWVersion);
1318
1319 return S_OK;
1320}
1321
1322STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1323{
1324 /* check known version */
1325 Utf8Str hwVersion = aHWVersion;
1326 if ( hwVersion.compare("1") != 0
1327 && hwVersion.compare("2") != 0)
1328 return setError(E_INVALIDARG,
1329 tr("Invalid hardware version: %ls\n"), aHWVersion);
1330
1331 AutoCaller autoCaller(this);
1332 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1333
1334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1335
1336 HRESULT rc = checkStateDependency(MutableStateDep);
1337 if (FAILED(rc)) return rc;
1338
1339 setModified(IsModified_MachineData);
1340 mHWData.backup();
1341 mHWData->mHWVersion = hwVersion;
1342
1343 return S_OK;
1344}
1345
1346STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1347{
1348 CheckComArgOutPointerValid(aUUID);
1349
1350 AutoCaller autoCaller(this);
1351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1352
1353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1354
1355 if (mHWData->mHardwareUUID.isValid())
1356 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1357 else
1358 mData->mUuid.toUtf16().cloneTo(aUUID);
1359
1360 return S_OK;
1361}
1362
1363STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1364{
1365 Guid hardwareUUID(aUUID);
1366 if (!hardwareUUID.isValid())
1367 return E_INVALIDARG;
1368
1369 AutoCaller autoCaller(this);
1370 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1371
1372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1373
1374 HRESULT rc = checkStateDependency(MutableStateDep);
1375 if (FAILED(rc)) return rc;
1376
1377 setModified(IsModified_MachineData);
1378 mHWData.backup();
1379 if (hardwareUUID == mData->mUuid)
1380 mHWData->mHardwareUUID.clear();
1381 else
1382 mHWData->mHardwareUUID = hardwareUUID;
1383
1384 return S_OK;
1385}
1386
1387STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1388{
1389 CheckComArgOutPointerValid(memorySize);
1390
1391 AutoCaller autoCaller(this);
1392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1393
1394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 *memorySize = mHWData->mMemorySize;
1397
1398 return S_OK;
1399}
1400
1401STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1402{
1403 /* check RAM limits */
1404 if ( memorySize < MM_RAM_MIN_IN_MB
1405 || memorySize > MM_RAM_MAX_IN_MB
1406 )
1407 return setError(E_INVALIDARG,
1408 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1409 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1410
1411 AutoCaller autoCaller(this);
1412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1413
1414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 HRESULT rc = checkStateDependency(MutableStateDep);
1417 if (FAILED(rc)) return rc;
1418
1419 setModified(IsModified_MachineData);
1420 mHWData.backup();
1421 mHWData->mMemorySize = memorySize;
1422
1423 return S_OK;
1424}
1425
1426STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1427{
1428 CheckComArgOutPointerValid(CPUCount);
1429
1430 AutoCaller autoCaller(this);
1431 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1432
1433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1434
1435 *CPUCount = mHWData->mCPUCount;
1436
1437 return S_OK;
1438}
1439
1440STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1441{
1442 /* check CPU limits */
1443 if ( CPUCount < SchemaDefs::MinCPUCount
1444 || CPUCount > SchemaDefs::MaxCPUCount
1445 )
1446 return setError(E_INVALIDARG,
1447 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1448 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1449
1450 AutoCaller autoCaller(this);
1451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1452
1453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1454
1455 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1456 if (mHWData->mCPUHotPlugEnabled)
1457 {
1458 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1459 {
1460 if (mHWData->mCPUAttached[idx])
1461 return setError(E_INVALIDARG,
1462 tr("There is still a CPU attached to socket %lu."
1463 "Detach the CPU before removing the socket"),
1464 CPUCount, idx+1);
1465 }
1466 }
1467
1468 HRESULT rc = checkStateDependency(MutableStateDep);
1469 if (FAILED(rc)) return rc;
1470
1471 setModified(IsModified_MachineData);
1472 mHWData.backup();
1473 mHWData->mCPUCount = CPUCount;
1474
1475 return S_OK;
1476}
1477
1478STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1479{
1480 CheckComArgOutPointerValid(aExecutionCap);
1481
1482 AutoCaller autoCaller(this);
1483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1484
1485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 *aExecutionCap = mHWData->mCpuExecutionCap;
1488
1489 return S_OK;
1490}
1491
1492STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1493{
1494 HRESULT rc = S_OK;
1495
1496 /* check throttle limits */
1497 if ( aExecutionCap < 1
1498 || aExecutionCap > 100
1499 )
1500 return setError(E_INVALIDARG,
1501 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1502 aExecutionCap, 1, 100);
1503
1504 AutoCaller autoCaller(this);
1505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1506
1507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1508
1509 alock.release();
1510 rc = onCPUExecutionCapChange(aExecutionCap);
1511 alock.acquire();
1512 if (FAILED(rc)) return rc;
1513
1514 setModified(IsModified_MachineData);
1515 mHWData.backup();
1516 mHWData->mCpuExecutionCap = aExecutionCap;
1517
1518 /* Save settings if online - todo why is this required?? */
1519 if (Global::IsOnline(mData->mMachineState))
1520 saveSettings(NULL);
1521
1522 return S_OK;
1523}
1524
1525
1526STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1527{
1528 CheckComArgOutPointerValid(aEnabled);
1529
1530 AutoCaller autoCaller(this);
1531 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1532
1533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1534
1535 *aEnabled = mHWData->mCPUHotPlugEnabled;
1536
1537 return S_OK;
1538}
1539
1540STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1541{
1542 HRESULT rc = S_OK;
1543
1544 AutoCaller autoCaller(this);
1545 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1546
1547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1548
1549 rc = checkStateDependency(MutableStateDep);
1550 if (FAILED(rc)) return rc;
1551
1552 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1553 {
1554 if (aEnabled)
1555 {
1556 setModified(IsModified_MachineData);
1557 mHWData.backup();
1558
1559 /* Add the amount of CPUs currently attached */
1560 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1561 {
1562 mHWData->mCPUAttached[i] = true;
1563 }
1564 }
1565 else
1566 {
1567 /*
1568 * We can disable hotplug only if the amount of maximum CPUs is equal
1569 * to the amount of attached CPUs
1570 */
1571 unsigned cCpusAttached = 0;
1572 unsigned iHighestId = 0;
1573
1574 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1575 {
1576 if (mHWData->mCPUAttached[i])
1577 {
1578 cCpusAttached++;
1579 iHighestId = i;
1580 }
1581 }
1582
1583 if ( (cCpusAttached != mHWData->mCPUCount)
1584 || (iHighestId >= mHWData->mCPUCount))
1585 return setError(E_INVALIDARG,
1586 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1587
1588 setModified(IsModified_MachineData);
1589 mHWData.backup();
1590 }
1591 }
1592
1593 mHWData->mCPUHotPlugEnabled = aEnabled;
1594
1595 return rc;
1596}
1597
1598STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1599{
1600#ifdef VBOX_WITH_USB_CARDREADER
1601 CheckComArgOutPointerValid(aEnabled);
1602
1603 AutoCaller autoCaller(this);
1604 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1605
1606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1607
1608 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1609
1610 return S_OK;
1611#else
1612 NOREF(aEnabled);
1613 return E_NOTIMPL;
1614#endif
1615}
1616
1617STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1618{
1619#ifdef VBOX_WITH_USB_CARDREADER
1620 AutoCaller autoCaller(this);
1621 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 HRESULT rc = checkStateDependency(MutableStateDep);
1625 if (FAILED(rc)) return rc;
1626
1627 setModified(IsModified_MachineData);
1628 mHWData.backup();
1629 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1630
1631 return S_OK;
1632#else
1633 NOREF(aEnabled);
1634 return E_NOTIMPL;
1635#endif
1636}
1637
1638STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1639{
1640#ifdef VBOX_WITH_USB_VIDEO
1641 CheckComArgOutPointerValid(aEnabled);
1642
1643 AutoCaller autoCaller(this);
1644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1645
1646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1647
1648 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1649
1650 return S_OK;
1651#else
1652 NOREF(aEnabled);
1653 return E_NOTIMPL;
1654#endif
1655}
1656
1657STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1658{
1659#ifdef VBOX_WITH_USB_VIDEO
1660 AutoCaller autoCaller(this);
1661 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1663
1664 HRESULT rc = checkStateDependency(MutableStateDep);
1665 if (FAILED(rc)) return rc;
1666
1667 setModified(IsModified_MachineData);
1668 mHWData.backup();
1669 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1670
1671 return S_OK;
1672#else
1673 NOREF(aEnabled);
1674 return E_NOTIMPL;
1675#endif
1676}
1677
1678STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1679{
1680 CheckComArgOutPointerValid(aEnabled);
1681
1682 AutoCaller autoCaller(this);
1683 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1685
1686 *aEnabled = mHWData->mHPETEnabled;
1687
1688 return S_OK;
1689}
1690
1691STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1692{
1693 HRESULT rc = S_OK;
1694
1695 AutoCaller autoCaller(this);
1696 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1698
1699 rc = checkStateDependency(MutableStateDep);
1700 if (FAILED(rc)) return rc;
1701
1702 setModified(IsModified_MachineData);
1703 mHWData.backup();
1704
1705 mHWData->mHPETEnabled = aEnabled;
1706
1707 return rc;
1708}
1709
1710STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1711{
1712 AutoCaller autoCaller(this);
1713 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1714
1715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 *fEnabled = mHWData->mVideoCaptureEnabled;
1718 return S_OK;
1719}
1720
1721STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1722{
1723 HRESULT rc = S_OK;
1724
1725 AutoCaller autoCaller(this);
1726 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1728
1729 rc = checkStateDependency(MutableStateDep);
1730 if (FAILED(rc)) return rc;
1731
1732 setModified(IsModified_MachineData);
1733 mHWData.backup();
1734
1735 mHWData->mVideoCaptureEnabled = fEnabled;
1736
1737 return rc;
1738}
1739
1740STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1741{
1742 CheckComArgOutSafeArrayPointerValid(aScreens);
1743
1744 AutoCaller autoCaller(this);
1745 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1746
1747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1748
1749 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1750 for (unsigned i = 0; i < screens.size(); i++)
1751 screens[i] = mHWData->maVideoCaptureScreens[i];
1752 screens.detachTo(ComSafeArrayOutArg(aScreens));
1753 return S_OK;
1754}
1755
1756STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1757{
1758 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1759 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1760 bool fChanged = false;
1761
1762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1763
1764 for (unsigned i = 0; i < screens.size(); i++)
1765 {
1766 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1767 {
1768 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1769 fChanged = true;
1770 }
1771 }
1772 if (fChanged)
1773 {
1774 alock.release();
1775 HRESULT rc = onVideoCaptureChange();
1776 alock.acquire();
1777 if (FAILED(rc)) return rc;
1778 setModified(IsModified_MachineData);
1779 if (Global::IsOnline(mData->mMachineState))
1780 saveSettings(NULL);
1781 }
1782
1783 return S_OK;
1784}
1785
1786STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1787{
1788 AutoCaller autoCaller(this);
1789 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1790
1791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1792 mHWData->mVideoCaptureFile.cloneTo(apFile);
1793 return S_OK;
1794}
1795
1796STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1797{
1798 Utf8Str strFile(aFile);
1799 AutoCaller autoCaller(this);
1800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1801
1802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1803
1804 HRESULT rc = checkStateDependency(MutableStateDep);
1805 if (FAILED(rc)) return rc;
1806
1807 if (strFile.isEmpty())
1808 strFile = "VideoCap.webm";
1809
1810 setModified(IsModified_MachineData);
1811 mHWData.backup();
1812 mHWData->mVideoCaptureFile = strFile;
1813
1814 return S_OK;
1815}
1816
1817STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1818{
1819 AutoCaller autoCaller(this);
1820 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1821
1822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1823 *aHorzRes = mHWData->mVideoCaptureWidth;
1824 return S_OK;
1825}
1826
1827STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1828{
1829 AutoCaller autoCaller(this);
1830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1831
1832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1833
1834 HRESULT rc = checkStateDependency(MutableStateDep);
1835 if (FAILED(rc)) return rc;
1836
1837 setModified(IsModified_MachineData);
1838 mHWData.backup();
1839 mHWData->mVideoCaptureWidth = aHorzRes;
1840
1841 return S_OK;
1842}
1843
1844STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1845{
1846 AutoCaller autoCaller(this);
1847 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1848
1849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1850 *aVertRes = mHWData->mVideoCaptureHeight;
1851 return S_OK;
1852}
1853
1854STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1855{
1856 AutoCaller autoCaller(this);
1857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1858
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 HRESULT rc = checkStateDependency(MutableStateDep);
1862 if (FAILED(rc)) return rc;
1863
1864 setModified(IsModified_MachineData);
1865 mHWData.backup();
1866 mHWData->mVideoCaptureHeight = aVertRes;
1867
1868 return S_OK;
1869}
1870
1871STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1872{
1873 AutoCaller autoCaller(this);
1874 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1875
1876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1877 *aRate = mHWData->mVideoCaptureRate;
1878 return S_OK;
1879}
1880
1881STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1882{
1883 AutoCaller autoCaller(this);
1884 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1885
1886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1887
1888 HRESULT rc = checkStateDependency(MutableStateDep);
1889 if (FAILED(rc)) return rc;
1890
1891 setModified(IsModified_MachineData);
1892 mHWData.backup();
1893 mHWData->mVideoCaptureRate = aRate;
1894
1895 return S_OK;
1896}
1897
1898STDMETHODIMP Machine::COMGETTER(VideoCaptureFps)(ULONG *aFps)
1899{
1900 AutoCaller autoCaller(this);
1901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1902
1903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1904 *aFps = mHWData->mVideoCaptureFps;
1905 return S_OK;
1906}
1907
1908STDMETHODIMP Machine::COMSETTER(VideoCaptureFps)(ULONG aFps)
1909{
1910 AutoCaller autoCaller(this);
1911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1912
1913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1914
1915 HRESULT rc = checkStateDependency(MutableStateDep);
1916 if (FAILED(rc)) return rc;
1917
1918 setModified(IsModified_MachineData);
1919 mHWData.backup();
1920 mHWData->mVideoCaptureFps = aFps;
1921
1922 return S_OK;
1923}
1924
1925STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1926{
1927 CheckComArgOutPointerValid(aGraphicsControllerType);
1928
1929 AutoCaller autoCaller(this);
1930 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1931
1932 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1933
1934 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1935
1936 return S_OK;
1937}
1938
1939STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1940{
1941 switch (aGraphicsControllerType)
1942 {
1943 case GraphicsControllerType_Null:
1944 case GraphicsControllerType_VBoxVGA:
1945 break;
1946 default:
1947 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1948 }
1949
1950 AutoCaller autoCaller(this);
1951 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1952
1953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1954
1955 HRESULT rc = checkStateDependency(MutableStateDep);
1956 if (FAILED(rc)) return rc;
1957
1958 setModified(IsModified_MachineData);
1959 mHWData.backup();
1960 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1961
1962 return S_OK;
1963}
1964
1965STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1966{
1967 CheckComArgOutPointerValid(memorySize);
1968
1969 AutoCaller autoCaller(this);
1970 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1971
1972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1973
1974 *memorySize = mHWData->mVRAMSize;
1975
1976 return S_OK;
1977}
1978
1979STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1980{
1981 /* check VRAM limits */
1982 if (memorySize < SchemaDefs::MinGuestVRAM ||
1983 memorySize > SchemaDefs::MaxGuestVRAM)
1984 return setError(E_INVALIDARG,
1985 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1986 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1987
1988 AutoCaller autoCaller(this);
1989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1990
1991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 HRESULT rc = checkStateDependency(MutableStateDep);
1994 if (FAILED(rc)) return rc;
1995
1996 setModified(IsModified_MachineData);
1997 mHWData.backup();
1998 mHWData->mVRAMSize = memorySize;
1999
2000 return S_OK;
2001}
2002
2003/** @todo this method should not be public */
2004STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2005{
2006 CheckComArgOutPointerValid(memoryBalloonSize);
2007
2008 AutoCaller autoCaller(this);
2009 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2010
2011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2012
2013 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2014
2015 return S_OK;
2016}
2017
2018/**
2019 * Set the memory balloon size.
2020 *
2021 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2022 * we have to make sure that we never call IGuest from here.
2023 */
2024STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2025{
2026 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2027#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2028 /* check limits */
2029 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2030 return setError(E_INVALIDARG,
2031 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2032 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2033
2034 AutoCaller autoCaller(this);
2035 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2036
2037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2038
2039 setModified(IsModified_MachineData);
2040 mHWData.backup();
2041 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2042
2043 return S_OK;
2044#else
2045 NOREF(memoryBalloonSize);
2046 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2047#endif
2048}
2049
2050STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2051{
2052 CheckComArgOutPointerValid(aEnabled);
2053
2054 AutoCaller autoCaller(this);
2055 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2056
2057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 *aEnabled = mHWData->mPageFusionEnabled;
2060 return S_OK;
2061}
2062
2063STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2064{
2065#ifdef VBOX_WITH_PAGE_SHARING
2066 AutoCaller autoCaller(this);
2067 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2068
2069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2070
2071 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2072 setModified(IsModified_MachineData);
2073 mHWData.backup();
2074 mHWData->mPageFusionEnabled = aEnabled;
2075 return S_OK;
2076#else
2077 NOREF(aEnabled);
2078 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2079#endif
2080}
2081
2082STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2083{
2084 CheckComArgOutPointerValid(aEnabled);
2085
2086 AutoCaller autoCaller(this);
2087 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2088
2089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2090
2091 *aEnabled = mHWData->mAccelerate3DEnabled;
2092
2093 return S_OK;
2094}
2095
2096STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2097{
2098 AutoCaller autoCaller(this);
2099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2100
2101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2102
2103 HRESULT rc = checkStateDependency(MutableStateDep);
2104 if (FAILED(rc)) return rc;
2105
2106 /** @todo check validity! */
2107
2108 setModified(IsModified_MachineData);
2109 mHWData.backup();
2110 mHWData->mAccelerate3DEnabled = enable;
2111
2112 return S_OK;
2113}
2114
2115
2116STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2117{
2118 CheckComArgOutPointerValid(aEnabled);
2119
2120 AutoCaller autoCaller(this);
2121 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2122
2123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2124
2125 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2126
2127 return S_OK;
2128}
2129
2130STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2131{
2132 AutoCaller autoCaller(this);
2133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2134
2135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2136
2137 HRESULT rc = checkStateDependency(MutableStateDep);
2138 if (FAILED(rc)) return rc;
2139
2140 /** @todo check validity! */
2141
2142 setModified(IsModified_MachineData);
2143 mHWData.backup();
2144 mHWData->mAccelerate2DVideoEnabled = enable;
2145
2146 return S_OK;
2147}
2148
2149STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2150{
2151 CheckComArgOutPointerValid(monitorCount);
2152
2153 AutoCaller autoCaller(this);
2154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2155
2156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 *monitorCount = mHWData->mMonitorCount;
2159
2160 return S_OK;
2161}
2162
2163STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2164{
2165 /* make sure monitor count is a sensible number */
2166 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2167 return setError(E_INVALIDARG,
2168 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2169 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2170
2171 AutoCaller autoCaller(this);
2172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2173
2174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2175
2176 HRESULT rc = checkStateDependency(MutableStateDep);
2177 if (FAILED(rc)) return rc;
2178
2179 setModified(IsModified_MachineData);
2180 mHWData.backup();
2181 mHWData->mMonitorCount = monitorCount;
2182
2183 return S_OK;
2184}
2185
2186STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2187{
2188 CheckComArgOutPointerValid(biosSettings);
2189
2190 AutoCaller autoCaller(this);
2191 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2192
2193 /* mBIOSSettings is constant during life time, no need to lock */
2194 mBIOSSettings.queryInterfaceTo(biosSettings);
2195
2196 return S_OK;
2197}
2198
2199STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2200{
2201 CheckComArgOutPointerValid(aVal);
2202
2203 AutoCaller autoCaller(this);
2204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2205
2206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2207
2208 switch (property)
2209 {
2210 case CPUPropertyType_PAE:
2211 *aVal = mHWData->mPAEEnabled;
2212 break;
2213
2214 case CPUPropertyType_Synthetic:
2215 *aVal = mHWData->mSyntheticCpu;
2216 break;
2217
2218 case CPUPropertyType_LongMode:
2219 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2220 *aVal = TRUE;
2221 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2222 *aVal = FALSE;
2223#if HC_ARCH_BITS == 64
2224 else
2225 *aVal = TRUE;
2226#else
2227 else
2228 {
2229 *aVal = FALSE;
2230
2231 ComPtr<IGuestOSType> ptrGuestOSType;
2232 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2233 if (SUCCEEDED(hrc2))
2234 {
2235 BOOL fIs64Bit = FALSE;
2236 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2237 if (SUCCEEDED(hrc2) && fIs64Bit)
2238 {
2239 ComObjPtr<Host> ptrHost = mParent->host();
2240 alock.release();
2241
2242 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2243 if (FAILED(hrc2))
2244 *aVal = FALSE;
2245 }
2246 }
2247 }
2248#endif
2249 break;
2250
2251 default:
2252 return E_INVALIDARG;
2253 }
2254 return S_OK;
2255}
2256
2257STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2258{
2259 AutoCaller autoCaller(this);
2260 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2261
2262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2263
2264 HRESULT rc = checkStateDependency(MutableStateDep);
2265 if (FAILED(rc)) return rc;
2266
2267 switch (property)
2268 {
2269 case CPUPropertyType_PAE:
2270 setModified(IsModified_MachineData);
2271 mHWData.backup();
2272 mHWData->mPAEEnabled = !!aVal;
2273 break;
2274
2275 case CPUPropertyType_Synthetic:
2276 setModified(IsModified_MachineData);
2277 mHWData.backup();
2278 mHWData->mSyntheticCpu = !!aVal;
2279 break;
2280
2281 case CPUPropertyType_LongMode:
2282 setModified(IsModified_MachineData);
2283 mHWData.backup();
2284 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2285 break;
2286
2287 default:
2288 return E_INVALIDARG;
2289 }
2290 return S_OK;
2291}
2292
2293STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2294{
2295 CheckComArgOutPointerValid(aValEax);
2296 CheckComArgOutPointerValid(aValEbx);
2297 CheckComArgOutPointerValid(aValEcx);
2298 CheckComArgOutPointerValid(aValEdx);
2299
2300 AutoCaller autoCaller(this);
2301 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2302
2303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2304
2305 switch(aId)
2306 {
2307 case 0x0:
2308 case 0x1:
2309 case 0x2:
2310 case 0x3:
2311 case 0x4:
2312 case 0x5:
2313 case 0x6:
2314 case 0x7:
2315 case 0x8:
2316 case 0x9:
2317 case 0xA:
2318 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2319 return E_INVALIDARG;
2320
2321 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2322 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2323 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2324 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2325 break;
2326
2327 case 0x80000000:
2328 case 0x80000001:
2329 case 0x80000002:
2330 case 0x80000003:
2331 case 0x80000004:
2332 case 0x80000005:
2333 case 0x80000006:
2334 case 0x80000007:
2335 case 0x80000008:
2336 case 0x80000009:
2337 case 0x8000000A:
2338 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2339 return E_INVALIDARG;
2340
2341 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2342 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2343 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2344 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2345 break;
2346
2347 default:
2348 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2349 }
2350 return S_OK;
2351}
2352
2353STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2354{
2355 AutoCaller autoCaller(this);
2356 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2357
2358 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2359
2360 HRESULT rc = checkStateDependency(MutableStateDep);
2361 if (FAILED(rc)) return rc;
2362
2363 switch(aId)
2364 {
2365 case 0x0:
2366 case 0x1:
2367 case 0x2:
2368 case 0x3:
2369 case 0x4:
2370 case 0x5:
2371 case 0x6:
2372 case 0x7:
2373 case 0x8:
2374 case 0x9:
2375 case 0xA:
2376 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2377 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2378 setModified(IsModified_MachineData);
2379 mHWData.backup();
2380 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2381 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2382 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2383 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2384 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2385 break;
2386
2387 case 0x80000000:
2388 case 0x80000001:
2389 case 0x80000002:
2390 case 0x80000003:
2391 case 0x80000004:
2392 case 0x80000005:
2393 case 0x80000006:
2394 case 0x80000007:
2395 case 0x80000008:
2396 case 0x80000009:
2397 case 0x8000000A:
2398 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2399 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2400 setModified(IsModified_MachineData);
2401 mHWData.backup();
2402 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2403 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2404 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2405 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2406 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2407 break;
2408
2409 default:
2410 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2411 }
2412 return S_OK;
2413}
2414
2415STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2416{
2417 AutoCaller autoCaller(this);
2418 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2419
2420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2421
2422 HRESULT rc = checkStateDependency(MutableStateDep);
2423 if (FAILED(rc)) return rc;
2424
2425 switch(aId)
2426 {
2427 case 0x0:
2428 case 0x1:
2429 case 0x2:
2430 case 0x3:
2431 case 0x4:
2432 case 0x5:
2433 case 0x6:
2434 case 0x7:
2435 case 0x8:
2436 case 0x9:
2437 case 0xA:
2438 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2439 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2440 setModified(IsModified_MachineData);
2441 mHWData.backup();
2442 /* Invalidate leaf. */
2443 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2444 break;
2445
2446 case 0x80000000:
2447 case 0x80000001:
2448 case 0x80000002:
2449 case 0x80000003:
2450 case 0x80000004:
2451 case 0x80000005:
2452 case 0x80000006:
2453 case 0x80000007:
2454 case 0x80000008:
2455 case 0x80000009:
2456 case 0x8000000A:
2457 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2458 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2459 setModified(IsModified_MachineData);
2460 mHWData.backup();
2461 /* Invalidate leaf. */
2462 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2463 break;
2464
2465 default:
2466 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2467 }
2468 return S_OK;
2469}
2470
2471STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2472{
2473 AutoCaller autoCaller(this);
2474 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2475
2476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2477
2478 HRESULT rc = checkStateDependency(MutableStateDep);
2479 if (FAILED(rc)) return rc;
2480
2481 setModified(IsModified_MachineData);
2482 mHWData.backup();
2483
2484 /* Invalidate all standard leafs. */
2485 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2486 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2487
2488 /* Invalidate all extended leafs. */
2489 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2490 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2491
2492 return S_OK;
2493}
2494
2495STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2496{
2497 CheckComArgOutPointerValid(aVal);
2498
2499 AutoCaller autoCaller(this);
2500 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2501
2502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2503
2504 switch(property)
2505 {
2506 case HWVirtExPropertyType_Enabled:
2507 *aVal = mHWData->mHWVirtExEnabled;
2508 break;
2509
2510 case HWVirtExPropertyType_Exclusive:
2511 *aVal = mHWData->mHWVirtExExclusive;
2512 break;
2513
2514 case HWVirtExPropertyType_VPID:
2515 *aVal = mHWData->mHWVirtExVPIDEnabled;
2516 break;
2517
2518 case HWVirtExPropertyType_NestedPaging:
2519 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2520 break;
2521
2522 case HWVirtExPropertyType_UnrestrictedExecution:
2523 *aVal = mHWData->mHWVirtExUXEnabled;
2524 break;
2525
2526 case HWVirtExPropertyType_LargePages:
2527 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2528#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2529 *aVal = FALSE;
2530#endif
2531 break;
2532
2533 case HWVirtExPropertyType_Force:
2534 *aVal = mHWData->mHWVirtExForceEnabled;
2535 break;
2536
2537 default:
2538 return E_INVALIDARG;
2539 }
2540 return S_OK;
2541}
2542
2543STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2544{
2545 AutoCaller autoCaller(this);
2546 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2547
2548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2549
2550 HRESULT rc = checkStateDependency(MutableStateDep);
2551 if (FAILED(rc)) return rc;
2552
2553 switch(property)
2554 {
2555 case HWVirtExPropertyType_Enabled:
2556 setModified(IsModified_MachineData);
2557 mHWData.backup();
2558 mHWData->mHWVirtExEnabled = !!aVal;
2559 break;
2560
2561 case HWVirtExPropertyType_Exclusive:
2562 setModified(IsModified_MachineData);
2563 mHWData.backup();
2564 mHWData->mHWVirtExExclusive = !!aVal;
2565 break;
2566
2567 case HWVirtExPropertyType_VPID:
2568 setModified(IsModified_MachineData);
2569 mHWData.backup();
2570 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2571 break;
2572
2573 case HWVirtExPropertyType_NestedPaging:
2574 setModified(IsModified_MachineData);
2575 mHWData.backup();
2576 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2577 break;
2578
2579 case HWVirtExPropertyType_UnrestrictedExecution:
2580 setModified(IsModified_MachineData);
2581 mHWData.backup();
2582 mHWData->mHWVirtExUXEnabled = !!aVal;
2583 break;
2584
2585 case HWVirtExPropertyType_LargePages:
2586 setModified(IsModified_MachineData);
2587 mHWData.backup();
2588 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2589 break;
2590
2591 case HWVirtExPropertyType_Force:
2592 setModified(IsModified_MachineData);
2593 mHWData.backup();
2594 mHWData->mHWVirtExForceEnabled = !!aVal;
2595 break;
2596
2597 default:
2598 return E_INVALIDARG;
2599 }
2600
2601 return S_OK;
2602}
2603
2604STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2605{
2606 CheckComArgOutPointerValid(aSnapshotFolder);
2607
2608 AutoCaller autoCaller(this);
2609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2610
2611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2612
2613 Utf8Str strFullSnapshotFolder;
2614 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2615 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2616
2617 return S_OK;
2618}
2619
2620STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2621{
2622 /* @todo (r=dmik):
2623 * 1. Allow to change the name of the snapshot folder containing snapshots
2624 * 2. Rename the folder on disk instead of just changing the property
2625 * value (to be smart and not to leave garbage). Note that it cannot be
2626 * done here because the change may be rolled back. Thus, the right
2627 * place is #saveSettings().
2628 */
2629
2630 AutoCaller autoCaller(this);
2631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2632
2633 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2634
2635 HRESULT rc = checkStateDependency(MutableStateDep);
2636 if (FAILED(rc)) return rc;
2637
2638 if (!mData->mCurrentSnapshot.isNull())
2639 return setError(E_FAIL,
2640 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2641
2642 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2643
2644 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2645 if (strSnapshotFolder.isEmpty())
2646 strSnapshotFolder = "Snapshots";
2647 int vrc = calculateFullPath(strSnapshotFolder,
2648 strSnapshotFolder);
2649 if (RT_FAILURE(vrc))
2650 return setError(E_FAIL,
2651 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2652 aSnapshotFolder, vrc);
2653
2654 setModified(IsModified_MachineData);
2655 mUserData.backup();
2656
2657 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2658
2659 return S_OK;
2660}
2661
2662STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2663{
2664 CheckComArgOutSafeArrayPointerValid(aAttachments);
2665
2666 AutoCaller autoCaller(this);
2667 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2668
2669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2670
2671 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2672 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2673
2674 return S_OK;
2675}
2676
2677STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2678{
2679 CheckComArgOutPointerValid(vrdeServer);
2680
2681 AutoCaller autoCaller(this);
2682 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2683
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 Assert(!!mVRDEServer);
2687 mVRDEServer.queryInterfaceTo(vrdeServer);
2688
2689 return S_OK;
2690}
2691
2692STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2693{
2694 CheckComArgOutPointerValid(audioAdapter);
2695
2696 AutoCaller autoCaller(this);
2697 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2698
2699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2700
2701 mAudioAdapter.queryInterfaceTo(audioAdapter);
2702 return S_OK;
2703}
2704
2705STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2706{
2707#ifdef VBOX_WITH_VUSB
2708 CheckComArgOutPointerValid(aUSBController);
2709
2710 AutoCaller autoCaller(this);
2711 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2712
2713 clearError();
2714 MultiResult rc(S_OK);
2715
2716# ifdef VBOX_WITH_USB
2717 rc = mParent->host()->checkUSBProxyService();
2718 if (FAILED(rc)) return rc;
2719# endif
2720
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 return rc = mUSBController.queryInterfaceTo(aUSBController);
2724#else
2725 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2726 * extended error info to indicate that USB is simply not available
2727 * (w/o treating it as a failure), for example, as in OSE */
2728 NOREF(aUSBController);
2729 ReturnComNotImplemented();
2730#endif /* VBOX_WITH_VUSB */
2731}
2732
2733STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2734{
2735 CheckComArgOutPointerValid(aFilePath);
2736
2737 AutoLimitedCaller autoCaller(this);
2738 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2739
2740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2741
2742 mData->m_strConfigFileFull.cloneTo(aFilePath);
2743 return S_OK;
2744}
2745
2746STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2747{
2748 CheckComArgOutPointerValid(aModified);
2749
2750 AutoCaller autoCaller(this);
2751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2752
2753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 HRESULT rc = checkStateDependency(MutableStateDep);
2756 if (FAILED(rc)) return rc;
2757
2758 if (!mData->pMachineConfigFile->fileExists())
2759 // this is a new machine, and no config file exists yet:
2760 *aModified = TRUE;
2761 else
2762 *aModified = (mData->flModifications != 0);
2763
2764 return S_OK;
2765}
2766
2767STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2768{
2769 CheckComArgOutPointerValid(aSessionState);
2770
2771 AutoCaller autoCaller(this);
2772 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2773
2774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2775
2776 *aSessionState = mData->mSession.mState;
2777
2778 return S_OK;
2779}
2780
2781STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2782{
2783 CheckComArgOutPointerValid(aSessionType);
2784
2785 AutoCaller autoCaller(this);
2786 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2787
2788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 mData->mSession.mType.cloneTo(aSessionType);
2791
2792 return S_OK;
2793}
2794
2795STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2796{
2797 CheckComArgOutPointerValid(aSessionPID);
2798
2799 AutoCaller autoCaller(this);
2800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2801
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 *aSessionPID = mData->mSession.mPID;
2805
2806 return S_OK;
2807}
2808
2809STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2810{
2811 CheckComArgOutPointerValid(machineState);
2812
2813 AutoCaller autoCaller(this);
2814 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2815
2816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2817
2818 *machineState = mData->mMachineState;
2819
2820 return S_OK;
2821}
2822
2823STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2824{
2825 CheckComArgOutPointerValid(aLastStateChange);
2826
2827 AutoCaller autoCaller(this);
2828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2829
2830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2831
2832 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2833
2834 return S_OK;
2835}
2836
2837STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2838{
2839 CheckComArgOutPointerValid(aStateFilePath);
2840
2841 AutoCaller autoCaller(this);
2842 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2843
2844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2845
2846 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2847
2848 return S_OK;
2849}
2850
2851STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2852{
2853 CheckComArgOutPointerValid(aLogFolder);
2854
2855 AutoCaller autoCaller(this);
2856 AssertComRCReturnRC(autoCaller.rc());
2857
2858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2859
2860 Utf8Str logFolder;
2861 getLogFolder(logFolder);
2862 logFolder.cloneTo(aLogFolder);
2863
2864 return S_OK;
2865}
2866
2867STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2868{
2869 CheckComArgOutPointerValid(aCurrentSnapshot);
2870
2871 AutoCaller autoCaller(this);
2872 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2873
2874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2875
2876 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2877
2878 return S_OK;
2879}
2880
2881STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2882{
2883 CheckComArgOutPointerValid(aSnapshotCount);
2884
2885 AutoCaller autoCaller(this);
2886 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2887
2888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2889
2890 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2891 ? 0
2892 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2893
2894 return S_OK;
2895}
2896
2897STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2898{
2899 CheckComArgOutPointerValid(aCurrentStateModified);
2900
2901 AutoCaller autoCaller(this);
2902 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2903
2904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 /* Note: for machines with no snapshots, we always return FALSE
2907 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2908 * reasons :) */
2909
2910 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2911 ? FALSE
2912 : mData->mCurrentStateModified;
2913
2914 return S_OK;
2915}
2916
2917STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2918{
2919 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2920
2921 AutoCaller autoCaller(this);
2922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2923
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2927 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2928
2929 return S_OK;
2930}
2931
2932STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2933{
2934 CheckComArgOutPointerValid(aClipboardMode);
2935
2936 AutoCaller autoCaller(this);
2937 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2938
2939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2940
2941 *aClipboardMode = mHWData->mClipboardMode;
2942
2943 return S_OK;
2944}
2945
2946STDMETHODIMP
2947Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2948{
2949 HRESULT rc = S_OK;
2950
2951 AutoCaller autoCaller(this);
2952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2953
2954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2955
2956 alock.release();
2957 rc = onClipboardModeChange(aClipboardMode);
2958 alock.acquire();
2959 if (FAILED(rc)) return rc;
2960
2961 setModified(IsModified_MachineData);
2962 mHWData.backup();
2963 mHWData->mClipboardMode = aClipboardMode;
2964
2965 /* Save settings if online - todo why is this required?? */
2966 if (Global::IsOnline(mData->mMachineState))
2967 saveSettings(NULL);
2968
2969 return S_OK;
2970}
2971
2972STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2973{
2974 CheckComArgOutPointerValid(aDragAndDropMode);
2975
2976 AutoCaller autoCaller(this);
2977 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2978
2979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2980
2981 *aDragAndDropMode = mHWData->mDragAndDropMode;
2982
2983 return S_OK;
2984}
2985
2986STDMETHODIMP
2987Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2988{
2989 HRESULT rc = S_OK;
2990
2991 AutoCaller autoCaller(this);
2992 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2993
2994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2995
2996 alock.release();
2997 rc = onDragAndDropModeChange(aDragAndDropMode);
2998 alock.acquire();
2999 if (FAILED(rc)) return rc;
3000
3001 setModified(IsModified_MachineData);
3002 mHWData.backup();
3003 mHWData->mDragAndDropMode = aDragAndDropMode;
3004
3005 /* Save settings if online - todo why is this required?? */
3006 if (Global::IsOnline(mData->mMachineState))
3007 saveSettings(NULL);
3008
3009 return S_OK;
3010}
3011
3012STDMETHODIMP
3013Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3014{
3015 CheckComArgOutPointerValid(aPatterns);
3016
3017 AutoCaller autoCaller(this);
3018 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3019
3020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3021
3022 try
3023 {
3024 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3025 }
3026 catch (...)
3027 {
3028 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3029 }
3030
3031 return S_OK;
3032}
3033
3034STDMETHODIMP
3035Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3036{
3037 AutoCaller autoCaller(this);
3038 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3039
3040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 HRESULT rc = checkStateDependency(MutableStateDep);
3043 if (FAILED(rc)) return rc;
3044
3045 setModified(IsModified_MachineData);
3046 mHWData.backup();
3047 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3048 return rc;
3049}
3050
3051STDMETHODIMP
3052Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3053{
3054 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3055
3056 AutoCaller autoCaller(this);
3057 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3058
3059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3060
3061 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3062 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3063
3064 return S_OK;
3065}
3066
3067STDMETHODIMP
3068Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3069{
3070 CheckComArgOutPointerValid(aEnabled);
3071
3072 AutoCaller autoCaller(this);
3073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3074
3075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3076
3077 *aEnabled = mUserData->s.fTeleporterEnabled;
3078
3079 return S_OK;
3080}
3081
3082STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3083{
3084 AutoCaller autoCaller(this);
3085 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3086
3087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3088
3089 /* Only allow it to be set to true when PoweredOff or Aborted.
3090 (Clearing it is always permitted.) */
3091 if ( aEnabled
3092 && mData->mRegistered
3093 && ( !isSessionMachine()
3094 || ( mData->mMachineState != MachineState_PoweredOff
3095 && mData->mMachineState != MachineState_Teleported
3096 && mData->mMachineState != MachineState_Aborted
3097 )
3098 )
3099 )
3100 return setError(VBOX_E_INVALID_VM_STATE,
3101 tr("The machine is not powered off (state is %s)"),
3102 Global::stringifyMachineState(mData->mMachineState));
3103
3104 setModified(IsModified_MachineData);
3105 mUserData.backup();
3106 mUserData->s.fTeleporterEnabled = !!aEnabled;
3107
3108 return S_OK;
3109}
3110
3111STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3112{
3113 CheckComArgOutPointerValid(aPort);
3114
3115 AutoCaller autoCaller(this);
3116 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3117
3118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3119
3120 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3121
3122 return S_OK;
3123}
3124
3125STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3126{
3127 if (aPort >= _64K)
3128 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3129
3130 AutoCaller autoCaller(this);
3131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3132
3133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3134
3135 HRESULT rc = checkStateDependency(MutableStateDep);
3136 if (FAILED(rc)) return rc;
3137
3138 setModified(IsModified_MachineData);
3139 mUserData.backup();
3140 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3141
3142 return S_OK;
3143}
3144
3145STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3146{
3147 CheckComArgOutPointerValid(aAddress);
3148
3149 AutoCaller autoCaller(this);
3150 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3151
3152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3153
3154 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3155
3156 return S_OK;
3157}
3158
3159STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3160{
3161 AutoCaller autoCaller(this);
3162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3163
3164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3165
3166 HRESULT rc = checkStateDependency(MutableStateDep);
3167 if (FAILED(rc)) return rc;
3168
3169 setModified(IsModified_MachineData);
3170 mUserData.backup();
3171 mUserData->s.strTeleporterAddress = aAddress;
3172
3173 return S_OK;
3174}
3175
3176STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3177{
3178 CheckComArgOutPointerValid(aPassword);
3179
3180 AutoCaller autoCaller(this);
3181 HRESULT hrc = autoCaller.rc();
3182 if (SUCCEEDED(hrc))
3183 {
3184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3185 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3186 }
3187
3188 return hrc;
3189}
3190
3191STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3192{
3193 /*
3194 * Hash the password first.
3195 */
3196 Utf8Str strPassword(aPassword);
3197 if (!strPassword.isEmpty())
3198 {
3199 if (VBoxIsPasswordHashed(&strPassword))
3200 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3201 VBoxHashPassword(&strPassword);
3202 }
3203
3204 /*
3205 * Do the update.
3206 */
3207 AutoCaller autoCaller(this);
3208 HRESULT hrc = autoCaller.rc();
3209 if (SUCCEEDED(hrc))
3210 {
3211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3212 hrc = checkStateDependency(MutableStateDep);
3213 if (SUCCEEDED(hrc))
3214 {
3215 setModified(IsModified_MachineData);
3216 mUserData.backup();
3217 mUserData->s.strTeleporterPassword = strPassword;
3218 }
3219 }
3220
3221 return hrc;
3222}
3223
3224STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3225{
3226 CheckComArgOutPointerValid(aState);
3227
3228 AutoCaller autoCaller(this);
3229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3230
3231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3232
3233 *aState = mUserData->s.enmFaultToleranceState;
3234 return S_OK;
3235}
3236
3237STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3238{
3239 AutoCaller autoCaller(this);
3240 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3241
3242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3243
3244 /* @todo deal with running state change. */
3245 HRESULT rc = checkStateDependency(MutableStateDep);
3246 if (FAILED(rc)) return rc;
3247
3248 setModified(IsModified_MachineData);
3249 mUserData.backup();
3250 mUserData->s.enmFaultToleranceState = aState;
3251 return S_OK;
3252}
3253
3254STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3255{
3256 CheckComArgOutPointerValid(aAddress);
3257
3258 AutoCaller autoCaller(this);
3259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3260
3261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3262
3263 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3264 return S_OK;
3265}
3266
3267STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3268{
3269 AutoCaller autoCaller(this);
3270 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3271
3272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3273
3274 /* @todo deal with running state change. */
3275 HRESULT rc = checkStateDependency(MutableStateDep);
3276 if (FAILED(rc)) return rc;
3277
3278 setModified(IsModified_MachineData);
3279 mUserData.backup();
3280 mUserData->s.strFaultToleranceAddress = aAddress;
3281 return S_OK;
3282}
3283
3284STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3285{
3286 CheckComArgOutPointerValid(aPort);
3287
3288 AutoCaller autoCaller(this);
3289 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3290
3291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3292
3293 *aPort = mUserData->s.uFaultTolerancePort;
3294 return S_OK;
3295}
3296
3297STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3298{
3299 AutoCaller autoCaller(this);
3300 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3301
3302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3303
3304 /* @todo deal with running state change. */
3305 HRESULT rc = checkStateDependency(MutableStateDep);
3306 if (FAILED(rc)) return rc;
3307
3308 setModified(IsModified_MachineData);
3309 mUserData.backup();
3310 mUserData->s.uFaultTolerancePort = aPort;
3311 return S_OK;
3312}
3313
3314STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3315{
3316 CheckComArgOutPointerValid(aPassword);
3317
3318 AutoCaller autoCaller(this);
3319 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3320
3321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3322
3323 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3324
3325 return S_OK;
3326}
3327
3328STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3329{
3330 AutoCaller autoCaller(this);
3331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3332
3333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3334
3335 /* @todo deal with running state change. */
3336 HRESULT rc = checkStateDependency(MutableStateDep);
3337 if (FAILED(rc)) return rc;
3338
3339 setModified(IsModified_MachineData);
3340 mUserData.backup();
3341 mUserData->s.strFaultTolerancePassword = aPassword;
3342
3343 return S_OK;
3344}
3345
3346STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3347{
3348 CheckComArgOutPointerValid(aInterval);
3349
3350 AutoCaller autoCaller(this);
3351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3352
3353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3354
3355 *aInterval = mUserData->s.uFaultToleranceInterval;
3356 return S_OK;
3357}
3358
3359STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3360{
3361 AutoCaller autoCaller(this);
3362 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3363
3364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3365
3366 /* @todo deal with running state change. */
3367 HRESULT rc = checkStateDependency(MutableStateDep);
3368 if (FAILED(rc)) return rc;
3369
3370 setModified(IsModified_MachineData);
3371 mUserData.backup();
3372 mUserData->s.uFaultToleranceInterval = aInterval;
3373 return S_OK;
3374}
3375
3376STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3377{
3378 CheckComArgOutPointerValid(aEnabled);
3379
3380 AutoCaller autoCaller(this);
3381 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3382
3383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3384
3385 *aEnabled = mUserData->s.fRTCUseUTC;
3386
3387 return S_OK;
3388}
3389
3390STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3391{
3392 AutoCaller autoCaller(this);
3393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3394
3395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3396
3397 /* Only allow it to be set to true when PoweredOff or Aborted.
3398 (Clearing it is always permitted.) */
3399 if ( aEnabled
3400 && mData->mRegistered
3401 && ( !isSessionMachine()
3402 || ( mData->mMachineState != MachineState_PoweredOff
3403 && mData->mMachineState != MachineState_Teleported
3404 && mData->mMachineState != MachineState_Aborted
3405 )
3406 )
3407 )
3408 return setError(VBOX_E_INVALID_VM_STATE,
3409 tr("The machine is not powered off (state is %s)"),
3410 Global::stringifyMachineState(mData->mMachineState));
3411
3412 setModified(IsModified_MachineData);
3413 mUserData.backup();
3414 mUserData->s.fRTCUseUTC = !!aEnabled;
3415
3416 return S_OK;
3417}
3418
3419STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3420{
3421 CheckComArgOutPointerValid(aEnabled);
3422
3423 AutoCaller autoCaller(this);
3424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3425
3426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3427
3428 *aEnabled = mHWData->mIOCacheEnabled;
3429
3430 return S_OK;
3431}
3432
3433STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3434{
3435 AutoCaller autoCaller(this);
3436 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3437
3438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3439
3440 HRESULT rc = checkStateDependency(MutableStateDep);
3441 if (FAILED(rc)) return rc;
3442
3443 setModified(IsModified_MachineData);
3444 mHWData.backup();
3445 mHWData->mIOCacheEnabled = aEnabled;
3446
3447 return S_OK;
3448}
3449
3450STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3451{
3452 CheckComArgOutPointerValid(aIOCacheSize);
3453
3454 AutoCaller autoCaller(this);
3455 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3456
3457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3458
3459 *aIOCacheSize = mHWData->mIOCacheSize;
3460
3461 return S_OK;
3462}
3463
3464STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3465{
3466 AutoCaller autoCaller(this);
3467 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3468
3469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3470
3471 HRESULT rc = checkStateDependency(MutableStateDep);
3472 if (FAILED(rc)) return rc;
3473
3474 setModified(IsModified_MachineData);
3475 mHWData.backup();
3476 mHWData->mIOCacheSize = aIOCacheSize;
3477
3478 return S_OK;
3479}
3480
3481
3482/**
3483 * @note Locks objects!
3484 */
3485STDMETHODIMP Machine::LockMachine(ISession *aSession,
3486 LockType_T lockType)
3487{
3488 CheckComArgNotNull(aSession);
3489
3490 AutoCaller autoCaller(this);
3491 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3492
3493 /* check the session state */
3494 SessionState_T state;
3495 HRESULT rc = aSession->COMGETTER(State)(&state);
3496 if (FAILED(rc)) return rc;
3497
3498 if (state != SessionState_Unlocked)
3499 return setError(VBOX_E_INVALID_OBJECT_STATE,
3500 tr("The given session is busy"));
3501
3502 // get the client's IInternalSessionControl interface
3503 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3504 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3505 E_INVALIDARG);
3506
3507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3508
3509 if (!mData->mRegistered)
3510 return setError(E_UNEXPECTED,
3511 tr("The machine '%s' is not registered"),
3512 mUserData->s.strName.c_str());
3513
3514 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3515
3516 SessionState_T oldState = mData->mSession.mState;
3517 /* Hack: in case the session is closing and there is a progress object
3518 * which allows waiting for the session to be closed, take the opportunity
3519 * and do a limited wait (max. 1 second). This helps a lot when the system
3520 * is busy and thus session closing can take a little while. */
3521 if ( mData->mSession.mState == SessionState_Unlocking
3522 && mData->mSession.mProgress)
3523 {
3524 alock.release();
3525 mData->mSession.mProgress->WaitForCompletion(1000);
3526 alock.acquire();
3527 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3528 }
3529
3530 // try again now
3531 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3532 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3533 )
3534 {
3535 // OK, share the session... we are now dealing with three processes:
3536 // 1) VBoxSVC (where this code runs);
3537 // 2) process C: the caller's client process (who wants a shared session);
3538 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3539
3540 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3541 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3542 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3543 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3544 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3545
3546 /*
3547 * Release the lock before calling the client process. It's safe here
3548 * since the only thing to do after we get the lock again is to add
3549 * the remote control to the list (which doesn't directly influence
3550 * anything).
3551 */
3552 alock.release();
3553
3554 // get the console of the session holding the write lock (this is a remote call)
3555 ComPtr<IConsole> pConsoleW;
3556 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3557 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3558 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3559 if (FAILED(rc))
3560 // the failure may occur w/o any error info (from RPC), so provide one
3561 return setError(VBOX_E_VM_ERROR,
3562 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3563
3564 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3565
3566 // share the session machine and W's console with the caller's session
3567 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3568 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3569 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3570
3571 if (FAILED(rc))
3572 // the failure may occur w/o any error info (from RPC), so provide one
3573 return setError(VBOX_E_VM_ERROR,
3574 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3575 alock.acquire();
3576
3577 // need to revalidate the state after acquiring the lock again
3578 if (mData->mSession.mState != SessionState_Locked)
3579 {
3580 pSessionControl->Uninitialize();
3581 return setError(VBOX_E_INVALID_SESSION_STATE,
3582 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3583 mUserData->s.strName.c_str());
3584 }
3585
3586 // add the caller's session to the list
3587 mData->mSession.mRemoteControls.push_back(pSessionControl);
3588 }
3589 else if ( mData->mSession.mState == SessionState_Locked
3590 || mData->mSession.mState == SessionState_Unlocking
3591 )
3592 {
3593 // sharing not permitted, or machine still unlocking:
3594 return setError(VBOX_E_INVALID_OBJECT_STATE,
3595 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3596 mUserData->s.strName.c_str());
3597 }
3598 else
3599 {
3600 // machine is not locked: then write-lock the machine (create the session machine)
3601
3602 // must not be busy
3603 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3604
3605 // get the caller's session PID
3606 RTPROCESS pid = NIL_RTPROCESS;
3607 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3608 pSessionControl->GetPID((ULONG*)&pid);
3609 Assert(pid != NIL_RTPROCESS);
3610
3611 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3612
3613 if (fLaunchingVMProcess)
3614 {
3615 // this machine is awaiting for a spawning session to be opened:
3616 // then the calling process must be the one that got started by
3617 // LaunchVMProcess()
3618
3619 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3620 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3621
3622 if (mData->mSession.mPID != pid)
3623 return setError(E_ACCESSDENIED,
3624 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3625 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3626 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3627 }
3628
3629 // create the mutable SessionMachine from the current machine
3630 ComObjPtr<SessionMachine> sessionMachine;
3631 sessionMachine.createObject();
3632 rc = sessionMachine->init(this);
3633 AssertComRC(rc);
3634
3635 /* NOTE: doing return from this function after this point but
3636 * before the end is forbidden since it may call SessionMachine::uninit()
3637 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3638 * lock while still holding the Machine lock in alock so that a deadlock
3639 * is possible due to the wrong lock order. */
3640
3641 if (SUCCEEDED(rc))
3642 {
3643 /*
3644 * Set the session state to Spawning to protect against subsequent
3645 * attempts to open a session and to unregister the machine after
3646 * we release the lock.
3647 */
3648 SessionState_T origState = mData->mSession.mState;
3649 mData->mSession.mState = SessionState_Spawning;
3650
3651 /*
3652 * Release the lock before calling the client process -- it will call
3653 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3654 * because the state is Spawning, so that LaunchVMProcess() and
3655 * LockMachine() calls will fail. This method, called before we
3656 * acquire the lock again, will fail because of the wrong PID.
3657 *
3658 * Note that mData->mSession.mRemoteControls accessed outside
3659 * the lock may not be modified when state is Spawning, so it's safe.
3660 */
3661 alock.release();
3662
3663 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3664 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3665 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3666
3667 /* The failure may occur w/o any error info (from RPC), so provide one */
3668 if (FAILED(rc))
3669 setError(VBOX_E_VM_ERROR,
3670 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3671
3672 if ( SUCCEEDED(rc)
3673 && fLaunchingVMProcess
3674 )
3675 {
3676 /* complete the remote session initialization */
3677
3678 /* get the console from the direct session */
3679 ComPtr<IConsole> console;
3680 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3681 ComAssertComRC(rc);
3682
3683 if (SUCCEEDED(rc) && !console)
3684 {
3685 ComAssert(!!console);
3686 rc = E_FAIL;
3687 }
3688
3689 /* assign machine & console to the remote session */
3690 if (SUCCEEDED(rc))
3691 {
3692 /*
3693 * after LaunchVMProcess(), the first and the only
3694 * entry in remoteControls is that remote session
3695 */
3696 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3697 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3698 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3699
3700 /* The failure may occur w/o any error info (from RPC), so provide one */
3701 if (FAILED(rc))
3702 setError(VBOX_E_VM_ERROR,
3703 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3704 }
3705
3706 if (FAILED(rc))
3707 pSessionControl->Uninitialize();
3708 }
3709
3710 /* acquire the lock again */
3711 alock.acquire();
3712
3713 /* Restore the session state */
3714 mData->mSession.mState = origState;
3715 }
3716
3717 // finalize spawning anyway (this is why we don't return on errors above)
3718 if (fLaunchingVMProcess)
3719 {
3720 /* Note that the progress object is finalized later */
3721 /** @todo Consider checking mData->mSession.mProgress for cancellation
3722 * around here. */
3723
3724 /* We don't reset mSession.mPID here because it is necessary for
3725 * SessionMachine::uninit() to reap the child process later. */
3726
3727 if (FAILED(rc))
3728 {
3729 /* Close the remote session, remove the remote control from the list
3730 * and reset session state to Closed (@note keep the code in sync
3731 * with the relevant part in openSession()). */
3732
3733 Assert(mData->mSession.mRemoteControls.size() == 1);
3734 if (mData->mSession.mRemoteControls.size() == 1)
3735 {
3736 ErrorInfoKeeper eik;
3737 mData->mSession.mRemoteControls.front()->Uninitialize();
3738 }
3739
3740 mData->mSession.mRemoteControls.clear();
3741 mData->mSession.mState = SessionState_Unlocked;
3742 }
3743 }
3744 else
3745 {
3746 /* memorize PID of the directly opened session */
3747 if (SUCCEEDED(rc))
3748 mData->mSession.mPID = pid;
3749 }
3750
3751 if (SUCCEEDED(rc))
3752 {
3753 /* memorize the direct session control and cache IUnknown for it */
3754 mData->mSession.mDirectControl = pSessionControl;
3755 mData->mSession.mState = SessionState_Locked;
3756 /* associate the SessionMachine with this Machine */
3757 mData->mSession.mMachine = sessionMachine;
3758
3759 /* request an IUnknown pointer early from the remote party for later
3760 * identity checks (it will be internally cached within mDirectControl
3761 * at least on XPCOM) */
3762 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3763 NOREF(unk);
3764 }
3765
3766 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3767 * would break the lock order */
3768 alock.release();
3769
3770 /* uninitialize the created session machine on failure */
3771 if (FAILED(rc))
3772 sessionMachine->uninit();
3773
3774 }
3775
3776 if (SUCCEEDED(rc))
3777 {
3778 /*
3779 * tell the client watcher thread to update the set of
3780 * machines that have open sessions
3781 */
3782 mParent->updateClientWatcher();
3783
3784 if (oldState != SessionState_Locked)
3785 /* fire an event */
3786 mParent->onSessionStateChange(getId(), SessionState_Locked);
3787 }
3788
3789 return rc;
3790}
3791
3792/**
3793 * @note Locks objects!
3794 */
3795STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3796 IN_BSTR aFrontend,
3797 IN_BSTR aEnvironment,
3798 IProgress **aProgress)
3799{
3800 CheckComArgStr(aFrontend);
3801 Utf8Str strFrontend(aFrontend);
3802 Utf8Str strEnvironment(aEnvironment);
3803 /* "emergencystop" doesn't need the session, so skip the checks/interface
3804 * retrieval. This code doesn't quite fit in here, but introducing a
3805 * special API method would be even more effort, and would require explicit
3806 * support by every API client. It's better to hide the feature a bit. */
3807 if (strFrontend != "emergencystop")
3808 CheckComArgNotNull(aSession);
3809 CheckComArgOutPointerValid(aProgress);
3810
3811 AutoCaller autoCaller(this);
3812 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3813
3814 HRESULT rc = S_OK;
3815 if (strFrontend.isEmpty())
3816 {
3817 Bstr bstrFrontend;
3818 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3819 if (FAILED(rc))
3820 return rc;
3821 strFrontend = bstrFrontend;
3822 if (strFrontend.isEmpty())
3823 {
3824 ComPtr<ISystemProperties> systemProperties;
3825 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3826 if (FAILED(rc))
3827 return rc;
3828 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3829 if (FAILED(rc))
3830 return rc;
3831 strFrontend = bstrFrontend;
3832 }
3833 /* paranoia - emergencystop is not a valid default */
3834 if (strFrontend == "emergencystop")
3835 strFrontend = Utf8Str::Empty;
3836 }
3837 /* default frontend: Qt GUI */
3838 if (strFrontend.isEmpty())
3839 strFrontend = "GUI/Qt";
3840
3841 if (strFrontend != "emergencystop")
3842 {
3843 /* check the session state */
3844 SessionState_T state;
3845 rc = aSession->COMGETTER(State)(&state);
3846 if (FAILED(rc))
3847 return rc;
3848
3849 if (state != SessionState_Unlocked)
3850 return setError(VBOX_E_INVALID_OBJECT_STATE,
3851 tr("The given session is busy"));
3852
3853 /* get the IInternalSessionControl interface */
3854 ComPtr<IInternalSessionControl> control(aSession);
3855 ComAssertMsgRet(!control.isNull(),
3856 ("No IInternalSessionControl interface"),
3857 E_INVALIDARG);
3858
3859 /* get the teleporter enable state for the progress object init. */
3860 BOOL fTeleporterEnabled;
3861 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3862 if (FAILED(rc))
3863 return rc;
3864
3865 /* create a progress object */
3866 ComObjPtr<ProgressProxy> progress;
3867 progress.createObject();
3868 rc = progress->init(mParent,
3869 static_cast<IMachine*>(this),
3870 Bstr(tr("Starting VM")).raw(),
3871 TRUE /* aCancelable */,
3872 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3873 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3874 2 /* uFirstOperationWeight */,
3875 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3876
3877 if (SUCCEEDED(rc))
3878 {
3879 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3880 if (SUCCEEDED(rc))
3881 {
3882 progress.queryInterfaceTo(aProgress);
3883
3884 /* signal the client watcher thread */
3885 mParent->updateClientWatcher();
3886
3887 /* fire an event */
3888 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3889 }
3890 }
3891 }
3892 else
3893 {
3894 /* no progress object - either instant success or failure */
3895 *aProgress = NULL;
3896
3897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3898
3899 if (mData->mSession.mState != SessionState_Locked)
3900 return setError(VBOX_E_INVALID_OBJECT_STATE,
3901 tr("The machine '%s' is not locked by a session"),
3902 mUserData->s.strName.c_str());
3903
3904 /* must have a VM process associated - do not kill normal API clients
3905 * with an open session */
3906 if (!Global::IsOnline(mData->mMachineState))
3907 return setError(VBOX_E_INVALID_OBJECT_STATE,
3908 tr("The machine '%s' does not have a VM process"),
3909 mUserData->s.strName.c_str());
3910
3911 /* forcibly terminate the VM process */
3912 if (mData->mSession.mPID != NIL_RTPROCESS)
3913 RTProcTerminate(mData->mSession.mPID);
3914
3915 /* signal the client watcher thread, as most likely the client has
3916 * been terminated */
3917 mParent->updateClientWatcher();
3918 }
3919
3920 return rc;
3921}
3922
3923STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3924{
3925 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3926 return setError(E_INVALIDARG,
3927 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3928 aPosition, SchemaDefs::MaxBootPosition);
3929
3930 if (aDevice == DeviceType_USB)
3931 return setError(E_NOTIMPL,
3932 tr("Booting from USB device is currently not supported"));
3933
3934 AutoCaller autoCaller(this);
3935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3936
3937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3938
3939 HRESULT rc = checkStateDependency(MutableStateDep);
3940 if (FAILED(rc)) return rc;
3941
3942 setModified(IsModified_MachineData);
3943 mHWData.backup();
3944 mHWData->mBootOrder[aPosition - 1] = aDevice;
3945
3946 return S_OK;
3947}
3948
3949STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3950{
3951 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3952 return setError(E_INVALIDARG,
3953 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3954 aPosition, SchemaDefs::MaxBootPosition);
3955
3956 AutoCaller autoCaller(this);
3957 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3958
3959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3960
3961 *aDevice = mHWData->mBootOrder[aPosition - 1];
3962
3963 return S_OK;
3964}
3965
3966STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3967 LONG aControllerPort,
3968 LONG aDevice,
3969 DeviceType_T aType,
3970 IMedium *aMedium)
3971{
3972 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3973 aControllerName, aControllerPort, aDevice, aType, aMedium));
3974
3975 CheckComArgStrNotEmptyOrNull(aControllerName);
3976
3977 AutoCaller autoCaller(this);
3978 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3979
3980 // request the host lock first, since might be calling Host methods for getting host drives;
3981 // next, protect the media tree all the while we're in here, as well as our member variables
3982 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3983 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3984
3985 HRESULT rc = checkStateDependency(MutableStateDep);
3986 if (FAILED(rc)) return rc;
3987
3988 /// @todo NEWMEDIA implicit machine registration
3989 if (!mData->mRegistered)
3990 return setError(VBOX_E_INVALID_OBJECT_STATE,
3991 tr("Cannot attach storage devices to an unregistered machine"));
3992
3993 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3994
3995 /* Check for an existing controller. */
3996 ComObjPtr<StorageController> ctl;
3997 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3998 if (FAILED(rc)) return rc;
3999
4000 StorageControllerType_T ctrlType;
4001 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4002 if (FAILED(rc))
4003 return setError(E_FAIL,
4004 tr("Could not get type of controller '%ls'"),
4005 aControllerName);
4006
4007 bool fSilent = false;
4008 Utf8Str strReconfig;
4009
4010 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4011 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4012 if (FAILED(rc))
4013 return rc;
4014 if ( mData->mMachineState == MachineState_Paused
4015 && strReconfig == "1")
4016 fSilent = true;
4017
4018 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4019 bool fHotplug = false;
4020 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4021 fHotplug = true;
4022
4023 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4024 return setError(VBOX_E_INVALID_VM_STATE,
4025 tr("Controller '%ls' does not support hotplugging"),
4026 aControllerName);
4027
4028 // check that the port and device are not out of range
4029 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4030 if (FAILED(rc)) return rc;
4031
4032 /* check if the device slot is already busy */
4033 MediumAttachment *pAttachTemp;
4034 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4035 aControllerName,
4036 aControllerPort,
4037 aDevice)))
4038 {
4039 Medium *pMedium = pAttachTemp->getMedium();
4040 if (pMedium)
4041 {
4042 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4043 return setError(VBOX_E_OBJECT_IN_USE,
4044 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4045 pMedium->getLocationFull().c_str(),
4046 aControllerPort,
4047 aDevice,
4048 aControllerName);
4049 }
4050 else
4051 return setError(VBOX_E_OBJECT_IN_USE,
4052 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4053 aControllerPort, aDevice, aControllerName);
4054 }
4055
4056 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4057 if (aMedium && medium.isNull())
4058 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4059
4060 AutoCaller mediumCaller(medium);
4061 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4062
4063 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4064
4065 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4066 && !medium.isNull()
4067 )
4068 return setError(VBOX_E_OBJECT_IN_USE,
4069 tr("Medium '%s' is already attached to this virtual machine"),
4070 medium->getLocationFull().c_str());
4071
4072 if (!medium.isNull())
4073 {
4074 MediumType_T mtype = medium->getType();
4075 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4076 // For DVDs it's not written to the config file, so needs no global config
4077 // version bump. For floppies it's a new attribute "type", which is ignored
4078 // by older VirtualBox version, so needs no global config version bump either.
4079 // For hard disks this type is not accepted.
4080 if (mtype == MediumType_MultiAttach)
4081 {
4082 // This type is new with VirtualBox 4.0 and therefore requires settings
4083 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4084 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4085 // two reasons: The medium type is a property of the media registry tree, which
4086 // can reside in the global config file (for pre-4.0 media); we would therefore
4087 // possibly need to bump the global config version. We don't want to do that though
4088 // because that might make downgrading to pre-4.0 impossible.
4089 // As a result, we can only use these two new types if the medium is NOT in the
4090 // global registry:
4091 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4092 if ( medium->isInRegistry(uuidGlobalRegistry)
4093 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4094 )
4095 return setError(VBOX_E_INVALID_OBJECT_STATE,
4096 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4097 "to machines that were created with VirtualBox 4.0 or later"),
4098 medium->getLocationFull().c_str());
4099 }
4100 }
4101
4102 bool fIndirect = false;
4103 if (!medium.isNull())
4104 fIndirect = medium->isReadOnly();
4105 bool associate = true;
4106
4107 do
4108 {
4109 if ( aType == DeviceType_HardDisk
4110 && mMediaData.isBackedUp())
4111 {
4112 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4113
4114 /* check if the medium was attached to the VM before we started
4115 * changing attachments in which case the attachment just needs to
4116 * be restored */
4117 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4118 {
4119 AssertReturn(!fIndirect, E_FAIL);
4120
4121 /* see if it's the same bus/channel/device */
4122 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4123 {
4124 /* the simplest case: restore the whole attachment
4125 * and return, nothing else to do */
4126 mMediaData->mAttachments.push_back(pAttachTemp);
4127
4128 /* Reattach the medium to the VM. */
4129 if (fHotplug || fSilent)
4130 {
4131 mediumLock.release();
4132 treeLock.release();
4133 alock.release();
4134
4135 MediumLockList *pMediumLockList(new MediumLockList());
4136
4137 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4138 true /* fMediumLockWrite */,
4139 NULL,
4140 *pMediumLockList);
4141 alock.acquire();
4142 if (FAILED(rc))
4143 delete pMediumLockList;
4144 else
4145 {
4146 mData->mSession.mLockedMedia.Unlock();
4147 alock.release();
4148 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4149 mData->mSession.mLockedMedia.Lock();
4150 alock.acquire();
4151 }
4152 alock.release();
4153
4154 if (SUCCEEDED(rc))
4155 {
4156 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4157 /* Remove lock list in case of error. */
4158 if (FAILED(rc))
4159 {
4160 mData->mSession.mLockedMedia.Unlock();
4161 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4162 mData->mSession.mLockedMedia.Lock();
4163 }
4164 }
4165 }
4166
4167 return S_OK;
4168 }
4169
4170 /* bus/channel/device differ; we need a new attachment object,
4171 * but don't try to associate it again */
4172 associate = false;
4173 break;
4174 }
4175 }
4176
4177 /* go further only if the attachment is to be indirect */
4178 if (!fIndirect)
4179 break;
4180
4181 /* perform the so called smart attachment logic for indirect
4182 * attachments. Note that smart attachment is only applicable to base
4183 * hard disks. */
4184
4185 if (medium->getParent().isNull())
4186 {
4187 /* first, investigate the backup copy of the current hard disk
4188 * attachments to make it possible to re-attach existing diffs to
4189 * another device slot w/o losing their contents */
4190 if (mMediaData.isBackedUp())
4191 {
4192 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4193
4194 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4195 uint32_t foundLevel = 0;
4196
4197 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4198 it != oldAtts.end();
4199 ++it)
4200 {
4201 uint32_t level = 0;
4202 MediumAttachment *pAttach = *it;
4203 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4204 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4205 if (pMedium.isNull())
4206 continue;
4207
4208 if (pMedium->getBase(&level) == medium)
4209 {
4210 /* skip the hard disk if its currently attached (we
4211 * cannot attach the same hard disk twice) */
4212 if (findAttachment(mMediaData->mAttachments,
4213 pMedium))
4214 continue;
4215
4216 /* matched device, channel and bus (i.e. attached to the
4217 * same place) will win and immediately stop the search;
4218 * otherwise the attachment that has the youngest
4219 * descendant of medium will be used
4220 */
4221 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4222 {
4223 /* the simplest case: restore the whole attachment
4224 * and return, nothing else to do */
4225 mMediaData->mAttachments.push_back(*it);
4226
4227 /* Reattach the medium to the VM. */
4228 if (fHotplug || fSilent)
4229 {
4230 mediumLock.release();
4231 treeLock.release();
4232 alock.release();
4233
4234 MediumLockList *pMediumLockList(new MediumLockList());
4235
4236 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4237 true /* fMediumLockWrite */,
4238 NULL,
4239 *pMediumLockList);
4240 alock.acquire();
4241 if (FAILED(rc))
4242 delete pMediumLockList;
4243 else
4244 {
4245 mData->mSession.mLockedMedia.Unlock();
4246 alock.release();
4247 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4248 mData->mSession.mLockedMedia.Lock();
4249 alock.acquire();
4250 }
4251 alock.release();
4252
4253 if (SUCCEEDED(rc))
4254 {
4255 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4256 /* Remove lock list in case of error. */
4257 if (FAILED(rc))
4258 {
4259 mData->mSession.mLockedMedia.Unlock();
4260 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4261 mData->mSession.mLockedMedia.Lock();
4262 }
4263 }
4264 }
4265
4266 return S_OK;
4267 }
4268 else if ( foundIt == oldAtts.end()
4269 || level > foundLevel /* prefer younger */
4270 )
4271 {
4272 foundIt = it;
4273 foundLevel = level;
4274 }
4275 }
4276 }
4277
4278 if (foundIt != oldAtts.end())
4279 {
4280 /* use the previously attached hard disk */
4281 medium = (*foundIt)->getMedium();
4282 mediumCaller.attach(medium);
4283 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4284 mediumLock.attach(medium);
4285 /* not implicit, doesn't require association with this VM */
4286 fIndirect = false;
4287 associate = false;
4288 /* go right to the MediumAttachment creation */
4289 break;
4290 }
4291 }
4292
4293 /* must give up the medium lock and medium tree lock as below we
4294 * go over snapshots, which needs a lock with higher lock order. */
4295 mediumLock.release();
4296 treeLock.release();
4297
4298 /* then, search through snapshots for the best diff in the given
4299 * hard disk's chain to base the new diff on */
4300
4301 ComObjPtr<Medium> base;
4302 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4303 while (snap)
4304 {
4305 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4306
4307 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4308
4309 MediumAttachment *pAttachFound = NULL;
4310 uint32_t foundLevel = 0;
4311
4312 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4313 it != snapAtts.end();
4314 ++it)
4315 {
4316 MediumAttachment *pAttach = *it;
4317 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4318 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4319 if (pMedium.isNull())
4320 continue;
4321
4322 uint32_t level = 0;
4323 if (pMedium->getBase(&level) == medium)
4324 {
4325 /* matched device, channel and bus (i.e. attached to the
4326 * same place) will win and immediately stop the search;
4327 * otherwise the attachment that has the youngest
4328 * descendant of medium will be used
4329 */
4330 if ( pAttach->getDevice() == aDevice
4331 && pAttach->getPort() == aControllerPort
4332 && pAttach->getControllerName() == aControllerName
4333 )
4334 {
4335 pAttachFound = pAttach;
4336 break;
4337 }
4338 else if ( !pAttachFound
4339 || level > foundLevel /* prefer younger */
4340 )
4341 {
4342 pAttachFound = pAttach;
4343 foundLevel = level;
4344 }
4345 }
4346 }
4347
4348 if (pAttachFound)
4349 {
4350 base = pAttachFound->getMedium();
4351 break;
4352 }
4353
4354 snap = snap->getParent();
4355 }
4356
4357 /* re-lock medium tree and the medium, as we need it below */
4358 treeLock.acquire();
4359 mediumLock.acquire();
4360
4361 /* found a suitable diff, use it as a base */
4362 if (!base.isNull())
4363 {
4364 medium = base;
4365 mediumCaller.attach(medium);
4366 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4367 mediumLock.attach(medium);
4368 }
4369 }
4370
4371 Utf8Str strFullSnapshotFolder;
4372 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4373
4374 ComObjPtr<Medium> diff;
4375 diff.createObject();
4376 // store this diff in the same registry as the parent
4377 Guid uuidRegistryParent;
4378 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4379 {
4380 // parent image has no registry: this can happen if we're attaching a new immutable
4381 // image that has not yet been attached (medium then points to the base and we're
4382 // creating the diff image for the immutable, and the parent is not yet registered);
4383 // put the parent in the machine registry then
4384 mediumLock.release();
4385 treeLock.release();
4386 alock.release();
4387 addMediumToRegistry(medium);
4388 alock.acquire();
4389 treeLock.acquire();
4390 mediumLock.acquire();
4391 medium->getFirstRegistryMachineId(uuidRegistryParent);
4392 }
4393 rc = diff->init(mParent,
4394 medium->getPreferredDiffFormat(),
4395 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4396 uuidRegistryParent);
4397 if (FAILED(rc)) return rc;
4398
4399 /* Apply the normal locking logic to the entire chain. */
4400 MediumLockList *pMediumLockList(new MediumLockList());
4401 mediumLock.release();
4402 treeLock.release();
4403 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4404 true /* fMediumLockWrite */,
4405 medium,
4406 *pMediumLockList);
4407 treeLock.acquire();
4408 mediumLock.acquire();
4409 if (SUCCEEDED(rc))
4410 {
4411 mediumLock.release();
4412 treeLock.release();
4413 rc = pMediumLockList->Lock();
4414 treeLock.acquire();
4415 mediumLock.acquire();
4416 if (FAILED(rc))
4417 setError(rc,
4418 tr("Could not lock medium when creating diff '%s'"),
4419 diff->getLocationFull().c_str());
4420 else
4421 {
4422 /* will release the lock before the potentially lengthy
4423 * operation, so protect with the special state */
4424 MachineState_T oldState = mData->mMachineState;
4425 setMachineState(MachineState_SettingUp);
4426
4427 mediumLock.release();
4428 treeLock.release();
4429 alock.release();
4430
4431 rc = medium->createDiffStorage(diff,
4432 MediumVariant_Standard,
4433 pMediumLockList,
4434 NULL /* aProgress */,
4435 true /* aWait */);
4436
4437 alock.acquire();
4438 treeLock.acquire();
4439 mediumLock.acquire();
4440
4441 setMachineState(oldState);
4442 }
4443 }
4444
4445 /* Unlock the media and free the associated memory. */
4446 delete pMediumLockList;
4447
4448 if (FAILED(rc)) return rc;
4449
4450 /* use the created diff for the actual attachment */
4451 medium = diff;
4452 mediumCaller.attach(medium);
4453 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4454 mediumLock.attach(medium);
4455 }
4456 while (0);
4457
4458 ComObjPtr<MediumAttachment> attachment;
4459 attachment.createObject();
4460 rc = attachment->init(this,
4461 medium,
4462 aControllerName,
4463 aControllerPort,
4464 aDevice,
4465 aType,
4466 fIndirect,
4467 false /* fPassthrough */,
4468 false /* fTempEject */,
4469 false /* fNonRotational */,
4470 false /* fDiscard */,
4471 Utf8Str::Empty);
4472 if (FAILED(rc)) return rc;
4473
4474 if (associate && !medium.isNull())
4475 {
4476 // as the last step, associate the medium to the VM
4477 rc = medium->addBackReference(mData->mUuid);
4478 // here we can fail because of Deleting, or being in process of creating a Diff
4479 if (FAILED(rc)) return rc;
4480
4481 mediumLock.release();
4482 treeLock.release();
4483 alock.release();
4484 addMediumToRegistry(medium);
4485 alock.acquire();
4486 treeLock.acquire();
4487 mediumLock.acquire();
4488 }
4489
4490 /* success: finally remember the attachment */
4491 setModified(IsModified_Storage);
4492 mMediaData.backup();
4493 mMediaData->mAttachments.push_back(attachment);
4494
4495 mediumLock.release();
4496 treeLock.release();
4497 alock.release();
4498
4499 if (fHotplug || fSilent)
4500 {
4501 MediumLockList *pMediumLockList(new MediumLockList());
4502
4503 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4504 true /* fMediumLockWrite */,
4505 NULL,
4506 *pMediumLockList);
4507 alock.acquire();
4508 if (FAILED(rc))
4509 delete pMediumLockList;
4510 else
4511 {
4512 mData->mSession.mLockedMedia.Unlock();
4513 alock.release();
4514 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4515 mData->mSession.mLockedMedia.Lock();
4516 alock.acquire();
4517 }
4518 alock.release();
4519
4520 if (SUCCEEDED(rc))
4521 {
4522 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4523 /* Remove lock list in case of error. */
4524 if (FAILED(rc))
4525 {
4526 mData->mSession.mLockedMedia.Unlock();
4527 mData->mSession.mLockedMedia.Remove(attachment);
4528 mData->mSession.mLockedMedia.Lock();
4529 }
4530 }
4531 }
4532
4533 mParent->saveModifiedRegistries();
4534
4535 return rc;
4536}
4537
4538STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4539 LONG aDevice)
4540{
4541 CheckComArgStrNotEmptyOrNull(aControllerName);
4542
4543 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4544 aControllerName, aControllerPort, aDevice));
4545
4546 AutoCaller autoCaller(this);
4547 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4548
4549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4550
4551 HRESULT rc = checkStateDependency(MutableStateDep);
4552 if (FAILED(rc)) return rc;
4553
4554 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4555
4556 /* Check for an existing controller. */
4557 ComObjPtr<StorageController> ctl;
4558 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4559 if (FAILED(rc)) return rc;
4560
4561 StorageControllerType_T ctrlType;
4562 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4563 if (FAILED(rc))
4564 return setError(E_FAIL,
4565 tr("Could not get type of controller '%ls'"),
4566 aControllerName);
4567
4568 bool fSilent = false;
4569 Utf8Str strReconfig;
4570
4571 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4572 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4573 if (FAILED(rc))
4574 return rc;
4575 if ( mData->mMachineState == MachineState_Paused
4576 && strReconfig == "1")
4577 fSilent = true;
4578
4579 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4580 bool fHotplug = false;
4581 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4582 fHotplug = true;
4583
4584 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4585 return setError(VBOX_E_INVALID_VM_STATE,
4586 tr("Controller '%ls' does not support hotplugging"),
4587 aControllerName);
4588
4589 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4590 aControllerName,
4591 aControllerPort,
4592 aDevice);
4593 if (!pAttach)
4594 return setError(VBOX_E_OBJECT_NOT_FOUND,
4595 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4596 aDevice, aControllerPort, aControllerName);
4597
4598 /*
4599 * The VM has to detach the device before we delete any implicit diffs.
4600 * If this fails we can roll back without loosing data.
4601 */
4602 if (fHotplug || fSilent)
4603 {
4604 alock.release();
4605 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4606 alock.acquire();
4607 }
4608 if (FAILED(rc)) return rc;
4609
4610 /* If we are here everything went well and we can delete the implicit now. */
4611 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4612
4613 alock.release();
4614
4615 mParent->saveModifiedRegistries();
4616
4617 return rc;
4618}
4619
4620STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4621 LONG aDevice, BOOL aPassthrough)
4622{
4623 CheckComArgStrNotEmptyOrNull(aControllerName);
4624
4625 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4626 aControllerName, aControllerPort, aDevice, aPassthrough));
4627
4628 AutoCaller autoCaller(this);
4629 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4630
4631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 HRESULT rc = checkStateDependency(MutableStateDep);
4634 if (FAILED(rc)) return rc;
4635
4636 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4637
4638 if (Global::IsOnlineOrTransient(mData->mMachineState))
4639 return setError(VBOX_E_INVALID_VM_STATE,
4640 tr("Invalid machine state: %s"),
4641 Global::stringifyMachineState(mData->mMachineState));
4642
4643 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4644 aControllerName,
4645 aControllerPort,
4646 aDevice);
4647 if (!pAttach)
4648 return setError(VBOX_E_OBJECT_NOT_FOUND,
4649 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4650 aDevice, aControllerPort, aControllerName);
4651
4652
4653 setModified(IsModified_Storage);
4654 mMediaData.backup();
4655
4656 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4657
4658 if (pAttach->getType() != DeviceType_DVD)
4659 return setError(E_INVALIDARG,
4660 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4661 aDevice, aControllerPort, aControllerName);
4662 pAttach->updatePassthrough(!!aPassthrough);
4663
4664 return S_OK;
4665}
4666
4667STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4668 LONG aDevice, BOOL aTemporaryEject)
4669{
4670 CheckComArgStrNotEmptyOrNull(aControllerName);
4671
4672 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4673 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4674
4675 AutoCaller autoCaller(this);
4676 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4677
4678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4679
4680 HRESULT rc = checkStateDependency(MutableStateDep);
4681 if (FAILED(rc)) return rc;
4682
4683 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4684 aControllerName,
4685 aControllerPort,
4686 aDevice);
4687 if (!pAttach)
4688 return setError(VBOX_E_OBJECT_NOT_FOUND,
4689 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4690 aDevice, aControllerPort, aControllerName);
4691
4692
4693 setModified(IsModified_Storage);
4694 mMediaData.backup();
4695
4696 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4697
4698 if (pAttach->getType() != DeviceType_DVD)
4699 return setError(E_INVALIDARG,
4700 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4701 aDevice, aControllerPort, aControllerName);
4702 pAttach->updateTempEject(!!aTemporaryEject);
4703
4704 return S_OK;
4705}
4706
4707STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4708 LONG aDevice, BOOL aNonRotational)
4709{
4710 CheckComArgStrNotEmptyOrNull(aControllerName);
4711
4712 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4713 aControllerName, aControllerPort, aDevice, aNonRotational));
4714
4715 AutoCaller autoCaller(this);
4716 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4717
4718 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4719
4720 HRESULT rc = checkStateDependency(MutableStateDep);
4721 if (FAILED(rc)) return rc;
4722
4723 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4724
4725 if (Global::IsOnlineOrTransient(mData->mMachineState))
4726 return setError(VBOX_E_INVALID_VM_STATE,
4727 tr("Invalid machine state: %s"),
4728 Global::stringifyMachineState(mData->mMachineState));
4729
4730 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4731 aControllerName,
4732 aControllerPort,
4733 aDevice);
4734 if (!pAttach)
4735 return setError(VBOX_E_OBJECT_NOT_FOUND,
4736 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4737 aDevice, aControllerPort, aControllerName);
4738
4739
4740 setModified(IsModified_Storage);
4741 mMediaData.backup();
4742
4743 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4744
4745 if (pAttach->getType() != DeviceType_HardDisk)
4746 return setError(E_INVALIDARG,
4747 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"),
4748 aDevice, aControllerPort, aControllerName);
4749 pAttach->updateNonRotational(!!aNonRotational);
4750
4751 return S_OK;
4752}
4753
4754STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4755 LONG aDevice, BOOL aDiscard)
4756{
4757 CheckComArgStrNotEmptyOrNull(aControllerName);
4758
4759 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4760 aControllerName, aControllerPort, aDevice, aDiscard));
4761
4762 AutoCaller autoCaller(this);
4763 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4764
4765 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4766
4767 HRESULT rc = checkStateDependency(MutableStateDep);
4768 if (FAILED(rc)) return rc;
4769
4770 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4771
4772 if (Global::IsOnlineOrTransient(mData->mMachineState))
4773 return setError(VBOX_E_INVALID_VM_STATE,
4774 tr("Invalid machine state: %s"),
4775 Global::stringifyMachineState(mData->mMachineState));
4776
4777 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4778 aControllerName,
4779 aControllerPort,
4780 aDevice);
4781 if (!pAttach)
4782 return setError(VBOX_E_OBJECT_NOT_FOUND,
4783 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4784 aDevice, aControllerPort, aControllerName);
4785
4786
4787 setModified(IsModified_Storage);
4788 mMediaData.backup();
4789
4790 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4791
4792 if (pAttach->getType() != DeviceType_HardDisk)
4793 return setError(E_INVALIDARG,
4794 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"),
4795 aDevice, aControllerPort, aControllerName);
4796 pAttach->updateDiscard(!!aDiscard);
4797
4798 return S_OK;
4799}
4800
4801STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4802 LONG aDevice)
4803{
4804 int rc = S_OK;
4805 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4806 aControllerName, aControllerPort, aDevice));
4807
4808 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4809
4810 return rc;
4811}
4812
4813STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4814 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4815{
4816 CheckComArgStrNotEmptyOrNull(aControllerName);
4817
4818 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4819 aControllerName, aControllerPort, aDevice));
4820
4821 AutoCaller autoCaller(this);
4822 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4823
4824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4825
4826 HRESULT rc = checkStateDependency(MutableStateDep);
4827 if (FAILED(rc)) return rc;
4828
4829 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4830
4831 if (Global::IsOnlineOrTransient(mData->mMachineState))
4832 return setError(VBOX_E_INVALID_VM_STATE,
4833 tr("Invalid machine state: %s"),
4834 Global::stringifyMachineState(mData->mMachineState));
4835
4836 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4837 aControllerName,
4838 aControllerPort,
4839 aDevice);
4840 if (!pAttach)
4841 return setError(VBOX_E_OBJECT_NOT_FOUND,
4842 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4843 aDevice, aControllerPort, aControllerName);
4844
4845
4846 setModified(IsModified_Storage);
4847 mMediaData.backup();
4848
4849 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4850 if (aBandwidthGroup && group.isNull())
4851 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4852
4853 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4854
4855 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4856 if (strBandwidthGroupOld.isNotEmpty())
4857 {
4858 /* Get the bandwidth group object and release it - this must not fail. */
4859 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4860 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4861 Assert(SUCCEEDED(rc));
4862
4863 pBandwidthGroupOld->release();
4864 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4865 }
4866
4867 if (!group.isNull())
4868 {
4869 group->reference();
4870 pAttach->updateBandwidthGroup(group->getName());
4871 }
4872
4873 return S_OK;
4874}
4875
4876STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4877 LONG aControllerPort,
4878 LONG aDevice,
4879 DeviceType_T aType)
4880{
4881 HRESULT rc = S_OK;
4882
4883 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4884 aControllerName, aControllerPort, aDevice, aType));
4885
4886 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4887
4888 return rc;
4889}
4890
4891
4892
4893STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4894 LONG aControllerPort,
4895 LONG aDevice,
4896 BOOL aForce)
4897{
4898 int rc = S_OK;
4899 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4900 aControllerName, aControllerPort, aForce));
4901
4902 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4903
4904 return rc;
4905}
4906
4907STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4908 LONG aControllerPort,
4909 LONG aDevice,
4910 IMedium *aMedium,
4911 BOOL aForce)
4912{
4913 int rc = S_OK;
4914 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4915 aControllerName, aControllerPort, aDevice, aForce));
4916
4917 CheckComArgStrNotEmptyOrNull(aControllerName);
4918
4919 AutoCaller autoCaller(this);
4920 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4921
4922 // request the host lock first, since might be calling Host methods for getting host drives;
4923 // next, protect the media tree all the while we're in here, as well as our member variables
4924 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4925 this->lockHandle(),
4926 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4927
4928 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4929 aControllerName,
4930 aControllerPort,
4931 aDevice);
4932 if (pAttach.isNull())
4933 return setError(VBOX_E_OBJECT_NOT_FOUND,
4934 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4935 aDevice, aControllerPort, aControllerName);
4936
4937 /* Remember previously mounted medium. The medium before taking the
4938 * backup is not necessarily the same thing. */
4939 ComObjPtr<Medium> oldmedium;
4940 oldmedium = pAttach->getMedium();
4941
4942 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4943 if (aMedium && pMedium.isNull())
4944 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4945
4946 AutoCaller mediumCaller(pMedium);
4947 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4948
4949 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4950 if (pMedium)
4951 {
4952 DeviceType_T mediumType = pAttach->getType();
4953 switch (mediumType)
4954 {
4955 case DeviceType_DVD:
4956 case DeviceType_Floppy:
4957 break;
4958
4959 default:
4960 return setError(VBOX_E_INVALID_OBJECT_STATE,
4961 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4962 aControllerPort,
4963 aDevice,
4964 aControllerName);
4965 }
4966 }
4967
4968 setModified(IsModified_Storage);
4969 mMediaData.backup();
4970
4971 {
4972 // The backup operation makes the pAttach reference point to the
4973 // old settings. Re-get the correct reference.
4974 pAttach = findAttachment(mMediaData->mAttachments,
4975 aControllerName,
4976 aControllerPort,
4977 aDevice);
4978 if (!oldmedium.isNull())
4979 oldmedium->removeBackReference(mData->mUuid);
4980 if (!pMedium.isNull())
4981 {
4982 pMedium->addBackReference(mData->mUuid);
4983
4984 mediumLock.release();
4985 multiLock.release();
4986 addMediumToRegistry(pMedium);
4987 multiLock.acquire();
4988 mediumLock.acquire();
4989 }
4990
4991 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4992 pAttach->updateMedium(pMedium);
4993 }
4994
4995 setModified(IsModified_Storage);
4996
4997 mediumLock.release();
4998 multiLock.release();
4999 rc = onMediumChange(pAttach, aForce);
5000 multiLock.acquire();
5001 mediumLock.acquire();
5002
5003 /* On error roll back this change only. */
5004 if (FAILED(rc))
5005 {
5006 if (!pMedium.isNull())
5007 pMedium->removeBackReference(mData->mUuid);
5008 pAttach = findAttachment(mMediaData->mAttachments,
5009 aControllerName,
5010 aControllerPort,
5011 aDevice);
5012 /* If the attachment is gone in the meantime, bail out. */
5013 if (pAttach.isNull())
5014 return rc;
5015 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5016 if (!oldmedium.isNull())
5017 oldmedium->addBackReference(mData->mUuid);
5018 pAttach->updateMedium(oldmedium);
5019 }
5020
5021 mediumLock.release();
5022 multiLock.release();
5023
5024 mParent->saveModifiedRegistries();
5025
5026 return rc;
5027}
5028
5029STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5030 LONG aControllerPort,
5031 LONG aDevice,
5032 IMedium **aMedium)
5033{
5034 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5035 aControllerName, aControllerPort, aDevice));
5036
5037 CheckComArgStrNotEmptyOrNull(aControllerName);
5038 CheckComArgOutPointerValid(aMedium);
5039
5040 AutoCaller autoCaller(this);
5041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5042
5043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5044
5045 *aMedium = NULL;
5046
5047 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5048 aControllerName,
5049 aControllerPort,
5050 aDevice);
5051 if (pAttach.isNull())
5052 return setError(VBOX_E_OBJECT_NOT_FOUND,
5053 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5054 aDevice, aControllerPort, aControllerName);
5055
5056 pAttach->getMedium().queryInterfaceTo(aMedium);
5057
5058 return S_OK;
5059}
5060
5061STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5062{
5063 CheckComArgOutPointerValid(port);
5064 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5065
5066 AutoCaller autoCaller(this);
5067 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5068
5069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5070
5071 mSerialPorts[slot].queryInterfaceTo(port);
5072
5073 return S_OK;
5074}
5075
5076STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5077{
5078 CheckComArgOutPointerValid(port);
5079 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5080
5081 AutoCaller autoCaller(this);
5082 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5083
5084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5085
5086 mParallelPorts[slot].queryInterfaceTo(port);
5087
5088 return S_OK;
5089}
5090
5091STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5092{
5093 CheckComArgOutPointerValid(adapter);
5094 /* Do not assert if slot is out of range, just return the advertised
5095 status. testdriver/vbox.py triggers this in logVmInfo. */
5096 if (slot >= mNetworkAdapters.size())
5097 return setError(E_INVALIDARG,
5098 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5099 slot, mNetworkAdapters.size());
5100
5101 AutoCaller autoCaller(this);
5102 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5103
5104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5105
5106 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5107
5108 return S_OK;
5109}
5110
5111STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5112{
5113 CheckComArgOutSafeArrayPointerValid(aKeys);
5114
5115 AutoCaller autoCaller(this);
5116 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5117
5118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5119
5120 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5121 int i = 0;
5122 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5123 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5124 ++it, ++i)
5125 {
5126 const Utf8Str &strKey = it->first;
5127 strKey.cloneTo(&saKeys[i]);
5128 }
5129 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5130
5131 return S_OK;
5132 }
5133
5134 /**
5135 * @note Locks this object for reading.
5136 */
5137STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5138 BSTR *aValue)
5139{
5140 CheckComArgStrNotEmptyOrNull(aKey);
5141 CheckComArgOutPointerValid(aValue);
5142
5143 AutoCaller autoCaller(this);
5144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5145
5146 /* start with nothing found */
5147 Bstr bstrResult("");
5148
5149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5150
5151 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5152 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5153 // found:
5154 bstrResult = it->second; // source is a Utf8Str
5155
5156 /* return the result to caller (may be empty) */
5157 bstrResult.cloneTo(aValue);
5158
5159 return S_OK;
5160}
5161
5162 /**
5163 * @note Locks mParent for writing + this object for writing.
5164 */
5165STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5166{
5167 CheckComArgStrNotEmptyOrNull(aKey);
5168
5169 AutoCaller autoCaller(this);
5170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5171
5172 Utf8Str strKey(aKey);
5173 Utf8Str strValue(aValue);
5174 Utf8Str strOldValue; // empty
5175
5176 // locking note: we only hold the read lock briefly to look up the old value,
5177 // then release it and call the onExtraCanChange callbacks. There is a small
5178 // chance of a race insofar as the callback might be called twice if two callers
5179 // change the same key at the same time, but that's a much better solution
5180 // than the deadlock we had here before. The actual changing of the extradata
5181 // is then performed under the write lock and race-free.
5182
5183 // look up the old value first; if nothing has changed then we need not do anything
5184 {
5185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5186 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5187 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5188 strOldValue = it->second;
5189 }
5190
5191 bool fChanged;
5192 if ((fChanged = (strOldValue != strValue)))
5193 {
5194 // ask for permission from all listeners outside the locks;
5195 // onExtraDataCanChange() only briefly requests the VirtualBox
5196 // lock to copy the list of callbacks to invoke
5197 Bstr error;
5198 Bstr bstrValue(aValue);
5199
5200 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5201 {
5202 const char *sep = error.isEmpty() ? "" : ": ";
5203 CBSTR err = error.raw();
5204 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5205 sep, err));
5206 return setError(E_ACCESSDENIED,
5207 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5208 aKey,
5209 bstrValue.raw(),
5210 sep,
5211 err);
5212 }
5213
5214 // data is changing and change not vetoed: then write it out under the lock
5215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5216
5217 if (isSnapshotMachine())
5218 {
5219 HRESULT rc = checkStateDependency(MutableStateDep);
5220 if (FAILED(rc)) return rc;
5221 }
5222
5223 if (strValue.isEmpty())
5224 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5225 else
5226 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5227 // creates a new key if needed
5228
5229 bool fNeedsGlobalSaveSettings = false;
5230 saveSettings(&fNeedsGlobalSaveSettings);
5231
5232 if (fNeedsGlobalSaveSettings)
5233 {
5234 // save the global settings; for that we should hold only the VirtualBox lock
5235 alock.release();
5236 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5237 mParent->saveSettings();
5238 }
5239 }
5240
5241 // fire notification outside the lock
5242 if (fChanged)
5243 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5244
5245 return S_OK;
5246}
5247
5248STDMETHODIMP Machine::SaveSettings()
5249{
5250 AutoCaller autoCaller(this);
5251 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5252
5253 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5254
5255 /* when there was auto-conversion, we want to save the file even if
5256 * the VM is saved */
5257 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5258 if (FAILED(rc)) return rc;
5259
5260 /* the settings file path may never be null */
5261 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5262
5263 /* save all VM data excluding snapshots */
5264 bool fNeedsGlobalSaveSettings = false;
5265 rc = saveSettings(&fNeedsGlobalSaveSettings);
5266 mlock.release();
5267
5268 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5269 {
5270 // save the global settings; for that we should hold only the VirtualBox lock
5271 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5272 rc = mParent->saveSettings();
5273 }
5274
5275 return rc;
5276}
5277
5278STDMETHODIMP Machine::DiscardSettings()
5279{
5280 AutoCaller autoCaller(this);
5281 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5282
5283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5284
5285 HRESULT rc = checkStateDependency(MutableStateDep);
5286 if (FAILED(rc)) return rc;
5287
5288 /*
5289 * during this rollback, the session will be notified if data has
5290 * been actually changed
5291 */
5292 rollback(true /* aNotify */);
5293
5294 return S_OK;
5295}
5296
5297/** @note Locks objects! */
5298STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5299 ComSafeArrayOut(IMedium*, aMedia))
5300{
5301 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5302 AutoLimitedCaller autoCaller(this);
5303 AssertComRCReturnRC(autoCaller.rc());
5304
5305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5306
5307 Guid id(getId());
5308
5309 if (mData->mSession.mState != SessionState_Unlocked)
5310 return setError(VBOX_E_INVALID_OBJECT_STATE,
5311 tr("Cannot unregister the machine '%s' while it is locked"),
5312 mUserData->s.strName.c_str());
5313
5314 // wait for state dependents to drop to zero
5315 ensureNoStateDependencies();
5316
5317 if (!mData->mAccessible)
5318 {
5319 // inaccessible maschines can only be unregistered; uninitialize ourselves
5320 // here because currently there may be no unregistered that are inaccessible
5321 // (this state combination is not supported). Note releasing the caller and
5322 // leaving the lock before calling uninit()
5323 alock.release();
5324 autoCaller.release();
5325
5326 uninit();
5327
5328 mParent->unregisterMachine(this, id);
5329 // calls VirtualBox::saveSettings()
5330
5331 return S_OK;
5332 }
5333
5334 HRESULT rc = S_OK;
5335
5336 // discard saved state
5337 if (mData->mMachineState == MachineState_Saved)
5338 {
5339 // add the saved state file to the list of files the caller should delete
5340 Assert(!mSSData->strStateFilePath.isEmpty());
5341 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5342
5343 mSSData->strStateFilePath.setNull();
5344
5345 // unconditionally set the machine state to powered off, we now
5346 // know no session has locked the machine
5347 mData->mMachineState = MachineState_PoweredOff;
5348 }
5349
5350 size_t cSnapshots = 0;
5351 if (mData->mFirstSnapshot)
5352 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5353 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5354 // fail now before we start detaching media
5355 return setError(VBOX_E_INVALID_OBJECT_STATE,
5356 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5357 mUserData->s.strName.c_str(), cSnapshots);
5358
5359 // This list collects the medium objects from all medium attachments
5360 // which we will detach from the machine and its snapshots, in a specific
5361 // order which allows for closing all media without getting "media in use"
5362 // errors, simply by going through the list from the front to the back:
5363 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5364 // and must be closed before the parent media from the snapshots, or closing the parents
5365 // will fail because they still have children);
5366 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5367 // the root ("first") snapshot of the machine.
5368 MediaList llMedia;
5369
5370 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5371 && mMediaData->mAttachments.size()
5372 )
5373 {
5374 // we have media attachments: detach them all and add the Medium objects to our list
5375 if (cleanupMode != CleanupMode_UnregisterOnly)
5376 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5377 else
5378 return setError(VBOX_E_INVALID_OBJECT_STATE,
5379 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5380 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5381 }
5382
5383 if (cSnapshots)
5384 {
5385 // autoCleanup must be true here, or we would have failed above
5386
5387 // add the media from the medium attachments of the snapshots to llMedia
5388 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5389 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5390 // into the children first
5391
5392 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5393 MachineState_T oldState = mData->mMachineState;
5394 mData->mMachineState = MachineState_DeletingSnapshot;
5395
5396 // make a copy of the first snapshot so the refcount does not drop to 0
5397 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5398 // because of the AutoCaller voodoo)
5399 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5400
5401 // GO!
5402 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5403
5404 mData->mMachineState = oldState;
5405 }
5406
5407 if (FAILED(rc))
5408 {
5409 rollbackMedia();
5410 return rc;
5411 }
5412
5413 // commit all the media changes made above
5414 commitMedia();
5415
5416 mData->mRegistered = false;
5417
5418 // machine lock no longer needed
5419 alock.release();
5420
5421 // return media to caller
5422 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5423 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5424
5425 mParent->unregisterMachine(this, id);
5426 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5427
5428 return S_OK;
5429}
5430
5431struct Machine::DeleteTask
5432{
5433 ComObjPtr<Machine> pMachine;
5434 RTCList<ComPtr<IMedium> > llMediums;
5435 StringsList llFilesToDelete;
5436 ComObjPtr<Progress> pProgress;
5437};
5438
5439STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5440{
5441 LogFlowFuncEnter();
5442
5443 AutoCaller autoCaller(this);
5444 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5445
5446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5447
5448 HRESULT rc = checkStateDependency(MutableStateDep);
5449 if (FAILED(rc)) return rc;
5450
5451 if (mData->mRegistered)
5452 return setError(VBOX_E_INVALID_VM_STATE,
5453 tr("Cannot delete settings of a registered machine"));
5454
5455 DeleteTask *pTask = new DeleteTask;
5456 pTask->pMachine = this;
5457 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5458
5459 // collect files to delete
5460 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5461
5462 for (size_t i = 0; i < sfaMedia.size(); ++i)
5463 {
5464 IMedium *pIMedium(sfaMedia[i]);
5465 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5466 if (pMedium.isNull())
5467 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5468 SafeArray<BSTR> ids;
5469 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5470 if (FAILED(rc)) return rc;
5471 /* At this point the medium should not have any back references
5472 * anymore. If it has it is attached to another VM and *must* not
5473 * deleted. */
5474 if (ids.size() < 1)
5475 pTask->llMediums.append(pMedium);
5476 }
5477 if (mData->pMachineConfigFile->fileExists())
5478 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5479
5480 pTask->pProgress.createObject();
5481 pTask->pProgress->init(getVirtualBox(),
5482 static_cast<IMachine*>(this) /* aInitiator */,
5483 Bstr(tr("Deleting files")).raw(),
5484 true /* fCancellable */,
5485 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5486 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5487
5488 int vrc = RTThreadCreate(NULL,
5489 Machine::deleteThread,
5490 (void*)pTask,
5491 0,
5492 RTTHREADTYPE_MAIN_WORKER,
5493 0,
5494 "MachineDelete");
5495
5496 pTask->pProgress.queryInterfaceTo(aProgress);
5497
5498 if (RT_FAILURE(vrc))
5499 {
5500 delete pTask;
5501 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5502 }
5503
5504 LogFlowFuncLeave();
5505
5506 return S_OK;
5507}
5508
5509/**
5510 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5511 * calls Machine::deleteTaskWorker() on the actual machine object.
5512 * @param Thread
5513 * @param pvUser
5514 * @return
5515 */
5516/*static*/
5517DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5518{
5519 LogFlowFuncEnter();
5520
5521 DeleteTask *pTask = (DeleteTask*)pvUser;
5522 Assert(pTask);
5523 Assert(pTask->pMachine);
5524 Assert(pTask->pProgress);
5525
5526 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5527 pTask->pProgress->notifyComplete(rc);
5528
5529 delete pTask;
5530
5531 LogFlowFuncLeave();
5532
5533 NOREF(Thread);
5534
5535 return VINF_SUCCESS;
5536}
5537
5538/**
5539 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5540 * @param task
5541 * @return
5542 */
5543HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5544{
5545 AutoCaller autoCaller(this);
5546 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5547
5548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5549
5550 HRESULT rc = S_OK;
5551
5552 try
5553 {
5554 ULONG uLogHistoryCount = 3;
5555 ComPtr<ISystemProperties> systemProperties;
5556 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5557 if (FAILED(rc)) throw rc;
5558
5559 if (!systemProperties.isNull())
5560 {
5561 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5562 if (FAILED(rc)) throw rc;
5563 }
5564
5565 MachineState_T oldState = mData->mMachineState;
5566 setMachineState(MachineState_SettingUp);
5567 alock.release();
5568 for (size_t i = 0; i < task.llMediums.size(); ++i)
5569 {
5570 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5571 {
5572 AutoCaller mac(pMedium);
5573 if (FAILED(mac.rc())) throw mac.rc();
5574 Utf8Str strLocation = pMedium->getLocationFull();
5575 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5576 if (FAILED(rc)) throw rc;
5577 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5578 }
5579 ComPtr<IProgress> pProgress2;
5580 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5581 if (FAILED(rc)) throw rc;
5582 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5583 if (FAILED(rc)) throw rc;
5584 /* Check the result of the asynchrony process. */
5585 LONG iRc;
5586 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5587 if (FAILED(rc)) throw rc;
5588 /* If the thread of the progress object has an error, then
5589 * retrieve the error info from there, or it'll be lost. */
5590 if (FAILED(iRc))
5591 throw setError(ProgressErrorInfo(pProgress2));
5592 }
5593 setMachineState(oldState);
5594 alock.acquire();
5595
5596 // delete the files pushed on the task list by Machine::Delete()
5597 // (this includes saved states of the machine and snapshots and
5598 // medium storage files from the IMedium list passed in, and the
5599 // machine XML file)
5600 StringsList::const_iterator it = task.llFilesToDelete.begin();
5601 while (it != task.llFilesToDelete.end())
5602 {
5603 const Utf8Str &strFile = *it;
5604 LogFunc(("Deleting file %s\n", strFile.c_str()));
5605 int vrc = RTFileDelete(strFile.c_str());
5606 if (RT_FAILURE(vrc))
5607 throw setError(VBOX_E_IPRT_ERROR,
5608 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5609
5610 ++it;
5611 if (it == task.llFilesToDelete.end())
5612 {
5613 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5614 if (FAILED(rc)) throw rc;
5615 break;
5616 }
5617
5618 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5619 if (FAILED(rc)) throw rc;
5620 }
5621
5622 /* delete the settings only when the file actually exists */
5623 if (mData->pMachineConfigFile->fileExists())
5624 {
5625 /* Delete any backup or uncommitted XML files. Ignore failures.
5626 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5627 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5628 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5629 RTFileDelete(otherXml.c_str());
5630 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5631 RTFileDelete(otherXml.c_str());
5632
5633 /* delete the Logs folder, nothing important should be left
5634 * there (we don't check for errors because the user might have
5635 * some private files there that we don't want to delete) */
5636 Utf8Str logFolder;
5637 getLogFolder(logFolder);
5638 Assert(logFolder.length());
5639 if (RTDirExists(logFolder.c_str()))
5640 {
5641 /* Delete all VBox.log[.N] files from the Logs folder
5642 * (this must be in sync with the rotation logic in
5643 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5644 * files that may have been created by the GUI. */
5645 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5646 logFolder.c_str(), RTPATH_DELIMITER);
5647 RTFileDelete(log.c_str());
5648 log = Utf8StrFmt("%s%cVBox.png",
5649 logFolder.c_str(), RTPATH_DELIMITER);
5650 RTFileDelete(log.c_str());
5651 for (int i = uLogHistoryCount; i > 0; i--)
5652 {
5653 log = Utf8StrFmt("%s%cVBox.log.%d",
5654 logFolder.c_str(), RTPATH_DELIMITER, i);
5655 RTFileDelete(log.c_str());
5656 log = Utf8StrFmt("%s%cVBox.png.%d",
5657 logFolder.c_str(), RTPATH_DELIMITER, i);
5658 RTFileDelete(log.c_str());
5659 }
5660
5661 RTDirRemove(logFolder.c_str());
5662 }
5663
5664 /* delete the Snapshots folder, nothing important should be left
5665 * there (we don't check for errors because the user might have
5666 * some private files there that we don't want to delete) */
5667 Utf8Str strFullSnapshotFolder;
5668 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5669 Assert(!strFullSnapshotFolder.isEmpty());
5670 if (RTDirExists(strFullSnapshotFolder.c_str()))
5671 RTDirRemove(strFullSnapshotFolder.c_str());
5672
5673 // delete the directory that contains the settings file, but only
5674 // if it matches the VM name
5675 Utf8Str settingsDir;
5676 if (isInOwnDir(&settingsDir))
5677 RTDirRemove(settingsDir.c_str());
5678 }
5679
5680 alock.release();
5681
5682 mParent->saveModifiedRegistries();
5683 }
5684 catch (HRESULT aRC) { rc = aRC; }
5685
5686 return rc;
5687}
5688
5689STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5690{
5691 CheckComArgOutPointerValid(aSnapshot);
5692
5693 AutoCaller autoCaller(this);
5694 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5695
5696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5697
5698 ComObjPtr<Snapshot> pSnapshot;
5699 HRESULT rc;
5700
5701 if (!aNameOrId || !*aNameOrId)
5702 // null case (caller wants root snapshot): findSnapshotById() handles this
5703 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5704 else
5705 {
5706 Guid uuid(aNameOrId);
5707 if (uuid.isValid())
5708 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5709 else
5710 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5711 }
5712 pSnapshot.queryInterfaceTo(aSnapshot);
5713
5714 return rc;
5715}
5716
5717STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5718{
5719 CheckComArgStrNotEmptyOrNull(aName);
5720 CheckComArgStrNotEmptyOrNull(aHostPath);
5721
5722 AutoCaller autoCaller(this);
5723 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5724
5725 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5726
5727 HRESULT rc = checkStateDependency(MutableStateDep);
5728 if (FAILED(rc)) return rc;
5729
5730 Utf8Str strName(aName);
5731
5732 ComObjPtr<SharedFolder> sharedFolder;
5733 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5734 if (SUCCEEDED(rc))
5735 return setError(VBOX_E_OBJECT_IN_USE,
5736 tr("Shared folder named '%s' already exists"),
5737 strName.c_str());
5738
5739 sharedFolder.createObject();
5740 rc = sharedFolder->init(getMachine(),
5741 strName,
5742 aHostPath,
5743 !!aWritable,
5744 !!aAutoMount,
5745 true /* fFailOnError */);
5746 if (FAILED(rc)) return rc;
5747
5748 setModified(IsModified_SharedFolders);
5749 mHWData.backup();
5750 mHWData->mSharedFolders.push_back(sharedFolder);
5751
5752 /* inform the direct session if any */
5753 alock.release();
5754 onSharedFolderChange();
5755
5756 return S_OK;
5757}
5758
5759STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5760{
5761 CheckComArgStrNotEmptyOrNull(aName);
5762
5763 AutoCaller autoCaller(this);
5764 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5765
5766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5767
5768 HRESULT rc = checkStateDependency(MutableStateDep);
5769 if (FAILED(rc)) return rc;
5770
5771 ComObjPtr<SharedFolder> sharedFolder;
5772 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5773 if (FAILED(rc)) return rc;
5774
5775 setModified(IsModified_SharedFolders);
5776 mHWData.backup();
5777 mHWData->mSharedFolders.remove(sharedFolder);
5778
5779 /* inform the direct session if any */
5780 alock.release();
5781 onSharedFolderChange();
5782
5783 return S_OK;
5784}
5785
5786STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5787{
5788 CheckComArgOutPointerValid(aCanShow);
5789
5790 /* start with No */
5791 *aCanShow = FALSE;
5792
5793 AutoCaller autoCaller(this);
5794 AssertComRCReturnRC(autoCaller.rc());
5795
5796 ComPtr<IInternalSessionControl> directControl;
5797 {
5798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5799
5800 if (mData->mSession.mState != SessionState_Locked)
5801 return setError(VBOX_E_INVALID_VM_STATE,
5802 tr("Machine is not locked for session (session state: %s)"),
5803 Global::stringifySessionState(mData->mSession.mState));
5804
5805 directControl = mData->mSession.mDirectControl;
5806 }
5807
5808 /* ignore calls made after #OnSessionEnd() is called */
5809 if (!directControl)
5810 return S_OK;
5811
5812 LONG64 dummy;
5813 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5814}
5815
5816STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5817{
5818 CheckComArgOutPointerValid(aWinId);
5819
5820 AutoCaller autoCaller(this);
5821 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5822
5823 ComPtr<IInternalSessionControl> directControl;
5824 {
5825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5826
5827 if (mData->mSession.mState != SessionState_Locked)
5828 return setError(E_FAIL,
5829 tr("Machine is not locked for session (session state: %s)"),
5830 Global::stringifySessionState(mData->mSession.mState));
5831
5832 directControl = mData->mSession.mDirectControl;
5833 }
5834
5835 /* ignore calls made after #OnSessionEnd() is called */
5836 if (!directControl)
5837 return S_OK;
5838
5839 BOOL dummy;
5840 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5841}
5842
5843#ifdef VBOX_WITH_GUEST_PROPS
5844/**
5845 * Look up a guest property in VBoxSVC's internal structures.
5846 */
5847HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5848 BSTR *aValue,
5849 LONG64 *aTimestamp,
5850 BSTR *aFlags) const
5851{
5852 using namespace guestProp;
5853
5854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5855 Utf8Str strName(aName);
5856 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5857
5858 if (it != mHWData->mGuestProperties.end())
5859 {
5860 char szFlags[MAX_FLAGS_LEN + 1];
5861 it->second.strValue.cloneTo(aValue);
5862 *aTimestamp = it->second.mTimestamp;
5863 writeFlags(it->second.mFlags, szFlags);
5864 Bstr(szFlags).cloneTo(aFlags);
5865 }
5866
5867 return S_OK;
5868}
5869
5870/**
5871 * Query the VM that a guest property belongs to for the property.
5872 * @returns E_ACCESSDENIED if the VM process is not available or not
5873 * currently handling queries and the lookup should then be done in
5874 * VBoxSVC.
5875 */
5876HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5877 BSTR *aValue,
5878 LONG64 *aTimestamp,
5879 BSTR *aFlags) const
5880{
5881 HRESULT rc;
5882 ComPtr<IInternalSessionControl> directControl;
5883 directControl = mData->mSession.mDirectControl;
5884
5885 /* fail if we were called after #OnSessionEnd() is called. This is a
5886 * silly race condition. */
5887
5888 if (!directControl)
5889 rc = E_ACCESSDENIED;
5890 else
5891 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5892 false /* isSetter */,
5893 aValue, aTimestamp, aFlags);
5894 return rc;
5895}
5896#endif // VBOX_WITH_GUEST_PROPS
5897
5898STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5899 BSTR *aValue,
5900 LONG64 *aTimestamp,
5901 BSTR *aFlags)
5902{
5903#ifndef VBOX_WITH_GUEST_PROPS
5904 ReturnComNotImplemented();
5905#else // VBOX_WITH_GUEST_PROPS
5906 CheckComArgStrNotEmptyOrNull(aName);
5907 CheckComArgOutPointerValid(aValue);
5908 CheckComArgOutPointerValid(aTimestamp);
5909 CheckComArgOutPointerValid(aFlags);
5910
5911 AutoCaller autoCaller(this);
5912 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5913
5914 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5915 if (rc == E_ACCESSDENIED)
5916 /* The VM is not running or the service is not (yet) accessible */
5917 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5918 return rc;
5919#endif // VBOX_WITH_GUEST_PROPS
5920}
5921
5922STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5923{
5924 LONG64 dummyTimestamp;
5925 Bstr dummyFlags;
5926 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5927}
5928
5929STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5930{
5931 Bstr dummyValue;
5932 Bstr dummyFlags;
5933 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5934}
5935
5936#ifdef VBOX_WITH_GUEST_PROPS
5937/**
5938 * Set a guest property in VBoxSVC's internal structures.
5939 */
5940HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5941 IN_BSTR aFlags)
5942{
5943 using namespace guestProp;
5944
5945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5946 HRESULT rc = S_OK;
5947
5948 rc = checkStateDependency(MutableStateDep);
5949 if (FAILED(rc)) return rc;
5950
5951 try
5952 {
5953 Utf8Str utf8Name(aName);
5954 Utf8Str utf8Flags(aFlags);
5955 uint32_t fFlags = NILFLAG;
5956 if ( aFlags != NULL
5957 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
5958 return setError(E_INVALIDARG,
5959 tr("Invalid guest property flag values: '%ls'"),
5960 aFlags);
5961
5962 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
5963 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
5964 if (it == mHWData->mGuestProperties.end())
5965 {
5966 if (!fDelete)
5967 {
5968 setModified(IsModified_MachineData);
5969 mHWData.backupEx();
5970
5971 RTTIMESPEC time;
5972 HWData::GuestProperty prop;
5973 prop.strValue = aValue;
5974 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5975 prop.mFlags = fFlags;
5976 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5977 }
5978 }
5979 else
5980 {
5981 if (it->second.mFlags & (RDONLYHOST))
5982 {
5983 rc = setError(E_ACCESSDENIED,
5984 tr("The property '%ls' cannot be changed by the host"),
5985 aName);
5986 }
5987 else
5988 {
5989 setModified(IsModified_MachineData);
5990 mHWData.backupEx();
5991
5992 /* The backupEx() operation invalidates our iterator,
5993 * so get a new one. */
5994 it = mHWData->mGuestProperties.find(utf8Name);
5995 Assert(it != mHWData->mGuestProperties.end());
5996
5997 if (!fDelete)
5998 {
5999 RTTIMESPEC time;
6000 it->second.strValue = aValue;
6001 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6002 it->second.mFlags = fFlags;
6003 }
6004 else
6005 mHWData->mGuestProperties.erase(it);
6006 }
6007 }
6008
6009 if ( SUCCEEDED(rc)
6010 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6011 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6012 RTSTR_MAX,
6013 utf8Name.c_str(),
6014 RTSTR_MAX,
6015 NULL)
6016 )
6017 )
6018 {
6019 alock.release();
6020
6021 mParent->onGuestPropertyChange(mData->mUuid, aName,
6022 aValue ? aValue : Bstr("").raw(),
6023 aFlags ? aFlags : Bstr("").raw());
6024 }
6025 }
6026 catch (std::bad_alloc &)
6027 {
6028 rc = E_OUTOFMEMORY;
6029 }
6030
6031 return rc;
6032}
6033
6034/**
6035 * Set a property on the VM that that property belongs to.
6036 * @returns E_ACCESSDENIED if the VM process is not available or not
6037 * currently handling queries and the setting should then be done in
6038 * VBoxSVC.
6039 */
6040HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6041 IN_BSTR aFlags)
6042{
6043 HRESULT rc;
6044
6045 try
6046 {
6047 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6048
6049 BSTR dummy = NULL; /* will not be changed (setter) */
6050 LONG64 dummy64;
6051 if (!directControl)
6052 rc = E_ACCESSDENIED;
6053 else
6054 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6055 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6056 true /* isSetter */,
6057 &dummy, &dummy64, &dummy);
6058 }
6059 catch (std::bad_alloc &)
6060 {
6061 rc = E_OUTOFMEMORY;
6062 }
6063
6064 return rc;
6065}
6066#endif // VBOX_WITH_GUEST_PROPS
6067
6068STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6069 IN_BSTR aFlags)
6070{
6071#ifndef VBOX_WITH_GUEST_PROPS
6072 ReturnComNotImplemented();
6073#else // VBOX_WITH_GUEST_PROPS
6074 CheckComArgStrNotEmptyOrNull(aName);
6075 CheckComArgMaybeNull(aFlags);
6076 CheckComArgMaybeNull(aValue);
6077
6078 AutoCaller autoCaller(this);
6079 if (FAILED(autoCaller.rc()))
6080 return autoCaller.rc();
6081
6082 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6083 if (rc == E_ACCESSDENIED)
6084 /* The VM is not running or the service is not (yet) accessible */
6085 rc = setGuestPropertyToService(aName, aValue, aFlags);
6086 return rc;
6087#endif // VBOX_WITH_GUEST_PROPS
6088}
6089
6090STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6091{
6092 return SetGuestProperty(aName, aValue, NULL);
6093}
6094
6095STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6096{
6097 return SetGuestProperty(aName, NULL, NULL);
6098}
6099
6100#ifdef VBOX_WITH_GUEST_PROPS
6101/**
6102 * Enumerate the guest properties in VBoxSVC's internal structures.
6103 */
6104HRESULT Machine::enumerateGuestPropertiesInService
6105 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6106 ComSafeArrayOut(BSTR, aValues),
6107 ComSafeArrayOut(LONG64, aTimestamps),
6108 ComSafeArrayOut(BSTR, aFlags))
6109{
6110 using namespace guestProp;
6111
6112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6113 Utf8Str strPatterns(aPatterns);
6114
6115 HWData::GuestPropertyMap propMap;
6116
6117 /*
6118 * Look for matching patterns and build up a list.
6119 */
6120 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6121 while (it != mHWData->mGuestProperties.end())
6122 {
6123 if ( strPatterns.isEmpty()
6124 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6125 RTSTR_MAX,
6126 it->first.c_str(),
6127 RTSTR_MAX,
6128 NULL)
6129 )
6130 {
6131 propMap.insert(*it);
6132 }
6133
6134 it++;
6135 }
6136
6137 alock.release();
6138
6139 /*
6140 * And build up the arrays for returning the property information.
6141 */
6142 size_t cEntries = propMap.size();
6143 SafeArray<BSTR> names(cEntries);
6144 SafeArray<BSTR> values(cEntries);
6145 SafeArray<LONG64> timestamps(cEntries);
6146 SafeArray<BSTR> flags(cEntries);
6147 size_t iProp = 0;
6148
6149 it = propMap.begin();
6150 while (it != propMap.end())
6151 {
6152 char szFlags[MAX_FLAGS_LEN + 1];
6153 it->first.cloneTo(&names[iProp]);
6154 it->second.strValue.cloneTo(&values[iProp]);
6155 timestamps[iProp] = it->second.mTimestamp;
6156 writeFlags(it->second.mFlags, szFlags);
6157 Bstr(szFlags).cloneTo(&flags[iProp++]);
6158 it++;
6159 }
6160 names.detachTo(ComSafeArrayOutArg(aNames));
6161 values.detachTo(ComSafeArrayOutArg(aValues));
6162 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6163 flags.detachTo(ComSafeArrayOutArg(aFlags));
6164 return S_OK;
6165}
6166
6167/**
6168 * Enumerate the properties managed by a VM.
6169 * @returns E_ACCESSDENIED if the VM process is not available or not
6170 * currently handling queries and the setting should then be done in
6171 * VBoxSVC.
6172 */
6173HRESULT Machine::enumerateGuestPropertiesOnVM
6174 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6175 ComSafeArrayOut(BSTR, aValues),
6176 ComSafeArrayOut(LONG64, aTimestamps),
6177 ComSafeArrayOut(BSTR, aFlags))
6178{
6179 HRESULT rc;
6180 ComPtr<IInternalSessionControl> directControl;
6181 directControl = mData->mSession.mDirectControl;
6182
6183 if (!directControl)
6184 rc = E_ACCESSDENIED;
6185 else
6186 rc = directControl->EnumerateGuestProperties
6187 (aPatterns, ComSafeArrayOutArg(aNames),
6188 ComSafeArrayOutArg(aValues),
6189 ComSafeArrayOutArg(aTimestamps),
6190 ComSafeArrayOutArg(aFlags));
6191 return rc;
6192}
6193#endif // VBOX_WITH_GUEST_PROPS
6194
6195STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6196 ComSafeArrayOut(BSTR, aNames),
6197 ComSafeArrayOut(BSTR, aValues),
6198 ComSafeArrayOut(LONG64, aTimestamps),
6199 ComSafeArrayOut(BSTR, aFlags))
6200{
6201#ifndef VBOX_WITH_GUEST_PROPS
6202 ReturnComNotImplemented();
6203#else // VBOX_WITH_GUEST_PROPS
6204 CheckComArgMaybeNull(aPatterns);
6205 CheckComArgOutSafeArrayPointerValid(aNames);
6206 CheckComArgOutSafeArrayPointerValid(aValues);
6207 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6208 CheckComArgOutSafeArrayPointerValid(aFlags);
6209
6210 AutoCaller autoCaller(this);
6211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6212
6213 HRESULT rc = enumerateGuestPropertiesOnVM
6214 (aPatterns, ComSafeArrayOutArg(aNames),
6215 ComSafeArrayOutArg(aValues),
6216 ComSafeArrayOutArg(aTimestamps),
6217 ComSafeArrayOutArg(aFlags));
6218 if (rc == E_ACCESSDENIED)
6219 /* The VM is not running or the service is not (yet) accessible */
6220 rc = enumerateGuestPropertiesInService
6221 (aPatterns, ComSafeArrayOutArg(aNames),
6222 ComSafeArrayOutArg(aValues),
6223 ComSafeArrayOutArg(aTimestamps),
6224 ComSafeArrayOutArg(aFlags));
6225 return rc;
6226#endif // VBOX_WITH_GUEST_PROPS
6227}
6228
6229STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6230 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6231{
6232 MediaData::AttachmentList atts;
6233
6234 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6235 if (FAILED(rc)) return rc;
6236
6237 SafeIfaceArray<IMediumAttachment> attachments(atts);
6238 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6239
6240 return S_OK;
6241}
6242
6243STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6244 LONG aControllerPort,
6245 LONG aDevice,
6246 IMediumAttachment **aAttachment)
6247{
6248 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6249 aControllerName, aControllerPort, aDevice));
6250
6251 CheckComArgStrNotEmptyOrNull(aControllerName);
6252 CheckComArgOutPointerValid(aAttachment);
6253
6254 AutoCaller autoCaller(this);
6255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6256
6257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6258
6259 *aAttachment = NULL;
6260
6261 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6262 aControllerName,
6263 aControllerPort,
6264 aDevice);
6265 if (pAttach.isNull())
6266 return setError(VBOX_E_OBJECT_NOT_FOUND,
6267 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6268 aDevice, aControllerPort, aControllerName);
6269
6270 pAttach.queryInterfaceTo(aAttachment);
6271
6272 return S_OK;
6273}
6274
6275STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6276 StorageBus_T aConnectionType,
6277 IStorageController **controller)
6278{
6279 CheckComArgStrNotEmptyOrNull(aName);
6280
6281 if ( (aConnectionType <= StorageBus_Null)
6282 || (aConnectionType > StorageBus_SAS))
6283 return setError(E_INVALIDARG,
6284 tr("Invalid connection type: %d"),
6285 aConnectionType);
6286
6287 AutoCaller autoCaller(this);
6288 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6289
6290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6291
6292 HRESULT rc = checkStateDependency(MutableStateDep);
6293 if (FAILED(rc)) return rc;
6294
6295 /* try to find one with the name first. */
6296 ComObjPtr<StorageController> ctrl;
6297
6298 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6299 if (SUCCEEDED(rc))
6300 return setError(VBOX_E_OBJECT_IN_USE,
6301 tr("Storage controller named '%ls' already exists"),
6302 aName);
6303
6304 ctrl.createObject();
6305
6306 /* get a new instance number for the storage controller */
6307 ULONG ulInstance = 0;
6308 bool fBootable = true;
6309 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6310 it != mStorageControllers->end();
6311 ++it)
6312 {
6313 if ((*it)->getStorageBus() == aConnectionType)
6314 {
6315 ULONG ulCurInst = (*it)->getInstance();
6316
6317 if (ulCurInst >= ulInstance)
6318 ulInstance = ulCurInst + 1;
6319
6320 /* Only one controller of each type can be marked as bootable. */
6321 if ((*it)->getBootable())
6322 fBootable = false;
6323 }
6324 }
6325
6326 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6327 if (FAILED(rc)) return rc;
6328
6329 setModified(IsModified_Storage);
6330 mStorageControllers.backup();
6331 mStorageControllers->push_back(ctrl);
6332
6333 ctrl.queryInterfaceTo(controller);
6334
6335 /* inform the direct session if any */
6336 alock.release();
6337 onStorageControllerChange();
6338
6339 return S_OK;
6340}
6341
6342STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6343 IStorageController **aStorageController)
6344{
6345 CheckComArgStrNotEmptyOrNull(aName);
6346
6347 AutoCaller autoCaller(this);
6348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6349
6350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6351
6352 ComObjPtr<StorageController> ctrl;
6353
6354 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6355 if (SUCCEEDED(rc))
6356 ctrl.queryInterfaceTo(aStorageController);
6357
6358 return rc;
6359}
6360
6361STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6362 IStorageController **aStorageController)
6363{
6364 AutoCaller autoCaller(this);
6365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6366
6367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6368
6369 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6370 it != mStorageControllers->end();
6371 ++it)
6372 {
6373 if ((*it)->getInstance() == aInstance)
6374 {
6375 (*it).queryInterfaceTo(aStorageController);
6376 return S_OK;
6377 }
6378 }
6379
6380 return setError(VBOX_E_OBJECT_NOT_FOUND,
6381 tr("Could not find a storage controller with instance number '%lu'"),
6382 aInstance);
6383}
6384
6385STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6386{
6387 AutoCaller autoCaller(this);
6388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6389
6390 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6391
6392 HRESULT rc = checkStateDependency(MutableStateDep);
6393 if (FAILED(rc)) return rc;
6394
6395 ComObjPtr<StorageController> ctrl;
6396
6397 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6398 if (SUCCEEDED(rc))
6399 {
6400 /* Ensure that only one controller of each type is marked as bootable. */
6401 if (fBootable == TRUE)
6402 {
6403 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6404 it != mStorageControllers->end();
6405 ++it)
6406 {
6407 ComObjPtr<StorageController> aCtrl = (*it);
6408
6409 if ( (aCtrl->getName() != Utf8Str(aName))
6410 && aCtrl->getBootable() == TRUE
6411 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6412 && aCtrl->getControllerType() == ctrl->getControllerType())
6413 {
6414 aCtrl->setBootable(FALSE);
6415 break;
6416 }
6417 }
6418 }
6419
6420 if (SUCCEEDED(rc))
6421 {
6422 ctrl->setBootable(fBootable);
6423 setModified(IsModified_Storage);
6424 }
6425 }
6426
6427 if (SUCCEEDED(rc))
6428 {
6429 /* inform the direct session if any */
6430 alock.release();
6431 onStorageControllerChange();
6432 }
6433
6434 return rc;
6435}
6436
6437STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6438{
6439 CheckComArgStrNotEmptyOrNull(aName);
6440
6441 AutoCaller autoCaller(this);
6442 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6443
6444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6445
6446 HRESULT rc = checkStateDependency(MutableStateDep);
6447 if (FAILED(rc)) return rc;
6448
6449 ComObjPtr<StorageController> ctrl;
6450 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6451 if (FAILED(rc)) return rc;
6452
6453 {
6454 /* find all attached devices to the appropriate storage controller and detach them all */
6455 // make a temporary list because detachDevice invalidates iterators into
6456 // mMediaData->mAttachments
6457 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6458
6459 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6460 it != llAttachments2.end();
6461 ++it)
6462 {
6463 MediumAttachment *pAttachTemp = *it;
6464
6465 AutoCaller localAutoCaller(pAttachTemp);
6466 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6467
6468 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6469
6470 if (pAttachTemp->getControllerName() == aName)
6471 {
6472 rc = detachDevice(pAttachTemp, alock, NULL);
6473 if (FAILED(rc)) return rc;
6474 }
6475 }
6476 }
6477
6478 /* We can remove it now. */
6479 setModified(IsModified_Storage);
6480 mStorageControllers.backup();
6481
6482 ctrl->unshare();
6483
6484 mStorageControllers->remove(ctrl);
6485
6486 /* inform the direct session if any */
6487 alock.release();
6488 onStorageControllerChange();
6489
6490 return S_OK;
6491}
6492
6493STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6494 ULONG *puOriginX,
6495 ULONG *puOriginY,
6496 ULONG *puWidth,
6497 ULONG *puHeight,
6498 BOOL *pfEnabled)
6499{
6500 LogFlowThisFunc(("\n"));
6501
6502 CheckComArgNotNull(puOriginX);
6503 CheckComArgNotNull(puOriginY);
6504 CheckComArgNotNull(puWidth);
6505 CheckComArgNotNull(puHeight);
6506 CheckComArgNotNull(pfEnabled);
6507
6508 uint32_t u32OriginX= 0;
6509 uint32_t u32OriginY= 0;
6510 uint32_t u32Width = 0;
6511 uint32_t u32Height = 0;
6512 uint16_t u16Flags = 0;
6513
6514 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6515 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6516 if (RT_FAILURE(vrc))
6517 {
6518#ifdef RT_OS_WINDOWS
6519 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6520 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6521 * So just assign fEnable to TRUE again.
6522 * The right fix would be to change GUI API wrappers to make sure that parameters
6523 * are changed only if API succeeds.
6524 */
6525 *pfEnabled = TRUE;
6526#endif
6527 return setError(VBOX_E_IPRT_ERROR,
6528 tr("Saved guest size is not available (%Rrc)"),
6529 vrc);
6530 }
6531
6532 *puOriginX = u32OriginX;
6533 *puOriginY = u32OriginY;
6534 *puWidth = u32Width;
6535 *puHeight = u32Height;
6536 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6537
6538 return S_OK;
6539}
6540
6541STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6542{
6543 LogFlowThisFunc(("\n"));
6544
6545 CheckComArgNotNull(aSize);
6546 CheckComArgNotNull(aWidth);
6547 CheckComArgNotNull(aHeight);
6548
6549 if (aScreenId != 0)
6550 return E_NOTIMPL;
6551
6552 AutoCaller autoCaller(this);
6553 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6554
6555 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6556
6557 uint8_t *pu8Data = NULL;
6558 uint32_t cbData = 0;
6559 uint32_t u32Width = 0;
6560 uint32_t u32Height = 0;
6561
6562 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6563
6564 if (RT_FAILURE(vrc))
6565 return setError(VBOX_E_IPRT_ERROR,
6566 tr("Saved screenshot data is not available (%Rrc)"),
6567 vrc);
6568
6569 *aSize = cbData;
6570 *aWidth = u32Width;
6571 *aHeight = u32Height;
6572
6573 freeSavedDisplayScreenshot(pu8Data);
6574
6575 return S_OK;
6576}
6577
6578STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6579{
6580 LogFlowThisFunc(("\n"));
6581
6582 CheckComArgNotNull(aWidth);
6583 CheckComArgNotNull(aHeight);
6584 CheckComArgOutSafeArrayPointerValid(aData);
6585
6586 if (aScreenId != 0)
6587 return E_NOTIMPL;
6588
6589 AutoCaller autoCaller(this);
6590 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6591
6592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6593
6594 uint8_t *pu8Data = NULL;
6595 uint32_t cbData = 0;
6596 uint32_t u32Width = 0;
6597 uint32_t u32Height = 0;
6598
6599 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6600
6601 if (RT_FAILURE(vrc))
6602 return setError(VBOX_E_IPRT_ERROR,
6603 tr("Saved screenshot data is not available (%Rrc)"),
6604 vrc);
6605
6606 *aWidth = u32Width;
6607 *aHeight = u32Height;
6608
6609 com::SafeArray<BYTE> bitmap(cbData);
6610 /* Convert pixels to format expected by the API caller. */
6611 if (aBGR)
6612 {
6613 /* [0] B, [1] G, [2] R, [3] A. */
6614 for (unsigned i = 0; i < cbData; i += 4)
6615 {
6616 bitmap[i] = pu8Data[i];
6617 bitmap[i + 1] = pu8Data[i + 1];
6618 bitmap[i + 2] = pu8Data[i + 2];
6619 bitmap[i + 3] = 0xff;
6620 }
6621 }
6622 else
6623 {
6624 /* [0] R, [1] G, [2] B, [3] A. */
6625 for (unsigned i = 0; i < cbData; i += 4)
6626 {
6627 bitmap[i] = pu8Data[i + 2];
6628 bitmap[i + 1] = pu8Data[i + 1];
6629 bitmap[i + 2] = pu8Data[i];
6630 bitmap[i + 3] = 0xff;
6631 }
6632 }
6633 bitmap.detachTo(ComSafeArrayOutArg(aData));
6634
6635 freeSavedDisplayScreenshot(pu8Data);
6636
6637 return S_OK;
6638}
6639
6640
6641STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6642{
6643 LogFlowThisFunc(("\n"));
6644
6645 CheckComArgNotNull(aWidth);
6646 CheckComArgNotNull(aHeight);
6647 CheckComArgOutSafeArrayPointerValid(aData);
6648
6649 if (aScreenId != 0)
6650 return E_NOTIMPL;
6651
6652 AutoCaller autoCaller(this);
6653 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6654
6655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6656
6657 uint8_t *pu8Data = NULL;
6658 uint32_t cbData = 0;
6659 uint32_t u32Width = 0;
6660 uint32_t u32Height = 0;
6661
6662 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6663
6664 if (RT_FAILURE(vrc))
6665 return setError(VBOX_E_IPRT_ERROR,
6666 tr("Saved screenshot data is not available (%Rrc)"),
6667 vrc);
6668
6669 *aWidth = u32Width;
6670 *aHeight = u32Height;
6671
6672 HRESULT rc = S_OK;
6673 uint8_t *pu8PNG = NULL;
6674 uint32_t cbPNG = 0;
6675 uint32_t cxPNG = 0;
6676 uint32_t cyPNG = 0;
6677
6678 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6679
6680 if (RT_SUCCESS(vrc))
6681 {
6682 com::SafeArray<BYTE> screenData(cbPNG);
6683 screenData.initFrom(pu8PNG, cbPNG);
6684 if (pu8PNG)
6685 RTMemFree(pu8PNG);
6686 screenData.detachTo(ComSafeArrayOutArg(aData));
6687 }
6688 else
6689 {
6690 if (pu8PNG)
6691 RTMemFree(pu8PNG);
6692 return setError(VBOX_E_IPRT_ERROR,
6693 tr("Could not convert screenshot to PNG (%Rrc)"),
6694 vrc);
6695 }
6696
6697 freeSavedDisplayScreenshot(pu8Data);
6698
6699 return rc;
6700}
6701
6702STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6703{
6704 LogFlowThisFunc(("\n"));
6705
6706 CheckComArgNotNull(aSize);
6707 CheckComArgNotNull(aWidth);
6708 CheckComArgNotNull(aHeight);
6709
6710 if (aScreenId != 0)
6711 return E_NOTIMPL;
6712
6713 AutoCaller autoCaller(this);
6714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6715
6716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6717
6718 uint8_t *pu8Data = NULL;
6719 uint32_t cbData = 0;
6720 uint32_t u32Width = 0;
6721 uint32_t u32Height = 0;
6722
6723 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6724
6725 if (RT_FAILURE(vrc))
6726 return setError(VBOX_E_IPRT_ERROR,
6727 tr("Saved screenshot data is not available (%Rrc)"),
6728 vrc);
6729
6730 *aSize = cbData;
6731 *aWidth = u32Width;
6732 *aHeight = u32Height;
6733
6734 freeSavedDisplayScreenshot(pu8Data);
6735
6736 return S_OK;
6737}
6738
6739STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6740{
6741 LogFlowThisFunc(("\n"));
6742
6743 CheckComArgNotNull(aWidth);
6744 CheckComArgNotNull(aHeight);
6745 CheckComArgOutSafeArrayPointerValid(aData);
6746
6747 if (aScreenId != 0)
6748 return E_NOTIMPL;
6749
6750 AutoCaller autoCaller(this);
6751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6752
6753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6754
6755 uint8_t *pu8Data = NULL;
6756 uint32_t cbData = 0;
6757 uint32_t u32Width = 0;
6758 uint32_t u32Height = 0;
6759
6760 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6761
6762 if (RT_FAILURE(vrc))
6763 return setError(VBOX_E_IPRT_ERROR,
6764 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6765 vrc);
6766
6767 *aWidth = u32Width;
6768 *aHeight = u32Height;
6769
6770 com::SafeArray<BYTE> png(cbData);
6771 png.initFrom(pu8Data, cbData);
6772 png.detachTo(ComSafeArrayOutArg(aData));
6773
6774 freeSavedDisplayScreenshot(pu8Data);
6775
6776 return S_OK;
6777}
6778
6779STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6780{
6781 HRESULT rc = S_OK;
6782 LogFlowThisFunc(("\n"));
6783
6784 AutoCaller autoCaller(this);
6785 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6786
6787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6788
6789 if (!mHWData->mCPUHotPlugEnabled)
6790 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6791
6792 if (aCpu >= mHWData->mCPUCount)
6793 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6794
6795 if (mHWData->mCPUAttached[aCpu])
6796 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6797
6798 alock.release();
6799 rc = onCPUChange(aCpu, false);
6800 alock.acquire();
6801 if (FAILED(rc)) return rc;
6802
6803 setModified(IsModified_MachineData);
6804 mHWData.backup();
6805 mHWData->mCPUAttached[aCpu] = true;
6806
6807 /* Save settings if online */
6808 if (Global::IsOnline(mData->mMachineState))
6809 saveSettings(NULL);
6810
6811 return S_OK;
6812}
6813
6814STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6815{
6816 HRESULT rc = S_OK;
6817 LogFlowThisFunc(("\n"));
6818
6819 AutoCaller autoCaller(this);
6820 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6821
6822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6823
6824 if (!mHWData->mCPUHotPlugEnabled)
6825 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6826
6827 if (aCpu >= SchemaDefs::MaxCPUCount)
6828 return setError(E_INVALIDARG,
6829 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6830 SchemaDefs::MaxCPUCount);
6831
6832 if (!mHWData->mCPUAttached[aCpu])
6833 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6834
6835 /* CPU 0 can't be detached */
6836 if (aCpu == 0)
6837 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6838
6839 alock.release();
6840 rc = onCPUChange(aCpu, true);
6841 alock.acquire();
6842 if (FAILED(rc)) return rc;
6843
6844 setModified(IsModified_MachineData);
6845 mHWData.backup();
6846 mHWData->mCPUAttached[aCpu] = false;
6847
6848 /* Save settings if online */
6849 if (Global::IsOnline(mData->mMachineState))
6850 saveSettings(NULL);
6851
6852 return S_OK;
6853}
6854
6855STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6856{
6857 LogFlowThisFunc(("\n"));
6858
6859 CheckComArgNotNull(aCpuAttached);
6860
6861 *aCpuAttached = false;
6862
6863 AutoCaller autoCaller(this);
6864 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6865
6866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6867
6868 /* If hotplug is enabled the CPU is always enabled. */
6869 if (!mHWData->mCPUHotPlugEnabled)
6870 {
6871 if (aCpu < mHWData->mCPUCount)
6872 *aCpuAttached = true;
6873 }
6874 else
6875 {
6876 if (aCpu < SchemaDefs::MaxCPUCount)
6877 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6878 }
6879
6880 return S_OK;
6881}
6882
6883STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6884{
6885 CheckComArgOutPointerValid(aName);
6886
6887 AutoCaller autoCaller(this);
6888 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6889
6890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6891
6892 Utf8Str log = queryLogFilename(aIdx);
6893 if (!RTFileExists(log.c_str()))
6894 log.setNull();
6895 log.cloneTo(aName);
6896
6897 return S_OK;
6898}
6899
6900STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6901{
6902 LogFlowThisFunc(("\n"));
6903 CheckComArgOutSafeArrayPointerValid(aData);
6904 if (aSize < 0)
6905 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6906
6907 AutoCaller autoCaller(this);
6908 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6909
6910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6911
6912 HRESULT rc = S_OK;
6913 Utf8Str log = queryLogFilename(aIdx);
6914
6915 /* do not unnecessarily hold the lock while doing something which does
6916 * not need the lock and potentially takes a long time. */
6917 alock.release();
6918
6919 /* Limit the chunk size to 32K for now, as that gives better performance
6920 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6921 * One byte expands to approx. 25 bytes of breathtaking XML. */
6922 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6923 com::SafeArray<BYTE> logData(cbData);
6924
6925 RTFILE LogFile;
6926 int vrc = RTFileOpen(&LogFile, log.c_str(),
6927 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6928 if (RT_SUCCESS(vrc))
6929 {
6930 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6931 if (RT_SUCCESS(vrc))
6932 logData.resize(cbData);
6933 else
6934 rc = setError(VBOX_E_IPRT_ERROR,
6935 tr("Could not read log file '%s' (%Rrc)"),
6936 log.c_str(), vrc);
6937 RTFileClose(LogFile);
6938 }
6939 else
6940 rc = setError(VBOX_E_IPRT_ERROR,
6941 tr("Could not open log file '%s' (%Rrc)"),
6942 log.c_str(), vrc);
6943
6944 if (FAILED(rc))
6945 logData.resize(0);
6946 logData.detachTo(ComSafeArrayOutArg(aData));
6947
6948 return rc;
6949}
6950
6951
6952/**
6953 * Currently this method doesn't attach device to the running VM,
6954 * just makes sure it's plugged on next VM start.
6955 */
6956STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6957{
6958 AutoCaller autoCaller(this);
6959 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6960
6961 // lock scope
6962 {
6963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6964
6965 HRESULT rc = checkStateDependency(MutableStateDep);
6966 if (FAILED(rc)) return rc;
6967
6968 ChipsetType_T aChipset = ChipsetType_PIIX3;
6969 COMGETTER(ChipsetType)(&aChipset);
6970
6971 if (aChipset != ChipsetType_ICH9)
6972 {
6973 return setError(E_INVALIDARG,
6974 tr("Host PCI attachment only supported with ICH9 chipset"));
6975 }
6976
6977 // check if device with this host PCI address already attached
6978 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6979 it != mHWData->mPCIDeviceAssignments.end();
6980 ++it)
6981 {
6982 LONG iHostAddress = -1;
6983 ComPtr<PCIDeviceAttachment> pAttach;
6984 pAttach = *it;
6985 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6986 if (iHostAddress == hostAddress)
6987 return setError(E_INVALIDARG,
6988 tr("Device with host PCI address already attached to this VM"));
6989 }
6990
6991 ComObjPtr<PCIDeviceAttachment> pda;
6992 char name[32];
6993
6994 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6995 Bstr bname(name);
6996 pda.createObject();
6997 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6998 setModified(IsModified_MachineData);
6999 mHWData.backup();
7000 mHWData->mPCIDeviceAssignments.push_back(pda);
7001 }
7002
7003 return S_OK;
7004}
7005
7006/**
7007 * Currently this method doesn't detach device from the running VM,
7008 * just makes sure it's not plugged on next VM start.
7009 */
7010STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7011{
7012 AutoCaller autoCaller(this);
7013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7014
7015 ComObjPtr<PCIDeviceAttachment> pAttach;
7016 bool fRemoved = false;
7017 HRESULT rc;
7018
7019 // lock scope
7020 {
7021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7022
7023 rc = checkStateDependency(MutableStateDep);
7024 if (FAILED(rc)) return rc;
7025
7026 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7027 it != mHWData->mPCIDeviceAssignments.end();
7028 ++it)
7029 {
7030 LONG iHostAddress = -1;
7031 pAttach = *it;
7032 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7033 if (iHostAddress != -1 && iHostAddress == hostAddress)
7034 {
7035 setModified(IsModified_MachineData);
7036 mHWData.backup();
7037 mHWData->mPCIDeviceAssignments.remove(pAttach);
7038 fRemoved = true;
7039 break;
7040 }
7041 }
7042 }
7043
7044
7045 /* Fire event outside of the lock */
7046 if (fRemoved)
7047 {
7048 Assert(!pAttach.isNull());
7049 ComPtr<IEventSource> es;
7050 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7051 Assert(SUCCEEDED(rc));
7052 Bstr mid;
7053 rc = this->COMGETTER(Id)(mid.asOutParam());
7054 Assert(SUCCEEDED(rc));
7055 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7056 }
7057
7058 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7059 tr("No host PCI device %08x attached"),
7060 hostAddress
7061 );
7062}
7063
7064STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7065{
7066 CheckComArgOutSafeArrayPointerValid(aAssignments);
7067
7068 AutoCaller autoCaller(this);
7069 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7070
7071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7072
7073 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7074 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7075
7076 return S_OK;
7077}
7078
7079STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7080{
7081 CheckComArgOutPointerValid(aBandwidthControl);
7082
7083 AutoCaller autoCaller(this);
7084 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7085
7086 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7087
7088 return S_OK;
7089}
7090
7091STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7092{
7093 CheckComArgOutPointerValid(pfEnabled);
7094 AutoCaller autoCaller(this);
7095 HRESULT hrc = autoCaller.rc();
7096 if (SUCCEEDED(hrc))
7097 {
7098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7099 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7100 }
7101 return hrc;
7102}
7103
7104STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7105{
7106 AutoCaller autoCaller(this);
7107 HRESULT hrc = autoCaller.rc();
7108 if (SUCCEEDED(hrc))
7109 {
7110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7111 hrc = checkStateDependency(MutableStateDep);
7112 if (SUCCEEDED(hrc))
7113 {
7114 hrc = mHWData.backupEx();
7115 if (SUCCEEDED(hrc))
7116 {
7117 setModified(IsModified_MachineData);
7118 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7119 }
7120 }
7121 }
7122 return hrc;
7123}
7124
7125STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7126{
7127 CheckComArgOutPointerValid(pbstrConfig);
7128 AutoCaller autoCaller(this);
7129 HRESULT hrc = autoCaller.rc();
7130 if (SUCCEEDED(hrc))
7131 {
7132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7133 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7134 }
7135 return hrc;
7136}
7137
7138STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7139{
7140 CheckComArgStr(bstrConfig);
7141 AutoCaller autoCaller(this);
7142 HRESULT hrc = autoCaller.rc();
7143 if (SUCCEEDED(hrc))
7144 {
7145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7146 hrc = checkStateDependency(MutableStateDep);
7147 if (SUCCEEDED(hrc))
7148 {
7149 hrc = mHWData.backupEx();
7150 if (SUCCEEDED(hrc))
7151 {
7152 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7153 if (SUCCEEDED(hrc))
7154 setModified(IsModified_MachineData);
7155 }
7156 }
7157 }
7158 return hrc;
7159
7160}
7161
7162STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7163{
7164 CheckComArgOutPointerValid(pfAllow);
7165 AutoCaller autoCaller(this);
7166 HRESULT hrc = autoCaller.rc();
7167 if (SUCCEEDED(hrc))
7168 {
7169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7170 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7171 }
7172 return hrc;
7173}
7174
7175STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7176{
7177 AutoCaller autoCaller(this);
7178 HRESULT hrc = autoCaller.rc();
7179 if (SUCCEEDED(hrc))
7180 {
7181 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7182 hrc = checkStateDependency(MutableStateDep);
7183 if (SUCCEEDED(hrc))
7184 {
7185 hrc = mHWData.backupEx();
7186 if (SUCCEEDED(hrc))
7187 {
7188 setModified(IsModified_MachineData);
7189 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7190 }
7191 }
7192 }
7193 return hrc;
7194}
7195
7196STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7197{
7198 CheckComArgOutPointerValid(pfEnabled);
7199 AutoCaller autoCaller(this);
7200 HRESULT hrc = autoCaller.rc();
7201 if (SUCCEEDED(hrc))
7202 {
7203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7204 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7205 }
7206 return hrc;
7207}
7208
7209STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7210{
7211 AutoCaller autoCaller(this);
7212 HRESULT hrc = autoCaller.rc();
7213 if (SUCCEEDED(hrc))
7214 {
7215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7216 hrc = checkStateDependency(MutableStateDep);
7217 if ( SUCCEEDED(hrc)
7218 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7219 {
7220 AutostartDb *autostartDb = mParent->getAutostartDb();
7221 int vrc;
7222
7223 if (fEnabled)
7224 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7225 else
7226 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7227
7228 if (RT_SUCCESS(vrc))
7229 {
7230 hrc = mHWData.backupEx();
7231 if (SUCCEEDED(hrc))
7232 {
7233 setModified(IsModified_MachineData);
7234 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7235 }
7236 }
7237 else if (vrc == VERR_NOT_SUPPORTED)
7238 hrc = setError(VBOX_E_NOT_SUPPORTED,
7239 tr("The VM autostart feature is not supported on this platform"));
7240 else if (vrc == VERR_PATH_NOT_FOUND)
7241 hrc = setError(E_FAIL,
7242 tr("The path to the autostart database is not set"));
7243 else
7244 hrc = setError(E_UNEXPECTED,
7245 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7246 fEnabled ? "Adding" : "Removing",
7247 mUserData->s.strName.c_str(), vrc);
7248 }
7249 }
7250 return hrc;
7251}
7252
7253STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7254{
7255 CheckComArgOutPointerValid(puDelay);
7256 AutoCaller autoCaller(this);
7257 HRESULT hrc = autoCaller.rc();
7258 if (SUCCEEDED(hrc))
7259 {
7260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7261 *puDelay = mHWData->mAutostart.uAutostartDelay;
7262 }
7263 return hrc;
7264}
7265
7266STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7267{
7268 AutoCaller autoCaller(this);
7269 HRESULT hrc = autoCaller.rc();
7270 if (SUCCEEDED(hrc))
7271 {
7272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7273 hrc = checkStateDependency(MutableStateDep);
7274 if (SUCCEEDED(hrc))
7275 {
7276 hrc = mHWData.backupEx();
7277 if (SUCCEEDED(hrc))
7278 {
7279 setModified(IsModified_MachineData);
7280 mHWData->mAutostart.uAutostartDelay = uDelay;
7281 }
7282 }
7283 }
7284 return hrc;
7285}
7286
7287STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7288{
7289 CheckComArgOutPointerValid(penmAutostopType);
7290 AutoCaller autoCaller(this);
7291 HRESULT hrc = autoCaller.rc();
7292 if (SUCCEEDED(hrc))
7293 {
7294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7295 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7296 }
7297 return hrc;
7298}
7299
7300STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7301{
7302 AutoCaller autoCaller(this);
7303 HRESULT hrc = autoCaller.rc();
7304 if (SUCCEEDED(hrc))
7305 {
7306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7307 hrc = checkStateDependency(MutableStateDep);
7308 if ( SUCCEEDED(hrc)
7309 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7310 {
7311 AutostartDb *autostartDb = mParent->getAutostartDb();
7312 int vrc;
7313
7314 if (enmAutostopType != AutostopType_Disabled)
7315 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7316 else
7317 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7318
7319 if (RT_SUCCESS(vrc))
7320 {
7321 hrc = mHWData.backupEx();
7322 if (SUCCEEDED(hrc))
7323 {
7324 setModified(IsModified_MachineData);
7325 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7326 }
7327 }
7328 else if (vrc == VERR_NOT_SUPPORTED)
7329 hrc = setError(VBOX_E_NOT_SUPPORTED,
7330 tr("The VM autostop feature is not supported on this platform"));
7331 else if (vrc == VERR_PATH_NOT_FOUND)
7332 hrc = setError(E_FAIL,
7333 tr("The path to the autostart database is not set"));
7334 else
7335 hrc = setError(E_UNEXPECTED,
7336 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7337 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7338 mUserData->s.strName.c_str(), vrc);
7339 }
7340 }
7341 return hrc;
7342}
7343
7344STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7345{
7346 CheckComArgOutPointerValid(aDefaultFrontend);
7347 AutoCaller autoCaller(this);
7348 HRESULT hrc = autoCaller.rc();
7349 if (SUCCEEDED(hrc))
7350 {
7351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7352 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7353 }
7354 return hrc;
7355}
7356
7357STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7358{
7359 CheckComArgStr(aDefaultFrontend);
7360 AutoCaller autoCaller(this);
7361 HRESULT hrc = autoCaller.rc();
7362 if (SUCCEEDED(hrc))
7363 {
7364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7365 hrc = checkStateDependency(MutableOrSavedStateDep);
7366 if (SUCCEEDED(hrc))
7367 {
7368 hrc = mHWData.backupEx();
7369 if (SUCCEEDED(hrc))
7370 {
7371 setModified(IsModified_MachineData);
7372 mHWData->mDefaultFrontend = aDefaultFrontend;
7373 }
7374 }
7375 }
7376 return hrc;
7377}
7378
7379STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7380{
7381 CheckComArgSafeArrayNotNull(aIcon);
7382 CheckComArgOutSafeArrayPointerValid(aIcon);
7383 AutoCaller autoCaller(this);
7384 HRESULT hrc = autoCaller.rc();
7385 if (SUCCEEDED(hrc))
7386 {
7387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7388 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7389 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7390 icon.detachTo(ComSafeArrayOutArg(aIcon));
7391 }
7392 return hrc;
7393}
7394
7395STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7396{
7397 CheckComArgSafeArrayNotNull(aIcon);
7398 AutoCaller autoCaller(this);
7399 HRESULT hrc = autoCaller.rc();
7400 if (SUCCEEDED(hrc))
7401 {
7402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7403 hrc = checkStateDependency(MutableOrSavedStateDep);
7404 if (SUCCEEDED(hrc))
7405 {
7406 setModified(IsModified_MachineData);
7407 mUserData.backup();
7408 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7409 mUserData->mIcon.clear();
7410 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7411 }
7412 }
7413 return hrc;
7414}
7415
7416STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7417{
7418 LogFlowFuncEnter();
7419
7420 CheckComArgNotNull(pTarget);
7421 CheckComArgOutPointerValid(pProgress);
7422
7423 /* Convert the options. */
7424 RTCList<CloneOptions_T> optList;
7425 if (options != NULL)
7426 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7427
7428 if (optList.contains(CloneOptions_Link))
7429 {
7430 if (!isSnapshotMachine())
7431 return setError(E_INVALIDARG,
7432 tr("Linked clone can only be created from a snapshot"));
7433 if (mode != CloneMode_MachineState)
7434 return setError(E_INVALIDARG,
7435 tr("Linked clone can only be created for a single machine state"));
7436 }
7437 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7438
7439 AutoCaller autoCaller(this);
7440 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7441
7442
7443 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7444
7445 HRESULT rc = pWorker->start(pProgress);
7446
7447 LogFlowFuncLeave();
7448
7449 return rc;
7450}
7451
7452// public methods for internal purposes
7453/////////////////////////////////////////////////////////////////////////////
7454
7455/**
7456 * Adds the given IsModified_* flag to the dirty flags of the machine.
7457 * This must be called either during loadSettings or under the machine write lock.
7458 * @param fl
7459 */
7460void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7461{
7462 mData->flModifications |= fl;
7463 if (fAllowStateModification && isStateModificationAllowed())
7464 mData->mCurrentStateModified = true;
7465}
7466
7467/**
7468 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7469 * care of the write locking.
7470 *
7471 * @param fModifications The flag to add.
7472 */
7473void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7474{
7475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7476 setModified(fModification, fAllowStateModification);
7477}
7478
7479/**
7480 * Saves the registry entry of this machine to the given configuration node.
7481 *
7482 * @param aEntryNode Node to save the registry entry to.
7483 *
7484 * @note locks this object for reading.
7485 */
7486HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7487{
7488 AutoLimitedCaller autoCaller(this);
7489 AssertComRCReturnRC(autoCaller.rc());
7490
7491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7492
7493 data.uuid = mData->mUuid;
7494 data.strSettingsFile = mData->m_strConfigFile;
7495
7496 return S_OK;
7497}
7498
7499/**
7500 * Calculates the absolute path of the given path taking the directory of the
7501 * machine settings file as the current directory.
7502 *
7503 * @param aPath Path to calculate the absolute path for.
7504 * @param aResult Where to put the result (used only on success, can be the
7505 * same Utf8Str instance as passed in @a aPath).
7506 * @return IPRT result.
7507 *
7508 * @note Locks this object for reading.
7509 */
7510int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7511{
7512 AutoCaller autoCaller(this);
7513 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7514
7515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7516
7517 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7518
7519 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7520
7521 strSettingsDir.stripFilename();
7522 char folder[RTPATH_MAX];
7523 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7524 if (RT_SUCCESS(vrc))
7525 aResult = folder;
7526
7527 return vrc;
7528}
7529
7530/**
7531 * Copies strSource to strTarget, making it relative to the machine folder
7532 * if it is a subdirectory thereof, or simply copying it otherwise.
7533 *
7534 * @param strSource Path to evaluate and copy.
7535 * @param strTarget Buffer to receive target path.
7536 *
7537 * @note Locks this object for reading.
7538 */
7539void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7540 Utf8Str &strTarget)
7541{
7542 AutoCaller autoCaller(this);
7543 AssertComRCReturn(autoCaller.rc(), (void)0);
7544
7545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7546
7547 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7548 // use strTarget as a temporary buffer to hold the machine settings dir
7549 strTarget = mData->m_strConfigFileFull;
7550 strTarget.stripFilename();
7551 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7552 {
7553 // is relative: then append what's left
7554 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7555 // for empty paths (only possible for subdirs) use "." to avoid
7556 // triggering default settings for not present config attributes.
7557 if (strTarget.isEmpty())
7558 strTarget = ".";
7559 }
7560 else
7561 // is not relative: then overwrite
7562 strTarget = strSource;
7563}
7564
7565/**
7566 * Returns the full path to the machine's log folder in the
7567 * \a aLogFolder argument.
7568 */
7569void Machine::getLogFolder(Utf8Str &aLogFolder)
7570{
7571 AutoCaller autoCaller(this);
7572 AssertComRCReturnVoid(autoCaller.rc());
7573
7574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7575
7576 char szTmp[RTPATH_MAX];
7577 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7578 if (RT_SUCCESS(vrc))
7579 {
7580 if (szTmp[0] && !mUserData.isNull())
7581 {
7582 char szTmp2[RTPATH_MAX];
7583 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7584 if (RT_SUCCESS(vrc))
7585 aLogFolder = BstrFmt("%s%c%s",
7586 szTmp2,
7587 RTPATH_DELIMITER,
7588 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7589 }
7590 else
7591 vrc = VERR_PATH_IS_RELATIVE;
7592 }
7593
7594 if (RT_FAILURE(vrc))
7595 {
7596 // fallback if VBOX_USER_LOGHOME is not set or invalid
7597 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7598 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7599 aLogFolder.append(RTPATH_DELIMITER);
7600 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7601 }
7602}
7603
7604/**
7605 * Returns the full path to the machine's log file for an given index.
7606 */
7607Utf8Str Machine::queryLogFilename(ULONG idx)
7608{
7609 Utf8Str logFolder;
7610 getLogFolder(logFolder);
7611 Assert(logFolder.length());
7612 Utf8Str log;
7613 if (idx == 0)
7614 log = Utf8StrFmt("%s%cVBox.log",
7615 logFolder.c_str(), RTPATH_DELIMITER);
7616 else
7617 log = Utf8StrFmt("%s%cVBox.log.%d",
7618 logFolder.c_str(), RTPATH_DELIMITER, idx);
7619 return log;
7620}
7621
7622/**
7623 * Composes a unique saved state filename based on the current system time. The filename is
7624 * granular to the second so this will work so long as no more than one snapshot is taken on
7625 * a machine per second.
7626 *
7627 * Before version 4.1, we used this formula for saved state files:
7628 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7629 * which no longer works because saved state files can now be shared between the saved state of the
7630 * "saved" machine and an online snapshot, and the following would cause problems:
7631 * 1) save machine
7632 * 2) create online snapshot from that machine state --> reusing saved state file
7633 * 3) save machine again --> filename would be reused, breaking the online snapshot
7634 *
7635 * So instead we now use a timestamp.
7636 *
7637 * @param str
7638 */
7639void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7640{
7641 AutoCaller autoCaller(this);
7642 AssertComRCReturnVoid(autoCaller.rc());
7643
7644 {
7645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7646 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7647 }
7648
7649 RTTIMESPEC ts;
7650 RTTimeNow(&ts);
7651 RTTIME time;
7652 RTTimeExplode(&time, &ts);
7653
7654 strStateFilePath += RTPATH_DELIMITER;
7655 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7656 time.i32Year, time.u8Month, time.u8MonthDay,
7657 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7658}
7659
7660/**
7661 * @note Locks this object for writing, calls the client process
7662 * (inside the lock).
7663 */
7664HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7665 const Utf8Str &strFrontend,
7666 const Utf8Str &strEnvironment,
7667 ProgressProxy *aProgress)
7668{
7669 LogFlowThisFuncEnter();
7670
7671 AssertReturn(aControl, E_FAIL);
7672 AssertReturn(aProgress, E_FAIL);
7673 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7674
7675 AutoCaller autoCaller(this);
7676 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7677
7678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7679
7680 if (!mData->mRegistered)
7681 return setError(E_UNEXPECTED,
7682 tr("The machine '%s' is not registered"),
7683 mUserData->s.strName.c_str());
7684
7685 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7686
7687 if ( mData->mSession.mState == SessionState_Locked
7688 || mData->mSession.mState == SessionState_Spawning
7689 || mData->mSession.mState == SessionState_Unlocking)
7690 return setError(VBOX_E_INVALID_OBJECT_STATE,
7691 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7692 mUserData->s.strName.c_str());
7693
7694 /* may not be busy */
7695 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7696
7697 /* get the path to the executable */
7698 char szPath[RTPATH_MAX];
7699 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7700 size_t sz = strlen(szPath);
7701 szPath[sz++] = RTPATH_DELIMITER;
7702 szPath[sz] = 0;
7703 char *cmd = szPath + sz;
7704 sz = RTPATH_MAX - sz;
7705
7706 int vrc = VINF_SUCCESS;
7707 RTPROCESS pid = NIL_RTPROCESS;
7708
7709 RTENV env = RTENV_DEFAULT;
7710
7711 if (!strEnvironment.isEmpty())
7712 {
7713 char *newEnvStr = NULL;
7714
7715 do
7716 {
7717 /* clone the current environment */
7718 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7719 AssertRCBreakStmt(vrc2, vrc = vrc2);
7720
7721 newEnvStr = RTStrDup(strEnvironment.c_str());
7722 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7723
7724 /* put new variables to the environment
7725 * (ignore empty variable names here since RTEnv API
7726 * intentionally doesn't do that) */
7727 char *var = newEnvStr;
7728 for (char *p = newEnvStr; *p; ++p)
7729 {
7730 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7731 {
7732 *p = '\0';
7733 if (*var)
7734 {
7735 char *val = strchr(var, '=');
7736 if (val)
7737 {
7738 *val++ = '\0';
7739 vrc2 = RTEnvSetEx(env, var, val);
7740 }
7741 else
7742 vrc2 = RTEnvUnsetEx(env, var);
7743 if (RT_FAILURE(vrc2))
7744 break;
7745 }
7746 var = p + 1;
7747 }
7748 }
7749 if (RT_SUCCESS(vrc2) && *var)
7750 vrc2 = RTEnvPutEx(env, var);
7751
7752 AssertRCBreakStmt(vrc2, vrc = vrc2);
7753 }
7754 while (0);
7755
7756 if (newEnvStr != NULL)
7757 RTStrFree(newEnvStr);
7758 }
7759
7760#ifdef VBOX_WITH_QTGUI
7761 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7762 {
7763# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7764 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7765# else
7766 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7767# endif
7768 Assert(sz >= sizeof(VirtualBox_exe));
7769 strcpy(cmd, VirtualBox_exe);
7770
7771 Utf8Str idStr = mData->mUuid.toString();
7772 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7773 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7774 }
7775#else /* !VBOX_WITH_QTGUI */
7776 if (0)
7777 ;
7778#endif /* VBOX_WITH_QTGUI */
7779
7780 else
7781
7782#ifdef VBOX_WITH_VBOXSDL
7783 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7784 {
7785 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7786 Assert(sz >= sizeof(VBoxSDL_exe));
7787 strcpy(cmd, VBoxSDL_exe);
7788
7789 Utf8Str idStr = mData->mUuid.toString();
7790 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7791 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7792 }
7793#else /* !VBOX_WITH_VBOXSDL */
7794 if (0)
7795 ;
7796#endif /* !VBOX_WITH_VBOXSDL */
7797
7798 else
7799
7800#ifdef VBOX_WITH_HEADLESS
7801 if ( strFrontend == "headless"
7802 || strFrontend == "capture"
7803 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7804 )
7805 {
7806 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7807 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7808 * and a VM works even if the server has not been installed.
7809 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7810 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7811 * differently in 4.0 and 3.x.
7812 */
7813 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7814 Assert(sz >= sizeof(VBoxHeadless_exe));
7815 strcpy(cmd, VBoxHeadless_exe);
7816
7817 Utf8Str idStr = mData->mUuid.toString();
7818 /* Leave space for "--capture" arg. */
7819 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7820 "--startvm", idStr.c_str(),
7821 "--vrde", "config",
7822 0, /* For "--capture". */
7823 0 };
7824 if (strFrontend == "capture")
7825 {
7826 unsigned pos = RT_ELEMENTS(args) - 2;
7827 args[pos] = "--capture";
7828 }
7829 vrc = RTProcCreate(szPath, args, env,
7830#ifdef RT_OS_WINDOWS
7831 RTPROC_FLAGS_NO_WINDOW
7832#else
7833 0
7834#endif
7835 , &pid);
7836 }
7837#else /* !VBOX_WITH_HEADLESS */
7838 if (0)
7839 ;
7840#endif /* !VBOX_WITH_HEADLESS */
7841 else
7842 {
7843 RTEnvDestroy(env);
7844 return setError(E_INVALIDARG,
7845 tr("Invalid frontend name: '%s'"),
7846 strFrontend.c_str());
7847 }
7848
7849 RTEnvDestroy(env);
7850
7851 if (RT_FAILURE(vrc))
7852 return setError(VBOX_E_IPRT_ERROR,
7853 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7854 mUserData->s.strName.c_str(), vrc);
7855
7856 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7857
7858 /*
7859 * Note that we don't release the lock here before calling the client,
7860 * because it doesn't need to call us back if called with a NULL argument.
7861 * Releasing the lock here is dangerous because we didn't prepare the
7862 * launch data yet, but the client we've just started may happen to be
7863 * too fast and call openSession() that will fail (because of PID, etc.),
7864 * so that the Machine will never get out of the Spawning session state.
7865 */
7866
7867 /* inform the session that it will be a remote one */
7868 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7869 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7870 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7871
7872 if (FAILED(rc))
7873 {
7874 /* restore the session state */
7875 mData->mSession.mState = SessionState_Unlocked;
7876 /* The failure may occur w/o any error info (from RPC), so provide one */
7877 return setError(VBOX_E_VM_ERROR,
7878 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7879 }
7880
7881 /* attach launch data to the machine */
7882 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7883 mData->mSession.mRemoteControls.push_back(aControl);
7884 mData->mSession.mProgress = aProgress;
7885 mData->mSession.mPID = pid;
7886 mData->mSession.mState = SessionState_Spawning;
7887 mData->mSession.mType = strFrontend;
7888
7889 LogFlowThisFuncLeave();
7890 return S_OK;
7891}
7892
7893/**
7894 * Returns @c true if the given machine has an open direct session and returns
7895 * the session machine instance and additional session data (on some platforms)
7896 * if so.
7897 *
7898 * Note that when the method returns @c false, the arguments remain unchanged.
7899 *
7900 * @param aMachine Session machine object.
7901 * @param aControl Direct session control object (optional).
7902 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7903 *
7904 * @note locks this object for reading.
7905 */
7906#if defined(RT_OS_WINDOWS)
7907bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7908 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7909 HANDLE *aIPCSem /*= NULL*/,
7910 bool aAllowClosing /*= false*/)
7911#elif defined(RT_OS_OS2)
7912bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7913 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7914 HMTX *aIPCSem /*= NULL*/,
7915 bool aAllowClosing /*= false*/)
7916#else
7917bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7918 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7919 bool aAllowClosing /*= false*/)
7920#endif
7921{
7922 AutoLimitedCaller autoCaller(this);
7923 AssertComRCReturn(autoCaller.rc(), false);
7924
7925 /* just return false for inaccessible machines */
7926 if (autoCaller.state() != Ready)
7927 return false;
7928
7929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7930
7931 if ( mData->mSession.mState == SessionState_Locked
7932 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7933 )
7934 {
7935 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7936
7937 aMachine = mData->mSession.mMachine;
7938
7939 if (aControl != NULL)
7940 *aControl = mData->mSession.mDirectControl;
7941
7942#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7943 /* Additional session data */
7944 if (aIPCSem != NULL)
7945 *aIPCSem = aMachine->mIPCSem;
7946#endif
7947 return true;
7948 }
7949
7950 return false;
7951}
7952
7953/**
7954 * Returns @c true if the given machine has an spawning direct session and
7955 * returns and additional session data (on some platforms) if so.
7956 *
7957 * Note that when the method returns @c false, the arguments remain unchanged.
7958 *
7959 * @param aPID PID of the spawned direct session process.
7960 *
7961 * @note locks this object for reading.
7962 */
7963#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7964bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7965#else
7966bool Machine::isSessionSpawning()
7967#endif
7968{
7969 AutoLimitedCaller autoCaller(this);
7970 AssertComRCReturn(autoCaller.rc(), false);
7971
7972 /* just return false for inaccessible machines */
7973 if (autoCaller.state() != Ready)
7974 return false;
7975
7976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7977
7978 if (mData->mSession.mState == SessionState_Spawning)
7979 {
7980#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7981 /* Additional session data */
7982 if (aPID != NULL)
7983 {
7984 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7985 *aPID = mData->mSession.mPID;
7986 }
7987#endif
7988 return true;
7989 }
7990
7991 return false;
7992}
7993
7994/**
7995 * Called from the client watcher thread to check for unexpected client process
7996 * death during Session_Spawning state (e.g. before it successfully opened a
7997 * direct session).
7998 *
7999 * On Win32 and on OS/2, this method is called only when we've got the
8000 * direct client's process termination notification, so it always returns @c
8001 * true.
8002 *
8003 * On other platforms, this method returns @c true if the client process is
8004 * terminated and @c false if it's still alive.
8005 *
8006 * @note Locks this object for writing.
8007 */
8008bool Machine::checkForSpawnFailure()
8009{
8010 AutoCaller autoCaller(this);
8011 if (!autoCaller.isOk())
8012 {
8013 /* nothing to do */
8014 LogFlowThisFunc(("Already uninitialized!\n"));
8015 return true;
8016 }
8017
8018 /* VirtualBox::addProcessToReap() needs a write lock */
8019 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
8020
8021 if (mData->mSession.mState != SessionState_Spawning)
8022 {
8023 /* nothing to do */
8024 LogFlowThisFunc(("Not spawning any more!\n"));
8025 return true;
8026 }
8027
8028 HRESULT rc = S_OK;
8029
8030#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8031
8032 /* the process was already unexpectedly terminated, we just need to set an
8033 * error and finalize session spawning */
8034 rc = setError(E_FAIL,
8035 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
8036 getName().c_str());
8037#else
8038
8039 /* PID not yet initialized, skip check. */
8040 if (mData->mSession.mPID == NIL_RTPROCESS)
8041 return false;
8042
8043 RTPROCSTATUS status;
8044 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
8045 &status);
8046
8047 if (vrc != VERR_PROCESS_RUNNING)
8048 {
8049 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8050 rc = setError(E_FAIL,
8051 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8052 getName().c_str(), status.iStatus);
8053 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8054 rc = setError(E_FAIL,
8055 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8056 getName().c_str(), status.iStatus);
8057 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8058 rc = setError(E_FAIL,
8059 tr("The virtual machine '%s' has terminated abnormally"),
8060 getName().c_str(), status.iStatus);
8061 else
8062 rc = setError(E_FAIL,
8063 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8064 getName().c_str(), rc);
8065 }
8066
8067#endif
8068
8069 if (FAILED(rc))
8070 {
8071 /* Close the remote session, remove the remote control from the list
8072 * and reset session state to Closed (@note keep the code in sync with
8073 * the relevant part in checkForSpawnFailure()). */
8074
8075 Assert(mData->mSession.mRemoteControls.size() == 1);
8076 if (mData->mSession.mRemoteControls.size() == 1)
8077 {
8078 ErrorInfoKeeper eik;
8079 mData->mSession.mRemoteControls.front()->Uninitialize();
8080 }
8081
8082 mData->mSession.mRemoteControls.clear();
8083 mData->mSession.mState = SessionState_Unlocked;
8084
8085 /* finalize the progress after setting the state */
8086 if (!mData->mSession.mProgress.isNull())
8087 {
8088 mData->mSession.mProgress->notifyComplete(rc);
8089 mData->mSession.mProgress.setNull();
8090 }
8091
8092 mParent->addProcessToReap(mData->mSession.mPID);
8093 mData->mSession.mPID = NIL_RTPROCESS;
8094
8095 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8096 return true;
8097 }
8098
8099 return false;
8100}
8101
8102/**
8103 * Checks whether the machine can be registered. If so, commits and saves
8104 * all settings.
8105 *
8106 * @note Must be called from mParent's write lock. Locks this object and
8107 * children for writing.
8108 */
8109HRESULT Machine::prepareRegister()
8110{
8111 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8112
8113 AutoLimitedCaller autoCaller(this);
8114 AssertComRCReturnRC(autoCaller.rc());
8115
8116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8117
8118 /* wait for state dependents to drop to zero */
8119 ensureNoStateDependencies();
8120
8121 if (!mData->mAccessible)
8122 return setError(VBOX_E_INVALID_OBJECT_STATE,
8123 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8124 mUserData->s.strName.c_str(),
8125 mData->mUuid.toString().c_str());
8126
8127 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8128
8129 if (mData->mRegistered)
8130 return setError(VBOX_E_INVALID_OBJECT_STATE,
8131 tr("The machine '%s' with UUID {%s} is already registered"),
8132 mUserData->s.strName.c_str(),
8133 mData->mUuid.toString().c_str());
8134
8135 HRESULT rc = S_OK;
8136
8137 // Ensure the settings are saved. If we are going to be registered and
8138 // no config file exists yet, create it by calling saveSettings() too.
8139 if ( (mData->flModifications)
8140 || (!mData->pMachineConfigFile->fileExists())
8141 )
8142 {
8143 rc = saveSettings(NULL);
8144 // no need to check whether VirtualBox.xml needs saving too since
8145 // we can't have a machine XML file rename pending
8146 if (FAILED(rc)) return rc;
8147 }
8148
8149 /* more config checking goes here */
8150
8151 if (SUCCEEDED(rc))
8152 {
8153 /* we may have had implicit modifications we want to fix on success */
8154 commit();
8155
8156 mData->mRegistered = true;
8157 }
8158 else
8159 {
8160 /* we may have had implicit modifications we want to cancel on failure*/
8161 rollback(false /* aNotify */);
8162 }
8163
8164 return rc;
8165}
8166
8167/**
8168 * Increases the number of objects dependent on the machine state or on the
8169 * registered state. Guarantees that these two states will not change at least
8170 * until #releaseStateDependency() is called.
8171 *
8172 * Depending on the @a aDepType value, additional state checks may be made.
8173 * These checks will set extended error info on failure. See
8174 * #checkStateDependency() for more info.
8175 *
8176 * If this method returns a failure, the dependency is not added and the caller
8177 * is not allowed to rely on any particular machine state or registration state
8178 * value and may return the failed result code to the upper level.
8179 *
8180 * @param aDepType Dependency type to add.
8181 * @param aState Current machine state (NULL if not interested).
8182 * @param aRegistered Current registered state (NULL if not interested).
8183 *
8184 * @note Locks this object for writing.
8185 */
8186HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8187 MachineState_T *aState /* = NULL */,
8188 BOOL *aRegistered /* = NULL */)
8189{
8190 AutoCaller autoCaller(this);
8191 AssertComRCReturnRC(autoCaller.rc());
8192
8193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8194
8195 HRESULT rc = checkStateDependency(aDepType);
8196 if (FAILED(rc)) return rc;
8197
8198 {
8199 if (mData->mMachineStateChangePending != 0)
8200 {
8201 /* ensureNoStateDependencies() is waiting for state dependencies to
8202 * drop to zero so don't add more. It may make sense to wait a bit
8203 * and retry before reporting an error (since the pending state
8204 * transition should be really quick) but let's just assert for
8205 * now to see if it ever happens on practice. */
8206
8207 AssertFailed();
8208
8209 return setError(E_ACCESSDENIED,
8210 tr("Machine state change is in progress. Please retry the operation later."));
8211 }
8212
8213 ++mData->mMachineStateDeps;
8214 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8215 }
8216
8217 if (aState)
8218 *aState = mData->mMachineState;
8219 if (aRegistered)
8220 *aRegistered = mData->mRegistered;
8221
8222 return S_OK;
8223}
8224
8225/**
8226 * Decreases the number of objects dependent on the machine state.
8227 * Must always complete the #addStateDependency() call after the state
8228 * dependency is no more necessary.
8229 */
8230void Machine::releaseStateDependency()
8231{
8232 AutoCaller autoCaller(this);
8233 AssertComRCReturnVoid(autoCaller.rc());
8234
8235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8236
8237 /* releaseStateDependency() w/o addStateDependency()? */
8238 AssertReturnVoid(mData->mMachineStateDeps != 0);
8239 -- mData->mMachineStateDeps;
8240
8241 if (mData->mMachineStateDeps == 0)
8242 {
8243 /* inform ensureNoStateDependencies() that there are no more deps */
8244 if (mData->mMachineStateChangePending != 0)
8245 {
8246 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8247 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8248 }
8249 }
8250}
8251
8252Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8253{
8254 /* start with nothing found */
8255 Utf8Str strResult("");
8256
8257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8258
8259 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8260 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8261 // found:
8262 strResult = it->second; // source is a Utf8Str
8263
8264 return strResult;
8265}
8266
8267// protected methods
8268/////////////////////////////////////////////////////////////////////////////
8269
8270/**
8271 * Performs machine state checks based on the @a aDepType value. If a check
8272 * fails, this method will set extended error info, otherwise it will return
8273 * S_OK. It is supposed, that on failure, the caller will immediately return
8274 * the return value of this method to the upper level.
8275 *
8276 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8277 *
8278 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8279 * current state of this machine object allows to change settings of the
8280 * machine (i.e. the machine is not registered, or registered but not running
8281 * and not saved). It is useful to call this method from Machine setters
8282 * before performing any change.
8283 *
8284 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8285 * as for MutableStateDep except that if the machine is saved, S_OK is also
8286 * returned. This is useful in setters which allow changing machine
8287 * properties when it is in the saved state.
8288 *
8289 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8290 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8291 * Aborted).
8292 *
8293 * @param aDepType Dependency type to check.
8294 *
8295 * @note Non Machine based classes should use #addStateDependency() and
8296 * #releaseStateDependency() methods or the smart AutoStateDependency
8297 * template.
8298 *
8299 * @note This method must be called from under this object's read or write
8300 * lock.
8301 */
8302HRESULT Machine::checkStateDependency(StateDependency aDepType)
8303{
8304 switch (aDepType)
8305 {
8306 case AnyStateDep:
8307 {
8308 break;
8309 }
8310 case MutableStateDep:
8311 {
8312 if ( mData->mRegistered
8313 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8314 || ( mData->mMachineState != MachineState_Paused
8315 && mData->mMachineState != MachineState_Running
8316 && mData->mMachineState != MachineState_Aborted
8317 && mData->mMachineState != MachineState_Teleported
8318 && mData->mMachineState != MachineState_PoweredOff
8319 )
8320 )
8321 )
8322 return setError(VBOX_E_INVALID_VM_STATE,
8323 tr("The machine is not mutable (state is %s)"),
8324 Global::stringifyMachineState(mData->mMachineState));
8325 break;
8326 }
8327 case MutableOrSavedStateDep:
8328 {
8329 if ( mData->mRegistered
8330 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8331 || ( mData->mMachineState != MachineState_Paused
8332 && mData->mMachineState != MachineState_Running
8333 && mData->mMachineState != MachineState_Aborted
8334 && mData->mMachineState != MachineState_Teleported
8335 && mData->mMachineState != MachineState_Saved
8336 && mData->mMachineState != MachineState_PoweredOff
8337 )
8338 )
8339 )
8340 return setError(VBOX_E_INVALID_VM_STATE,
8341 tr("The machine is not mutable (state is %s)"),
8342 Global::stringifyMachineState(mData->mMachineState));
8343 break;
8344 }
8345 case OfflineStateDep:
8346 {
8347 if ( mData->mRegistered
8348 && ( !isSessionMachine()
8349 || ( mData->mMachineState != MachineState_PoweredOff
8350 && mData->mMachineState != MachineState_Saved
8351 && mData->mMachineState != MachineState_Aborted
8352 && mData->mMachineState != MachineState_Teleported
8353 )
8354 )
8355 )
8356 return setError(VBOX_E_INVALID_VM_STATE,
8357 tr("The machine is not offline (state is %s)"),
8358 Global::stringifyMachineState(mData->mMachineState));
8359 break;
8360 }
8361 }
8362
8363 return S_OK;
8364}
8365
8366/**
8367 * Helper to initialize all associated child objects and allocate data
8368 * structures.
8369 *
8370 * This method must be called as a part of the object's initialization procedure
8371 * (usually done in the #init() method).
8372 *
8373 * @note Must be called only from #init() or from #registeredInit().
8374 */
8375HRESULT Machine::initDataAndChildObjects()
8376{
8377 AutoCaller autoCaller(this);
8378 AssertComRCReturnRC(autoCaller.rc());
8379 AssertComRCReturn(autoCaller.state() == InInit ||
8380 autoCaller.state() == Limited, E_FAIL);
8381
8382 AssertReturn(!mData->mAccessible, E_FAIL);
8383
8384 /* allocate data structures */
8385 mSSData.allocate();
8386 mUserData.allocate();
8387 mHWData.allocate();
8388 mMediaData.allocate();
8389 mStorageControllers.allocate();
8390
8391 /* initialize mOSTypeId */
8392 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8393
8394 /* create associated BIOS settings object */
8395 unconst(mBIOSSettings).createObject();
8396 mBIOSSettings->init(this);
8397
8398 /* create an associated VRDE object (default is disabled) */
8399 unconst(mVRDEServer).createObject();
8400 mVRDEServer->init(this);
8401
8402 /* create associated serial port objects */
8403 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8404 {
8405 unconst(mSerialPorts[slot]).createObject();
8406 mSerialPorts[slot]->init(this, slot);
8407 }
8408
8409 /* create associated parallel port objects */
8410 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8411 {
8412 unconst(mParallelPorts[slot]).createObject();
8413 mParallelPorts[slot]->init(this, slot);
8414 }
8415
8416 /* create the audio adapter object (always present, default is disabled) */
8417 unconst(mAudioAdapter).createObject();
8418 mAudioAdapter->init(this);
8419
8420 /* create the USB controller object (always present, default is disabled) */
8421 unconst(mUSBController).createObject();
8422 mUSBController->init(this);
8423
8424 /* create associated network adapter objects */
8425 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8426 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8427 {
8428 unconst(mNetworkAdapters[slot]).createObject();
8429 mNetworkAdapters[slot]->init(this, slot);
8430 }
8431
8432 /* create the bandwidth control */
8433 unconst(mBandwidthControl).createObject();
8434 mBandwidthControl->init(this);
8435
8436 return S_OK;
8437}
8438
8439/**
8440 * Helper to uninitialize all associated child objects and to free all data
8441 * structures.
8442 *
8443 * This method must be called as a part of the object's uninitialization
8444 * procedure (usually done in the #uninit() method).
8445 *
8446 * @note Must be called only from #uninit() or from #registeredInit().
8447 */
8448void Machine::uninitDataAndChildObjects()
8449{
8450 AutoCaller autoCaller(this);
8451 AssertComRCReturnVoid(autoCaller.rc());
8452 AssertComRCReturnVoid( autoCaller.state() == InUninit
8453 || autoCaller.state() == Limited);
8454
8455 /* tell all our other child objects we've been uninitialized */
8456 if (mBandwidthControl)
8457 {
8458 mBandwidthControl->uninit();
8459 unconst(mBandwidthControl).setNull();
8460 }
8461
8462 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8463 {
8464 if (mNetworkAdapters[slot])
8465 {
8466 mNetworkAdapters[slot]->uninit();
8467 unconst(mNetworkAdapters[slot]).setNull();
8468 }
8469 }
8470
8471 if (mUSBController)
8472 {
8473 mUSBController->uninit();
8474 unconst(mUSBController).setNull();
8475 }
8476
8477 if (mAudioAdapter)
8478 {
8479 mAudioAdapter->uninit();
8480 unconst(mAudioAdapter).setNull();
8481 }
8482
8483 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8484 {
8485 if (mParallelPorts[slot])
8486 {
8487 mParallelPorts[slot]->uninit();
8488 unconst(mParallelPorts[slot]).setNull();
8489 }
8490 }
8491
8492 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8493 {
8494 if (mSerialPorts[slot])
8495 {
8496 mSerialPorts[slot]->uninit();
8497 unconst(mSerialPorts[slot]).setNull();
8498 }
8499 }
8500
8501 if (mVRDEServer)
8502 {
8503 mVRDEServer->uninit();
8504 unconst(mVRDEServer).setNull();
8505 }
8506
8507 if (mBIOSSettings)
8508 {
8509 mBIOSSettings->uninit();
8510 unconst(mBIOSSettings).setNull();
8511 }
8512
8513 /* Deassociate media (only when a real Machine or a SnapshotMachine
8514 * instance is uninitialized; SessionMachine instances refer to real
8515 * Machine media). This is necessary for a clean re-initialization of
8516 * the VM after successfully re-checking the accessibility state. Note
8517 * that in case of normal Machine or SnapshotMachine uninitialization (as
8518 * a result of unregistering or deleting the snapshot), outdated media
8519 * attachments will already be uninitialized and deleted, so this
8520 * code will not affect them. */
8521 if ( !!mMediaData
8522 && (!isSessionMachine())
8523 )
8524 {
8525 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8526 it != mMediaData->mAttachments.end();
8527 ++it)
8528 {
8529 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8530 if (pMedium.isNull())
8531 continue;
8532 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8533 AssertComRC(rc);
8534 }
8535 }
8536
8537 if (!isSessionMachine() && !isSnapshotMachine())
8538 {
8539 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8540 if (mData->mFirstSnapshot)
8541 {
8542 // snapshots tree is protected by machine write lock; strictly
8543 // this isn't necessary here since we're deleting the entire
8544 // machine, but otherwise we assert in Snapshot::uninit()
8545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8546 mData->mFirstSnapshot->uninit();
8547 mData->mFirstSnapshot.setNull();
8548 }
8549
8550 mData->mCurrentSnapshot.setNull();
8551 }
8552
8553 /* free data structures (the essential mData structure is not freed here
8554 * since it may be still in use) */
8555 mMediaData.free();
8556 mStorageControllers.free();
8557 mHWData.free();
8558 mUserData.free();
8559 mSSData.free();
8560}
8561
8562/**
8563 * Returns a pointer to the Machine object for this machine that acts like a
8564 * parent for complex machine data objects such as shared folders, etc.
8565 *
8566 * For primary Machine objects and for SnapshotMachine objects, returns this
8567 * object's pointer itself. For SessionMachine objects, returns the peer
8568 * (primary) machine pointer.
8569 */
8570Machine* Machine::getMachine()
8571{
8572 if (isSessionMachine())
8573 return (Machine*)mPeer;
8574 return this;
8575}
8576
8577/**
8578 * Makes sure that there are no machine state dependents. If necessary, waits
8579 * for the number of dependents to drop to zero.
8580 *
8581 * Make sure this method is called from under this object's write lock to
8582 * guarantee that no new dependents may be added when this method returns
8583 * control to the caller.
8584 *
8585 * @note Locks this object for writing. The lock will be released while waiting
8586 * (if necessary).
8587 *
8588 * @warning To be used only in methods that change the machine state!
8589 */
8590void Machine::ensureNoStateDependencies()
8591{
8592 AssertReturnVoid(isWriteLockOnCurrentThread());
8593
8594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8595
8596 /* Wait for all state dependents if necessary */
8597 if (mData->mMachineStateDeps != 0)
8598 {
8599 /* lazy semaphore creation */
8600 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8601 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8602
8603 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8604 mData->mMachineStateDeps));
8605
8606 ++mData->mMachineStateChangePending;
8607
8608 /* reset the semaphore before waiting, the last dependent will signal
8609 * it */
8610 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8611
8612 alock.release();
8613
8614 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8615
8616 alock.acquire();
8617
8618 -- mData->mMachineStateChangePending;
8619 }
8620}
8621
8622/**
8623 * Changes the machine state and informs callbacks.
8624 *
8625 * This method is not intended to fail so it either returns S_OK or asserts (and
8626 * returns a failure).
8627 *
8628 * @note Locks this object for writing.
8629 */
8630HRESULT Machine::setMachineState(MachineState_T aMachineState)
8631{
8632 LogFlowThisFuncEnter();
8633 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8634
8635 AutoCaller autoCaller(this);
8636 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8637
8638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8639
8640 /* wait for state dependents to drop to zero */
8641 ensureNoStateDependencies();
8642
8643 if (mData->mMachineState != aMachineState)
8644 {
8645 mData->mMachineState = aMachineState;
8646
8647 RTTimeNow(&mData->mLastStateChange);
8648
8649 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8650 }
8651
8652 LogFlowThisFuncLeave();
8653 return S_OK;
8654}
8655
8656/**
8657 * Searches for a shared folder with the given logical name
8658 * in the collection of shared folders.
8659 *
8660 * @param aName logical name of the shared folder
8661 * @param aSharedFolder where to return the found object
8662 * @param aSetError whether to set the error info if the folder is
8663 * not found
8664 * @return
8665 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8666 *
8667 * @note
8668 * must be called from under the object's lock!
8669 */
8670HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8671 ComObjPtr<SharedFolder> &aSharedFolder,
8672 bool aSetError /* = false */)
8673{
8674 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8675 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8676 it != mHWData->mSharedFolders.end();
8677 ++it)
8678 {
8679 SharedFolder *pSF = *it;
8680 AutoCaller autoCaller(pSF);
8681 if (pSF->getName() == aName)
8682 {
8683 aSharedFolder = pSF;
8684 rc = S_OK;
8685 break;
8686 }
8687 }
8688
8689 if (aSetError && FAILED(rc))
8690 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8691
8692 return rc;
8693}
8694
8695/**
8696 * Initializes all machine instance data from the given settings structures
8697 * from XML. The exception is the machine UUID which needs special handling
8698 * depending on the caller's use case, so the caller needs to set that herself.
8699 *
8700 * This gets called in several contexts during machine initialization:
8701 *
8702 * -- When machine XML exists on disk already and needs to be loaded into memory,
8703 * for example, from registeredInit() to load all registered machines on
8704 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8705 * attached to the machine should be part of some media registry already.
8706 *
8707 * -- During OVF import, when a machine config has been constructed from an
8708 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8709 * ensure that the media listed as attachments in the config (which have
8710 * been imported from the OVF) receive the correct registry ID.
8711 *
8712 * -- During VM cloning.
8713 *
8714 * @param config Machine settings from XML.
8715 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8716 * @return
8717 */
8718HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8719 const Guid *puuidRegistry)
8720{
8721 // copy name, description, OS type, teleporter, UTC etc.
8722 #define DECODE_STR_MAX _1M
8723 mUserData->s = config.machineUserData;
8724
8725 // Decode the Icon overide data from config userdata and set onto Machine.
8726 const char* pszStr = config.machineUserData.ovIcon.c_str();
8727 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8728 if (cbOut > DECODE_STR_MAX)
8729 return setError(E_FAIL,
8730 tr("Icon Data too long.'%d' > '%d'"),
8731 cbOut,
8732 DECODE_STR_MAX);
8733 com::SafeArray<BYTE> iconByte(cbOut);
8734 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8735 if (FAILED(rc))
8736 return setError(E_FAIL,
8737 tr("Failure to Decode Icon Data. '%s' (%d)"),
8738 pszStr,
8739 rc);
8740 COMSETTER(Icon)(ComSafeArrayAsInParam(iconByte));
8741
8742 // look up the object by Id to check it is valid
8743 ComPtr<IGuestOSType> guestOSType;
8744 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8745 guestOSType.asOutParam());
8746 if (FAILED(rc)) return rc;
8747
8748 // stateFile (optional)
8749 if (config.strStateFile.isEmpty())
8750 mSSData->strStateFilePath.setNull();
8751 else
8752 {
8753 Utf8Str stateFilePathFull(config.strStateFile);
8754 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8755 if (RT_FAILURE(vrc))
8756 return setError(E_FAIL,
8757 tr("Invalid saved state file path '%s' (%Rrc)"),
8758 config.strStateFile.c_str(),
8759 vrc);
8760 mSSData->strStateFilePath = stateFilePathFull;
8761 }
8762
8763 // snapshot folder needs special processing so set it again
8764 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8765 if (FAILED(rc)) return rc;
8766
8767 /* Copy the extra data items (Not in any case config is already the same as
8768 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8769 * make sure the extra data map is copied). */
8770 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8771
8772 /* currentStateModified (optional, default is true) */
8773 mData->mCurrentStateModified = config.fCurrentStateModified;
8774
8775 mData->mLastStateChange = config.timeLastStateChange;
8776
8777 /*
8778 * note: all mUserData members must be assigned prior this point because
8779 * we need to commit changes in order to let mUserData be shared by all
8780 * snapshot machine instances.
8781 */
8782 mUserData.commitCopy();
8783
8784 // machine registry, if present (must be loaded before snapshots)
8785 if (config.canHaveOwnMediaRegistry())
8786 {
8787 // determine machine folder
8788 Utf8Str strMachineFolder = getSettingsFileFull();
8789 strMachineFolder.stripFilename();
8790 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8791 config.mediaRegistry,
8792 strMachineFolder);
8793 if (FAILED(rc)) return rc;
8794 }
8795
8796 /* Snapshot node (optional) */
8797 size_t cRootSnapshots;
8798 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8799 {
8800 // there must be only one root snapshot
8801 Assert(cRootSnapshots == 1);
8802
8803 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8804
8805 rc = loadSnapshot(snap,
8806 config.uuidCurrentSnapshot,
8807 NULL); // no parent == first snapshot
8808 if (FAILED(rc)) return rc;
8809 }
8810
8811 // hardware data
8812 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8813 if (FAILED(rc)) return rc;
8814
8815 // load storage controllers
8816 rc = loadStorageControllers(config.storageMachine,
8817 puuidRegistry,
8818 NULL /* puuidSnapshot */);
8819 if (FAILED(rc)) return rc;
8820
8821 /*
8822 * NOTE: the assignment below must be the last thing to do,
8823 * otherwise it will be not possible to change the settings
8824 * somewhere in the code above because all setters will be
8825 * blocked by checkStateDependency(MutableStateDep).
8826 */
8827
8828 /* set the machine state to Aborted or Saved when appropriate */
8829 if (config.fAborted)
8830 {
8831 mSSData->strStateFilePath.setNull();
8832
8833 /* no need to use setMachineState() during init() */
8834 mData->mMachineState = MachineState_Aborted;
8835 }
8836 else if (!mSSData->strStateFilePath.isEmpty())
8837 {
8838 /* no need to use setMachineState() during init() */
8839 mData->mMachineState = MachineState_Saved;
8840 }
8841
8842 // after loading settings, we are no longer different from the XML on disk
8843 mData->flModifications = 0;
8844
8845 return S_OK;
8846}
8847
8848/**
8849 * Recursively loads all snapshots starting from the given.
8850 *
8851 * @param aNode <Snapshot> node.
8852 * @param aCurSnapshotId Current snapshot ID from the settings file.
8853 * @param aParentSnapshot Parent snapshot.
8854 */
8855HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8856 const Guid &aCurSnapshotId,
8857 Snapshot *aParentSnapshot)
8858{
8859 AssertReturn(!isSnapshotMachine(), E_FAIL);
8860 AssertReturn(!isSessionMachine(), E_FAIL);
8861
8862 HRESULT rc = S_OK;
8863
8864 Utf8Str strStateFile;
8865 if (!data.strStateFile.isEmpty())
8866 {
8867 /* optional */
8868 strStateFile = data.strStateFile;
8869 int vrc = calculateFullPath(strStateFile, strStateFile);
8870 if (RT_FAILURE(vrc))
8871 return setError(E_FAIL,
8872 tr("Invalid saved state file path '%s' (%Rrc)"),
8873 strStateFile.c_str(),
8874 vrc);
8875 }
8876
8877 /* create a snapshot machine object */
8878 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8879 pSnapshotMachine.createObject();
8880 rc = pSnapshotMachine->initFromSettings(this,
8881 data.hardware,
8882 &data.debugging,
8883 &data.autostart,
8884 data.storage,
8885 data.uuid.ref(),
8886 strStateFile);
8887 if (FAILED(rc)) return rc;
8888
8889 /* create a snapshot object */
8890 ComObjPtr<Snapshot> pSnapshot;
8891 pSnapshot.createObject();
8892 /* initialize the snapshot */
8893 rc = pSnapshot->init(mParent, // VirtualBox object
8894 data.uuid,
8895 data.strName,
8896 data.strDescription,
8897 data.timestamp,
8898 pSnapshotMachine,
8899 aParentSnapshot);
8900 if (FAILED(rc)) return rc;
8901
8902 /* memorize the first snapshot if necessary */
8903 if (!mData->mFirstSnapshot)
8904 mData->mFirstSnapshot = pSnapshot;
8905
8906 /* memorize the current snapshot when appropriate */
8907 if ( !mData->mCurrentSnapshot
8908 && pSnapshot->getId() == aCurSnapshotId
8909 )
8910 mData->mCurrentSnapshot = pSnapshot;
8911
8912 // now create the children
8913 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8914 it != data.llChildSnapshots.end();
8915 ++it)
8916 {
8917 const settings::Snapshot &childData = *it;
8918 // recurse
8919 rc = loadSnapshot(childData,
8920 aCurSnapshotId,
8921 pSnapshot); // parent = the one we created above
8922 if (FAILED(rc)) return rc;
8923 }
8924
8925 return rc;
8926}
8927
8928/**
8929 * Loads settings into mHWData.
8930 *
8931 * @param data Reference to the hardware settings.
8932 * @param pDbg Pointer to the debugging settings.
8933 * @param pAutostart Pointer to the autostart settings.
8934 */
8935HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8936 const settings::Autostart *pAutostart)
8937{
8938 AssertReturn(!isSessionMachine(), E_FAIL);
8939
8940 HRESULT rc = S_OK;
8941
8942 try
8943 {
8944 /* The hardware version attribute (optional). */
8945 mHWData->mHWVersion = data.strVersion;
8946 mHWData->mHardwareUUID = data.uuid;
8947
8948 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8949 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8950 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8951 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8952 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8953 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8954 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8955 mHWData->mPAEEnabled = data.fPAE;
8956 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8957 mHWData->mLongMode = data.enmLongMode;
8958 mHWData->mCPUCount = data.cCPUs;
8959 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8960 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8961
8962 // cpu
8963 if (mHWData->mCPUHotPlugEnabled)
8964 {
8965 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8966 it != data.llCpus.end();
8967 ++it)
8968 {
8969 const settings::Cpu &cpu = *it;
8970
8971 mHWData->mCPUAttached[cpu.ulId] = true;
8972 }
8973 }
8974
8975 // cpuid leafs
8976 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8977 it != data.llCpuIdLeafs.end();
8978 ++it)
8979 {
8980 const settings::CpuIdLeaf &leaf = *it;
8981
8982 switch (leaf.ulId)
8983 {
8984 case 0x0:
8985 case 0x1:
8986 case 0x2:
8987 case 0x3:
8988 case 0x4:
8989 case 0x5:
8990 case 0x6:
8991 case 0x7:
8992 case 0x8:
8993 case 0x9:
8994 case 0xA:
8995 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8996 break;
8997
8998 case 0x80000000:
8999 case 0x80000001:
9000 case 0x80000002:
9001 case 0x80000003:
9002 case 0x80000004:
9003 case 0x80000005:
9004 case 0x80000006:
9005 case 0x80000007:
9006 case 0x80000008:
9007 case 0x80000009:
9008 case 0x8000000A:
9009 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9010 break;
9011
9012 default:
9013 /* just ignore */
9014 break;
9015 }
9016 }
9017
9018 mHWData->mMemorySize = data.ulMemorySizeMB;
9019 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9020
9021 // boot order
9022 for (size_t i = 0;
9023 i < RT_ELEMENTS(mHWData->mBootOrder);
9024 i++)
9025 {
9026 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9027 if (it == data.mapBootOrder.end())
9028 mHWData->mBootOrder[i] = DeviceType_Null;
9029 else
9030 mHWData->mBootOrder[i] = it->second;
9031 }
9032
9033 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9034 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9035 mHWData->mMonitorCount = data.cMonitors;
9036 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9037 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9038 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9039 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9040 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9041 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9042 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9043 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9044 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9045 mHWData->mVideoCaptureFps = data.ulVideoCaptureFps;
9046 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
9047 mHWData->mFirmwareType = data.firmwareType;
9048 mHWData->mPointingHIDType = data.pointingHIDType;
9049 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9050 mHWData->mChipsetType = data.chipsetType;
9051 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9052 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9053 mHWData->mHPETEnabled = data.fHPETEnabled;
9054
9055 /* VRDEServer */
9056 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9057 if (FAILED(rc)) return rc;
9058
9059 /* BIOS */
9060 rc = mBIOSSettings->loadSettings(data.biosSettings);
9061 if (FAILED(rc)) return rc;
9062
9063 // Bandwidth control (must come before network adapters)
9064 rc = mBandwidthControl->loadSettings(data.ioSettings);
9065 if (FAILED(rc)) return rc;
9066
9067 /* USB Controller */
9068 rc = mUSBController->loadSettings(data.usbController);
9069 if (FAILED(rc)) return rc;
9070
9071 // network adapters
9072 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9073 uint32_t oldCount = mNetworkAdapters.size();
9074 if (newCount > oldCount)
9075 {
9076 mNetworkAdapters.resize(newCount);
9077 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9078 {
9079 unconst(mNetworkAdapters[slot]).createObject();
9080 mNetworkAdapters[slot]->init(this, slot);
9081 }
9082 }
9083 else if (newCount < oldCount)
9084 mNetworkAdapters.resize(newCount);
9085 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9086 it != data.llNetworkAdapters.end();
9087 ++it)
9088 {
9089 const settings::NetworkAdapter &nic = *it;
9090
9091 /* slot unicity is guaranteed by XML Schema */
9092 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9093 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9094 if (FAILED(rc)) return rc;
9095 }
9096
9097 // serial ports
9098 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9099 it != data.llSerialPorts.end();
9100 ++it)
9101 {
9102 const settings::SerialPort &s = *it;
9103
9104 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9105 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9106 if (FAILED(rc)) return rc;
9107 }
9108
9109 // parallel ports (optional)
9110 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9111 it != data.llParallelPorts.end();
9112 ++it)
9113 {
9114 const settings::ParallelPort &p = *it;
9115
9116 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9117 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9118 if (FAILED(rc)) return rc;
9119 }
9120
9121 /* AudioAdapter */
9122 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9123 if (FAILED(rc)) return rc;
9124
9125 /* Shared folders */
9126 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9127 it != data.llSharedFolders.end();
9128 ++it)
9129 {
9130 const settings::SharedFolder &sf = *it;
9131
9132 ComObjPtr<SharedFolder> sharedFolder;
9133 /* Check for double entries. Not allowed! */
9134 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9135 if (SUCCEEDED(rc))
9136 return setError(VBOX_E_OBJECT_IN_USE,
9137 tr("Shared folder named '%s' already exists"),
9138 sf.strName.c_str());
9139
9140 /* Create the new shared folder. Don't break on error. This will be
9141 * reported when the machine starts. */
9142 sharedFolder.createObject();
9143 rc = sharedFolder->init(getMachine(),
9144 sf.strName,
9145 sf.strHostPath,
9146 RT_BOOL(sf.fWritable),
9147 RT_BOOL(sf.fAutoMount),
9148 false /* fFailOnError */);
9149 if (FAILED(rc)) return rc;
9150 mHWData->mSharedFolders.push_back(sharedFolder);
9151 }
9152
9153 // Clipboard
9154 mHWData->mClipboardMode = data.clipboardMode;
9155
9156 // drag'n'drop
9157 mHWData->mDragAndDropMode = data.dragAndDropMode;
9158
9159 // guest settings
9160 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9161
9162 // IO settings
9163 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9164 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9165
9166 // Host PCI devices
9167 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9168 it != data.pciAttachments.end();
9169 ++it)
9170 {
9171 const settings::HostPCIDeviceAttachment &hpda = *it;
9172 ComObjPtr<PCIDeviceAttachment> pda;
9173
9174 pda.createObject();
9175 pda->loadSettings(this, hpda);
9176 mHWData->mPCIDeviceAssignments.push_back(pda);
9177 }
9178
9179 /*
9180 * (The following isn't really real hardware, but it lives in HWData
9181 * for reasons of convenience.)
9182 */
9183
9184#ifdef VBOX_WITH_GUEST_PROPS
9185 /* Guest properties (optional) */
9186 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9187 it != data.llGuestProperties.end();
9188 ++it)
9189 {
9190 const settings::GuestProperty &prop = *it;
9191 uint32_t fFlags = guestProp::NILFLAG;
9192 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9193 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9194 mHWData->mGuestProperties[prop.strName] = property;
9195 }
9196
9197 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9198#endif /* VBOX_WITH_GUEST_PROPS defined */
9199
9200 rc = loadDebugging(pDbg);
9201 if (FAILED(rc))
9202 return rc;
9203
9204 mHWData->mAutostart = *pAutostart;
9205
9206 /* default frontend */
9207 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9208 }
9209 catch(std::bad_alloc &)
9210 {
9211 return E_OUTOFMEMORY;
9212 }
9213
9214 AssertComRC(rc);
9215 return rc;
9216}
9217
9218/**
9219 * Called from Machine::loadHardware() to load the debugging settings of the
9220 * machine.
9221 *
9222 * @param pDbg Pointer to the settings.
9223 */
9224HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9225{
9226 mHWData->mDebugging = *pDbg;
9227 /* no more processing currently required, this will probably change. */
9228 return S_OK;
9229}
9230
9231/**
9232 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9233 *
9234 * @param data
9235 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9236 * @param puuidSnapshot
9237 * @return
9238 */
9239HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9240 const Guid *puuidRegistry,
9241 const Guid *puuidSnapshot)
9242{
9243 AssertReturn(!isSessionMachine(), E_FAIL);
9244
9245 HRESULT rc = S_OK;
9246
9247 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9248 it != data.llStorageControllers.end();
9249 ++it)
9250 {
9251 const settings::StorageController &ctlData = *it;
9252
9253 ComObjPtr<StorageController> pCtl;
9254 /* Try to find one with the name first. */
9255 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9256 if (SUCCEEDED(rc))
9257 return setError(VBOX_E_OBJECT_IN_USE,
9258 tr("Storage controller named '%s' already exists"),
9259 ctlData.strName.c_str());
9260
9261 pCtl.createObject();
9262 rc = pCtl->init(this,
9263 ctlData.strName,
9264 ctlData.storageBus,
9265 ctlData.ulInstance,
9266 ctlData.fBootable);
9267 if (FAILED(rc)) return rc;
9268
9269 mStorageControllers->push_back(pCtl);
9270
9271 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9272 if (FAILED(rc)) return rc;
9273
9274 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9275 if (FAILED(rc)) return rc;
9276
9277 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9278 if (FAILED(rc)) return rc;
9279
9280 /* Set IDE emulation settings (only for AHCI controller). */
9281 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9282 {
9283 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9284 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9285 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9286 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9287 )
9288 return rc;
9289 }
9290
9291 /* Load the attached devices now. */
9292 rc = loadStorageDevices(pCtl,
9293 ctlData,
9294 puuidRegistry,
9295 puuidSnapshot);
9296 if (FAILED(rc)) return rc;
9297 }
9298
9299 return S_OK;
9300}
9301
9302/**
9303 * Called from loadStorageControllers for a controller's devices.
9304 *
9305 * @param aStorageController
9306 * @param data
9307 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9308 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9309 * @return
9310 */
9311HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9312 const settings::StorageController &data,
9313 const Guid *puuidRegistry,
9314 const Guid *puuidSnapshot)
9315{
9316 HRESULT rc = S_OK;
9317
9318 /* paranoia: detect duplicate attachments */
9319 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9320 it != data.llAttachedDevices.end();
9321 ++it)
9322 {
9323 const settings::AttachedDevice &ad = *it;
9324
9325 for (settings::AttachedDevicesList::const_iterator it2 = it;
9326 it2 != data.llAttachedDevices.end();
9327 ++it2)
9328 {
9329 if (it == it2)
9330 continue;
9331
9332 const settings::AttachedDevice &ad2 = *it2;
9333
9334 if ( ad.lPort == ad2.lPort
9335 && ad.lDevice == ad2.lDevice)
9336 {
9337 return setError(E_FAIL,
9338 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9339 aStorageController->getName().c_str(),
9340 ad.lPort,
9341 ad.lDevice,
9342 mUserData->s.strName.c_str());
9343 }
9344 }
9345 }
9346
9347 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9348 it != data.llAttachedDevices.end();
9349 ++it)
9350 {
9351 const settings::AttachedDevice &dev = *it;
9352 ComObjPtr<Medium> medium;
9353
9354 switch (dev.deviceType)
9355 {
9356 case DeviceType_Floppy:
9357 case DeviceType_DVD:
9358 if (dev.strHostDriveSrc.isNotEmpty())
9359 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9360 else
9361 rc = mParent->findRemoveableMedium(dev.deviceType,
9362 dev.uuid,
9363 false /* fRefresh */,
9364 false /* aSetError */,
9365 medium);
9366 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9367 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9368 rc = S_OK;
9369 break;
9370
9371 case DeviceType_HardDisk:
9372 {
9373 /* find a hard disk by UUID */
9374 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9375 if (FAILED(rc))
9376 {
9377 if (isSnapshotMachine())
9378 {
9379 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9380 // so the user knows that the bad disk is in a snapshot somewhere
9381 com::ErrorInfo info;
9382 return setError(E_FAIL,
9383 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9384 puuidSnapshot->raw(),
9385 info.getText().raw());
9386 }
9387 else
9388 return rc;
9389 }
9390
9391 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9392
9393 if (medium->getType() == MediumType_Immutable)
9394 {
9395 if (isSnapshotMachine())
9396 return setError(E_FAIL,
9397 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9398 "of the virtual machine '%s' ('%s')"),
9399 medium->getLocationFull().c_str(),
9400 dev.uuid.raw(),
9401 puuidSnapshot->raw(),
9402 mUserData->s.strName.c_str(),
9403 mData->m_strConfigFileFull.c_str());
9404
9405 return setError(E_FAIL,
9406 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9407 medium->getLocationFull().c_str(),
9408 dev.uuid.raw(),
9409 mUserData->s.strName.c_str(),
9410 mData->m_strConfigFileFull.c_str());
9411 }
9412
9413 if (medium->getType() == MediumType_MultiAttach)
9414 {
9415 if (isSnapshotMachine())
9416 return setError(E_FAIL,
9417 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9418 "of the virtual machine '%s' ('%s')"),
9419 medium->getLocationFull().c_str(),
9420 dev.uuid.raw(),
9421 puuidSnapshot->raw(),
9422 mUserData->s.strName.c_str(),
9423 mData->m_strConfigFileFull.c_str());
9424
9425 return setError(E_FAIL,
9426 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9427 medium->getLocationFull().c_str(),
9428 dev.uuid.raw(),
9429 mUserData->s.strName.c_str(),
9430 mData->m_strConfigFileFull.c_str());
9431 }
9432
9433 if ( !isSnapshotMachine()
9434 && medium->getChildren().size() != 0
9435 )
9436 return setError(E_FAIL,
9437 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9438 "because it has %d differencing child hard disks"),
9439 medium->getLocationFull().c_str(),
9440 dev.uuid.raw(),
9441 mUserData->s.strName.c_str(),
9442 mData->m_strConfigFileFull.c_str(),
9443 medium->getChildren().size());
9444
9445 if (findAttachment(mMediaData->mAttachments,
9446 medium))
9447 return setError(E_FAIL,
9448 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9449 medium->getLocationFull().c_str(),
9450 dev.uuid.raw(),
9451 mUserData->s.strName.c_str(),
9452 mData->m_strConfigFileFull.c_str());
9453
9454 break;
9455 }
9456
9457 default:
9458 return setError(E_FAIL,
9459 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9460 medium->getLocationFull().c_str(),
9461 mUserData->s.strName.c_str(),
9462 mData->m_strConfigFileFull.c_str());
9463 }
9464
9465 if (FAILED(rc))
9466 break;
9467
9468 /* Bandwidth groups are loaded at this point. */
9469 ComObjPtr<BandwidthGroup> pBwGroup;
9470
9471 if (!dev.strBwGroup.isEmpty())
9472 {
9473 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9474 if (FAILED(rc))
9475 return setError(E_FAIL,
9476 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9477 medium->getLocationFull().c_str(),
9478 dev.strBwGroup.c_str(),
9479 mUserData->s.strName.c_str(),
9480 mData->m_strConfigFileFull.c_str());
9481 pBwGroup->reference();
9482 }
9483
9484 const Bstr controllerName = aStorageController->getName();
9485 ComObjPtr<MediumAttachment> pAttachment;
9486 pAttachment.createObject();
9487 rc = pAttachment->init(this,
9488 medium,
9489 controllerName,
9490 dev.lPort,
9491 dev.lDevice,
9492 dev.deviceType,
9493 false,
9494 dev.fPassThrough,
9495 dev.fTempEject,
9496 dev.fNonRotational,
9497 dev.fDiscard,
9498 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9499 if (FAILED(rc)) break;
9500
9501 /* associate the medium with this machine and snapshot */
9502 if (!medium.isNull())
9503 {
9504 AutoCaller medCaller(medium);
9505 if (FAILED(medCaller.rc())) return medCaller.rc();
9506 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9507
9508 if (isSnapshotMachine())
9509 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9510 else
9511 rc = medium->addBackReference(mData->mUuid);
9512 /* If the medium->addBackReference fails it sets an appropriate
9513 * error message, so no need to do any guesswork here. */
9514
9515 if (puuidRegistry)
9516 // caller wants registry ID to be set on all attached media (OVF import case)
9517 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9518 }
9519
9520 if (FAILED(rc))
9521 break;
9522
9523 /* back up mMediaData to let registeredInit() properly rollback on failure
9524 * (= limited accessibility) */
9525 setModified(IsModified_Storage);
9526 mMediaData.backup();
9527 mMediaData->mAttachments.push_back(pAttachment);
9528 }
9529
9530 return rc;
9531}
9532
9533/**
9534 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9535 *
9536 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9537 * @param aSnapshot where to return the found snapshot
9538 * @param aSetError true to set extended error info on failure
9539 */
9540HRESULT Machine::findSnapshotById(const Guid &aId,
9541 ComObjPtr<Snapshot> &aSnapshot,
9542 bool aSetError /* = false */)
9543{
9544 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9545
9546 if (!mData->mFirstSnapshot)
9547 {
9548 if (aSetError)
9549 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9550 return E_FAIL;
9551 }
9552
9553 if (aId.isZero())
9554 aSnapshot = mData->mFirstSnapshot;
9555 else
9556 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9557
9558 if (!aSnapshot)
9559 {
9560 if (aSetError)
9561 return setError(E_FAIL,
9562 tr("Could not find a snapshot with UUID {%s}"),
9563 aId.toString().c_str());
9564 return E_FAIL;
9565 }
9566
9567 return S_OK;
9568}
9569
9570/**
9571 * Returns the snapshot with the given name or fails of no such snapshot.
9572 *
9573 * @param aName snapshot name to find
9574 * @param aSnapshot where to return the found snapshot
9575 * @param aSetError true to set extended error info on failure
9576 */
9577HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9578 ComObjPtr<Snapshot> &aSnapshot,
9579 bool aSetError /* = false */)
9580{
9581 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9582
9583 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9584
9585 if (!mData->mFirstSnapshot)
9586 {
9587 if (aSetError)
9588 return setError(VBOX_E_OBJECT_NOT_FOUND,
9589 tr("This machine does not have any snapshots"));
9590 return VBOX_E_OBJECT_NOT_FOUND;
9591 }
9592
9593 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9594
9595 if (!aSnapshot)
9596 {
9597 if (aSetError)
9598 return setError(VBOX_E_OBJECT_NOT_FOUND,
9599 tr("Could not find a snapshot named '%s'"), strName.c_str());
9600 return VBOX_E_OBJECT_NOT_FOUND;
9601 }
9602
9603 return S_OK;
9604}
9605
9606/**
9607 * Returns a storage controller object with the given name.
9608 *
9609 * @param aName storage controller name to find
9610 * @param aStorageController where to return the found storage controller
9611 * @param aSetError true to set extended error info on failure
9612 */
9613HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9614 ComObjPtr<StorageController> &aStorageController,
9615 bool aSetError /* = false */)
9616{
9617 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9618
9619 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9620 it != mStorageControllers->end();
9621 ++it)
9622 {
9623 if ((*it)->getName() == aName)
9624 {
9625 aStorageController = (*it);
9626 return S_OK;
9627 }
9628 }
9629
9630 if (aSetError)
9631 return setError(VBOX_E_OBJECT_NOT_FOUND,
9632 tr("Could not find a storage controller named '%s'"),
9633 aName.c_str());
9634 return VBOX_E_OBJECT_NOT_FOUND;
9635}
9636
9637HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9638 MediaData::AttachmentList &atts)
9639{
9640 AutoCaller autoCaller(this);
9641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9642
9643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9644
9645 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9646 it != mMediaData->mAttachments.end();
9647 ++it)
9648 {
9649 const ComObjPtr<MediumAttachment> &pAtt = *it;
9650
9651 // should never happen, but deal with NULL pointers in the list.
9652 AssertStmt(!pAtt.isNull(), continue);
9653
9654 // getControllerName() needs caller+read lock
9655 AutoCaller autoAttCaller(pAtt);
9656 if (FAILED(autoAttCaller.rc()))
9657 {
9658 atts.clear();
9659 return autoAttCaller.rc();
9660 }
9661 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9662
9663 if (pAtt->getControllerName() == aName)
9664 atts.push_back(pAtt);
9665 }
9666
9667 return S_OK;
9668}
9669
9670/**
9671 * Helper for #saveSettings. Cares about renaming the settings directory and
9672 * file if the machine name was changed and about creating a new settings file
9673 * if this is a new machine.
9674 *
9675 * @note Must be never called directly but only from #saveSettings().
9676 */
9677HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9678{
9679 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9680
9681 HRESULT rc = S_OK;
9682
9683 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9684
9685 /// @todo need to handle primary group change, too
9686
9687 /* attempt to rename the settings file if machine name is changed */
9688 if ( mUserData->s.fNameSync
9689 && mUserData.isBackedUp()
9690 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9691 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9692 )
9693 {
9694 bool dirRenamed = false;
9695 bool fileRenamed = false;
9696
9697 Utf8Str configFile, newConfigFile;
9698 Utf8Str configFilePrev, newConfigFilePrev;
9699 Utf8Str configDir, newConfigDir;
9700
9701 do
9702 {
9703 int vrc = VINF_SUCCESS;
9704
9705 Utf8Str name = mUserData.backedUpData()->s.strName;
9706 Utf8Str newName = mUserData->s.strName;
9707 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9708 if (group == "/")
9709 group.setNull();
9710 Utf8Str newGroup = mUserData->s.llGroups.front();
9711 if (newGroup == "/")
9712 newGroup.setNull();
9713
9714 configFile = mData->m_strConfigFileFull;
9715
9716 /* first, rename the directory if it matches the group and machine name */
9717 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9718 group.c_str(), RTPATH_DELIMITER, name.c_str());
9719 /** @todo hack, make somehow use of ComposeMachineFilename */
9720 if (mUserData->s.fDirectoryIncludesUUID)
9721 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9722 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9723 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9724 /** @todo hack, make somehow use of ComposeMachineFilename */
9725 if (mUserData->s.fDirectoryIncludesUUID)
9726 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9727 configDir = configFile;
9728 configDir.stripFilename();
9729 newConfigDir = configDir;
9730 if ( configDir.length() >= groupPlusName.length()
9731 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9732 {
9733 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9734 Utf8Str newConfigBaseDir(newConfigDir);
9735 newConfigDir.append(newGroupPlusName);
9736 /* consistency: use \ if appropriate on the platform */
9737 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9738 /* new dir and old dir cannot be equal here because of 'if'
9739 * above and because name != newName */
9740 Assert(configDir != newConfigDir);
9741 if (!fSettingsFileIsNew)
9742 {
9743 /* perform real rename only if the machine is not new */
9744 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9745 if ( vrc == VERR_FILE_NOT_FOUND
9746 || vrc == VERR_PATH_NOT_FOUND)
9747 {
9748 /* create the parent directory, then retry renaming */
9749 Utf8Str parent(newConfigDir);
9750 parent.stripFilename();
9751 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9752 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9753 }
9754 if (RT_FAILURE(vrc))
9755 {
9756 rc = setError(E_FAIL,
9757 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9758 configDir.c_str(),
9759 newConfigDir.c_str(),
9760 vrc);
9761 break;
9762 }
9763 /* delete subdirectories which are no longer needed */
9764 Utf8Str dir(configDir);
9765 dir.stripFilename();
9766 while (dir != newConfigBaseDir && dir != ".")
9767 {
9768 vrc = RTDirRemove(dir.c_str());
9769 if (RT_FAILURE(vrc))
9770 break;
9771 dir.stripFilename();
9772 }
9773 dirRenamed = true;
9774 }
9775 }
9776
9777 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9778 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9779
9780 /* then try to rename the settings file itself */
9781 if (newConfigFile != configFile)
9782 {
9783 /* get the path to old settings file in renamed directory */
9784 configFile = Utf8StrFmt("%s%c%s",
9785 newConfigDir.c_str(),
9786 RTPATH_DELIMITER,
9787 RTPathFilename(configFile.c_str()));
9788 if (!fSettingsFileIsNew)
9789 {
9790 /* perform real rename only if the machine is not new */
9791 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9792 if (RT_FAILURE(vrc))
9793 {
9794 rc = setError(E_FAIL,
9795 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9796 configFile.c_str(),
9797 newConfigFile.c_str(),
9798 vrc);
9799 break;
9800 }
9801 fileRenamed = true;
9802 configFilePrev = configFile;
9803 configFilePrev += "-prev";
9804 newConfigFilePrev = newConfigFile;
9805 newConfigFilePrev += "-prev";
9806 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9807 }
9808 }
9809
9810 // update m_strConfigFileFull amd mConfigFile
9811 mData->m_strConfigFileFull = newConfigFile;
9812 // compute the relative path too
9813 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9814
9815 // store the old and new so that VirtualBox::saveSettings() can update
9816 // the media registry
9817 if ( mData->mRegistered
9818 && configDir != newConfigDir)
9819 {
9820 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9821
9822 if (pfNeedsGlobalSaveSettings)
9823 *pfNeedsGlobalSaveSettings = true;
9824 }
9825
9826 // in the saved state file path, replace the old directory with the new directory
9827 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9828 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9829
9830 // and do the same thing for the saved state file paths of all the online snapshots
9831 if (mData->mFirstSnapshot)
9832 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9833 newConfigDir.c_str());
9834 }
9835 while (0);
9836
9837 if (FAILED(rc))
9838 {
9839 /* silently try to rename everything back */
9840 if (fileRenamed)
9841 {
9842 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9843 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9844 }
9845 if (dirRenamed)
9846 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9847 }
9848
9849 if (FAILED(rc)) return rc;
9850 }
9851
9852 if (fSettingsFileIsNew)
9853 {
9854 /* create a virgin config file */
9855 int vrc = VINF_SUCCESS;
9856
9857 /* ensure the settings directory exists */
9858 Utf8Str path(mData->m_strConfigFileFull);
9859 path.stripFilename();
9860 if (!RTDirExists(path.c_str()))
9861 {
9862 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9863 if (RT_FAILURE(vrc))
9864 {
9865 return setError(E_FAIL,
9866 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9867 path.c_str(),
9868 vrc);
9869 }
9870 }
9871
9872 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9873 path = Utf8Str(mData->m_strConfigFileFull);
9874 RTFILE f = NIL_RTFILE;
9875 vrc = RTFileOpen(&f, path.c_str(),
9876 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9877 if (RT_FAILURE(vrc))
9878 return setError(E_FAIL,
9879 tr("Could not create the settings file '%s' (%Rrc)"),
9880 path.c_str(),
9881 vrc);
9882 RTFileClose(f);
9883 }
9884
9885 return rc;
9886}
9887
9888/**
9889 * Saves and commits machine data, user data and hardware data.
9890 *
9891 * Note that on failure, the data remains uncommitted.
9892 *
9893 * @a aFlags may combine the following flags:
9894 *
9895 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9896 * Used when saving settings after an operation that makes them 100%
9897 * correspond to the settings from the current snapshot.
9898 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9899 * #isReallyModified() returns false. This is necessary for cases when we
9900 * change machine data directly, not through the backup()/commit() mechanism.
9901 * - SaveS_Force: settings will be saved without doing a deep compare of the
9902 * settings structures. This is used when this is called because snapshots
9903 * have changed to avoid the overhead of the deep compare.
9904 *
9905 * @note Must be called from under this object's write lock. Locks children for
9906 * writing.
9907 *
9908 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9909 * initialized to false and that will be set to true by this function if
9910 * the caller must invoke VirtualBox::saveSettings() because the global
9911 * settings have changed. This will happen if a machine rename has been
9912 * saved and the global machine and media registries will therefore need
9913 * updating.
9914 */
9915HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9916 int aFlags /*= 0*/)
9917{
9918 LogFlowThisFuncEnter();
9919
9920 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9921
9922 /* make sure child objects are unable to modify the settings while we are
9923 * saving them */
9924 ensureNoStateDependencies();
9925
9926 AssertReturn(!isSnapshotMachine(),
9927 E_FAIL);
9928
9929 HRESULT rc = S_OK;
9930 bool fNeedsWrite = false;
9931
9932 /* First, prepare to save settings. It will care about renaming the
9933 * settings directory and file if the machine name was changed and about
9934 * creating a new settings file if this is a new machine. */
9935 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9936 if (FAILED(rc)) return rc;
9937
9938 // keep a pointer to the current settings structures
9939 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9940 settings::MachineConfigFile *pNewConfig = NULL;
9941
9942 try
9943 {
9944 // make a fresh one to have everyone write stuff into
9945 pNewConfig = new settings::MachineConfigFile(NULL);
9946 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9947
9948 // now go and copy all the settings data from COM to the settings structures
9949 // (this calles saveSettings() on all the COM objects in the machine)
9950 copyMachineDataToSettings(*pNewConfig);
9951
9952 if (aFlags & SaveS_ResetCurStateModified)
9953 {
9954 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9955 mData->mCurrentStateModified = FALSE;
9956 fNeedsWrite = true; // always, no need to compare
9957 }
9958 else if (aFlags & SaveS_Force)
9959 {
9960 fNeedsWrite = true; // always, no need to compare
9961 }
9962 else
9963 {
9964 if (!mData->mCurrentStateModified)
9965 {
9966 // do a deep compare of the settings that we just saved with the settings
9967 // previously stored in the config file; this invokes MachineConfigFile::operator==
9968 // which does a deep compare of all the settings, which is expensive but less expensive
9969 // than writing out XML in vain
9970 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9971
9972 // could still be modified if any settings changed
9973 mData->mCurrentStateModified = fAnySettingsChanged;
9974
9975 fNeedsWrite = fAnySettingsChanged;
9976 }
9977 else
9978 fNeedsWrite = true;
9979 }
9980
9981 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9982
9983 if (fNeedsWrite)
9984 // now spit it all out!
9985 pNewConfig->write(mData->m_strConfigFileFull);
9986
9987 mData->pMachineConfigFile = pNewConfig;
9988 delete pOldConfig;
9989 commit();
9990
9991 // after saving settings, we are no longer different from the XML on disk
9992 mData->flModifications = 0;
9993 }
9994 catch (HRESULT err)
9995 {
9996 // we assume that error info is set by the thrower
9997 rc = err;
9998
9999 // restore old config
10000 delete pNewConfig;
10001 mData->pMachineConfigFile = pOldConfig;
10002 }
10003 catch (...)
10004 {
10005 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10006 }
10007
10008 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10009 {
10010 /* Fire the data change event, even on failure (since we've already
10011 * committed all data). This is done only for SessionMachines because
10012 * mutable Machine instances are always not registered (i.e. private
10013 * to the client process that creates them) and thus don't need to
10014 * inform callbacks. */
10015 if (isSessionMachine())
10016 mParent->onMachineDataChange(mData->mUuid);
10017 }
10018
10019 LogFlowThisFunc(("rc=%08X\n", rc));
10020 LogFlowThisFuncLeave();
10021 return rc;
10022}
10023
10024/**
10025 * Implementation for saving the machine settings into the given
10026 * settings::MachineConfigFile instance. This copies machine extradata
10027 * from the previous machine config file in the instance data, if any.
10028 *
10029 * This gets called from two locations:
10030 *
10031 * -- Machine::saveSettings(), during the regular XML writing;
10032 *
10033 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10034 * exported to OVF and we write the VirtualBox proprietary XML
10035 * into a <vbox:Machine> tag.
10036 *
10037 * This routine fills all the fields in there, including snapshots, *except*
10038 * for the following:
10039 *
10040 * -- fCurrentStateModified. There is some special logic associated with that.
10041 *
10042 * The caller can then call MachineConfigFile::write() or do something else
10043 * with it.
10044 *
10045 * Caller must hold the machine lock!
10046 *
10047 * This throws XML errors and HRESULT, so the caller must have a catch block!
10048 */
10049void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10050{
10051 // deep copy extradata
10052 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10053
10054 config.uuid = mData->mUuid;
10055
10056 // copy name, description, OS type, teleport, UTC etc.
10057 config.machineUserData = mUserData->s;
10058
10059 // Encode the Icon Override data from Machine and store on config userdata.
10060 com::SafeArray<BYTE> iconByte;
10061 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10062 ssize_t cbData = iconByte.size();
10063 if (cbData > 0)
10064 {
10065 ssize_t cchOut = RTBase64EncodedLength(cbData);
10066 Utf8Str strIconData;
10067 strIconData.reserve(cchOut+1);
10068 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10069 strIconData.mutableRaw(), strIconData.capacity(),
10070 NULL);
10071 if (RT_FAILURE(vrc))
10072 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10073 strIconData.jolt();
10074 config.machineUserData.ovIcon = strIconData;
10075 }
10076 else
10077 config.machineUserData.ovIcon.setNull();
10078
10079 if ( mData->mMachineState == MachineState_Saved
10080 || mData->mMachineState == MachineState_Restoring
10081 // when deleting a snapshot we may or may not have a saved state in the current state,
10082 // so let's not assert here please
10083 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10084 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10085 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10086 && (!mSSData->strStateFilePath.isEmpty())
10087 )
10088 )
10089 {
10090 Assert(!mSSData->strStateFilePath.isEmpty());
10091 /* try to make the file name relative to the settings file dir */
10092 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10093 }
10094 else
10095 {
10096 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10097 config.strStateFile.setNull();
10098 }
10099
10100 if (mData->mCurrentSnapshot)
10101 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10102 else
10103 config.uuidCurrentSnapshot.clear();
10104
10105 config.timeLastStateChange = mData->mLastStateChange;
10106 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10107 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10108
10109 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10110 if (FAILED(rc)) throw rc;
10111
10112 rc = saveStorageControllers(config.storageMachine);
10113 if (FAILED(rc)) throw rc;
10114
10115 // save machine's media registry if this is VirtualBox 4.0 or later
10116 if (config.canHaveOwnMediaRegistry())
10117 {
10118 // determine machine folder
10119 Utf8Str strMachineFolder = getSettingsFileFull();
10120 strMachineFolder.stripFilename();
10121 mParent->saveMediaRegistry(config.mediaRegistry,
10122 getId(), // only media with registry ID == machine UUID
10123 strMachineFolder);
10124 // this throws HRESULT
10125 }
10126
10127 // save snapshots
10128 rc = saveAllSnapshots(config);
10129 if (FAILED(rc)) throw rc;
10130}
10131
10132/**
10133 * Saves all snapshots of the machine into the given machine config file. Called
10134 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10135 * @param config
10136 * @return
10137 */
10138HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10139{
10140 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10141
10142 HRESULT rc = S_OK;
10143
10144 try
10145 {
10146 config.llFirstSnapshot.clear();
10147
10148 if (mData->mFirstSnapshot)
10149 {
10150 settings::Snapshot snapNew;
10151 config.llFirstSnapshot.push_back(snapNew);
10152
10153 // get reference to the fresh copy of the snapshot on the list and
10154 // work on that copy directly to avoid excessive copying later
10155 settings::Snapshot &snap = config.llFirstSnapshot.front();
10156
10157 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10158 if (FAILED(rc)) throw rc;
10159 }
10160
10161// if (mType == IsSessionMachine)
10162// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10163
10164 }
10165 catch (HRESULT err)
10166 {
10167 /* we assume that error info is set by the thrower */
10168 rc = err;
10169 }
10170 catch (...)
10171 {
10172 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10173 }
10174
10175 return rc;
10176}
10177
10178/**
10179 * Saves the VM hardware configuration. It is assumed that the
10180 * given node is empty.
10181 *
10182 * @param data Reference to the settings object for the hardware config.
10183 * @param pDbg Pointer to the settings object for the debugging config
10184 * which happens to live in mHWData.
10185 * @param pAutostart Pointer to the settings object for the autostart config
10186 * which happens to live in mHWData.
10187 */
10188HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10189 settings::Autostart *pAutostart)
10190{
10191 HRESULT rc = S_OK;
10192
10193 try
10194 {
10195 /* The hardware version attribute (optional).
10196 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10197 if ( mHWData->mHWVersion == "1"
10198 && mSSData->strStateFilePath.isEmpty()
10199 )
10200 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. */
10201
10202 data.strVersion = mHWData->mHWVersion;
10203 data.uuid = mHWData->mHardwareUUID;
10204
10205 // CPU
10206 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10207 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10208 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10209 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10210 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10211 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10212 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10213 data.fPAE = !!mHWData->mPAEEnabled;
10214 data.enmLongMode = mHWData->mLongMode;
10215 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10216
10217 /* Standard and Extended CPUID leafs. */
10218 data.llCpuIdLeafs.clear();
10219 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10220 {
10221 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10222 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10223 }
10224 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10225 {
10226 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10227 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10228 }
10229
10230 data.cCPUs = mHWData->mCPUCount;
10231 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10232 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10233
10234 data.llCpus.clear();
10235 if (data.fCpuHotPlug)
10236 {
10237 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10238 {
10239 if (mHWData->mCPUAttached[idx])
10240 {
10241 settings::Cpu cpu;
10242 cpu.ulId = idx;
10243 data.llCpus.push_back(cpu);
10244 }
10245 }
10246 }
10247
10248 // memory
10249 data.ulMemorySizeMB = mHWData->mMemorySize;
10250 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10251
10252 // firmware
10253 data.firmwareType = mHWData->mFirmwareType;
10254
10255 // HID
10256 data.pointingHIDType = mHWData->mPointingHIDType;
10257 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10258
10259 // chipset
10260 data.chipsetType = mHWData->mChipsetType;
10261
10262 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10263 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10264
10265 // HPET
10266 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10267
10268 // boot order
10269 data.mapBootOrder.clear();
10270 for (size_t i = 0;
10271 i < RT_ELEMENTS(mHWData->mBootOrder);
10272 ++i)
10273 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10274
10275 // display
10276 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10277 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10278 data.cMonitors = mHWData->mMonitorCount;
10279 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10280 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10281 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10282 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10283 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10284 data.ulVideoCaptureFps = mHWData->mVideoCaptureFps;
10285 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10286 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10287 {
10288 if (mHWData->maVideoCaptureScreens[i])
10289 ASMBitSet(&data.u64VideoCaptureScreens, i);
10290 else
10291 ASMBitClear(&data.u64VideoCaptureScreens, i);
10292 }
10293 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
10294
10295 /* VRDEServer settings (optional) */
10296 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10297 if (FAILED(rc)) throw rc;
10298
10299 /* BIOS (required) */
10300 rc = mBIOSSettings->saveSettings(data.biosSettings);
10301 if (FAILED(rc)) throw rc;
10302
10303 /* USB Controller (required) */
10304 rc = mUSBController->saveSettings(data.usbController);
10305 if (FAILED(rc)) throw rc;
10306
10307 /* Network adapters (required) */
10308 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10309 data.llNetworkAdapters.clear();
10310 /* Write out only the nominal number of network adapters for this
10311 * chipset type. Since Machine::commit() hasn't been called there
10312 * may be extra NIC settings in the vector. */
10313 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10314 {
10315 settings::NetworkAdapter nic;
10316 nic.ulSlot = slot;
10317 /* paranoia check... must not be NULL, but must not crash either. */
10318 if (mNetworkAdapters[slot])
10319 {
10320 rc = mNetworkAdapters[slot]->saveSettings(nic);
10321 if (FAILED(rc)) throw rc;
10322
10323 data.llNetworkAdapters.push_back(nic);
10324 }
10325 }
10326
10327 /* Serial ports */
10328 data.llSerialPorts.clear();
10329 for (ULONG slot = 0;
10330 slot < RT_ELEMENTS(mSerialPorts);
10331 ++slot)
10332 {
10333 settings::SerialPort s;
10334 s.ulSlot = slot;
10335 rc = mSerialPorts[slot]->saveSettings(s);
10336 if (FAILED(rc)) return rc;
10337
10338 data.llSerialPorts.push_back(s);
10339 }
10340
10341 /* Parallel ports */
10342 data.llParallelPorts.clear();
10343 for (ULONG slot = 0;
10344 slot < RT_ELEMENTS(mParallelPorts);
10345 ++slot)
10346 {
10347 settings::ParallelPort p;
10348 p.ulSlot = slot;
10349 rc = mParallelPorts[slot]->saveSettings(p);
10350 if (FAILED(rc)) return rc;
10351
10352 data.llParallelPorts.push_back(p);
10353 }
10354
10355 /* Audio adapter */
10356 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10357 if (FAILED(rc)) return rc;
10358
10359 /* Shared folders */
10360 data.llSharedFolders.clear();
10361 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10362 it != mHWData->mSharedFolders.end();
10363 ++it)
10364 {
10365 SharedFolder *pSF = *it;
10366 AutoCaller sfCaller(pSF);
10367 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10368 settings::SharedFolder sf;
10369 sf.strName = pSF->getName();
10370 sf.strHostPath = pSF->getHostPath();
10371 sf.fWritable = !!pSF->isWritable();
10372 sf.fAutoMount = !!pSF->isAutoMounted();
10373
10374 data.llSharedFolders.push_back(sf);
10375 }
10376
10377 // clipboard
10378 data.clipboardMode = mHWData->mClipboardMode;
10379
10380 // drag'n'drop
10381 data.dragAndDropMode = mHWData->mDragAndDropMode;
10382
10383 /* Guest */
10384 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10385
10386 // IO settings
10387 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10388 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10389
10390 /* BandwidthControl (required) */
10391 rc = mBandwidthControl->saveSettings(data.ioSettings);
10392 if (FAILED(rc)) throw rc;
10393
10394 /* Host PCI devices */
10395 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10396 it != mHWData->mPCIDeviceAssignments.end();
10397 ++it)
10398 {
10399 ComObjPtr<PCIDeviceAttachment> pda = *it;
10400 settings::HostPCIDeviceAttachment hpda;
10401
10402 rc = pda->saveSettings(hpda);
10403 if (FAILED(rc)) throw rc;
10404
10405 data.pciAttachments.push_back(hpda);
10406 }
10407
10408
10409 // guest properties
10410 data.llGuestProperties.clear();
10411#ifdef VBOX_WITH_GUEST_PROPS
10412 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10413 it != mHWData->mGuestProperties.end();
10414 ++it)
10415 {
10416 HWData::GuestProperty property = it->second;
10417
10418 /* Remove transient guest properties at shutdown unless we
10419 * are saving state */
10420 if ( ( mData->mMachineState == MachineState_PoweredOff
10421 || mData->mMachineState == MachineState_Aborted
10422 || mData->mMachineState == MachineState_Teleported)
10423 && ( property.mFlags & guestProp::TRANSIENT
10424 || property.mFlags & guestProp::TRANSRESET))
10425 continue;
10426 settings::GuestProperty prop;
10427 prop.strName = it->first;
10428 prop.strValue = property.strValue;
10429 prop.timestamp = property.mTimestamp;
10430 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10431 guestProp::writeFlags(property.mFlags, szFlags);
10432 prop.strFlags = szFlags;
10433
10434 data.llGuestProperties.push_back(prop);
10435 }
10436
10437 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10438 /* I presume this doesn't require a backup(). */
10439 mData->mGuestPropertiesModified = FALSE;
10440#endif /* VBOX_WITH_GUEST_PROPS defined */
10441
10442 *pDbg = mHWData->mDebugging;
10443 *pAutostart = mHWData->mAutostart;
10444
10445 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10446 }
10447 catch(std::bad_alloc &)
10448 {
10449 return E_OUTOFMEMORY;
10450 }
10451
10452 AssertComRC(rc);
10453 return rc;
10454}
10455
10456/**
10457 * Saves the storage controller configuration.
10458 *
10459 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10460 */
10461HRESULT Machine::saveStorageControllers(settings::Storage &data)
10462{
10463 data.llStorageControllers.clear();
10464
10465 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10466 it != mStorageControllers->end();
10467 ++it)
10468 {
10469 HRESULT rc;
10470 ComObjPtr<StorageController> pCtl = *it;
10471
10472 settings::StorageController ctl;
10473 ctl.strName = pCtl->getName();
10474 ctl.controllerType = pCtl->getControllerType();
10475 ctl.storageBus = pCtl->getStorageBus();
10476 ctl.ulInstance = pCtl->getInstance();
10477 ctl.fBootable = pCtl->getBootable();
10478
10479 /* Save the port count. */
10480 ULONG portCount;
10481 rc = pCtl->COMGETTER(PortCount)(&portCount);
10482 ComAssertComRCRet(rc, rc);
10483 ctl.ulPortCount = portCount;
10484
10485 /* Save fUseHostIOCache */
10486 BOOL fUseHostIOCache;
10487 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10488 ComAssertComRCRet(rc, rc);
10489 ctl.fUseHostIOCache = !!fUseHostIOCache;
10490
10491 /* Save IDE emulation settings. */
10492 if (ctl.controllerType == StorageControllerType_IntelAhci)
10493 {
10494 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10495 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10496 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10497 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10498 )
10499 ComAssertComRCRet(rc, rc);
10500 }
10501
10502 /* save the devices now. */
10503 rc = saveStorageDevices(pCtl, ctl);
10504 ComAssertComRCRet(rc, rc);
10505
10506 data.llStorageControllers.push_back(ctl);
10507 }
10508
10509 return S_OK;
10510}
10511
10512/**
10513 * Saves the hard disk configuration.
10514 */
10515HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10516 settings::StorageController &data)
10517{
10518 MediaData::AttachmentList atts;
10519
10520 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10521 if (FAILED(rc)) return rc;
10522
10523 data.llAttachedDevices.clear();
10524 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10525 it != atts.end();
10526 ++it)
10527 {
10528 settings::AttachedDevice dev;
10529
10530 MediumAttachment *pAttach = *it;
10531 Medium *pMedium = pAttach->getMedium();
10532
10533 dev.deviceType = pAttach->getType();
10534 dev.lPort = pAttach->getPort();
10535 dev.lDevice = pAttach->getDevice();
10536 if (pMedium)
10537 {
10538 if (pMedium->isHostDrive())
10539 dev.strHostDriveSrc = pMedium->getLocationFull();
10540 else
10541 dev.uuid = pMedium->getId();
10542 dev.fPassThrough = pAttach->getPassthrough();
10543 dev.fTempEject = pAttach->getTempEject();
10544 dev.fNonRotational = pAttach->getNonRotational();
10545 dev.fDiscard = pAttach->getDiscard();
10546 }
10547
10548 dev.strBwGroup = pAttach->getBandwidthGroup();
10549
10550 data.llAttachedDevices.push_back(dev);
10551 }
10552
10553 return S_OK;
10554}
10555
10556/**
10557 * Saves machine state settings as defined by aFlags
10558 * (SaveSTS_* values).
10559 *
10560 * @param aFlags Combination of SaveSTS_* flags.
10561 *
10562 * @note Locks objects for writing.
10563 */
10564HRESULT Machine::saveStateSettings(int aFlags)
10565{
10566 if (aFlags == 0)
10567 return S_OK;
10568
10569 AutoCaller autoCaller(this);
10570 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10571
10572 /* This object's write lock is also necessary to serialize file access
10573 * (prevent concurrent reads and writes) */
10574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10575
10576 HRESULT rc = S_OK;
10577
10578 Assert(mData->pMachineConfigFile);
10579
10580 try
10581 {
10582 if (aFlags & SaveSTS_CurStateModified)
10583 mData->pMachineConfigFile->fCurrentStateModified = true;
10584
10585 if (aFlags & SaveSTS_StateFilePath)
10586 {
10587 if (!mSSData->strStateFilePath.isEmpty())
10588 /* try to make the file name relative to the settings file dir */
10589 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10590 else
10591 mData->pMachineConfigFile->strStateFile.setNull();
10592 }
10593
10594 if (aFlags & SaveSTS_StateTimeStamp)
10595 {
10596 Assert( mData->mMachineState != MachineState_Aborted
10597 || mSSData->strStateFilePath.isEmpty());
10598
10599 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10600
10601 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10602//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10603 }
10604
10605 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10606 }
10607 catch (...)
10608 {
10609 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10610 }
10611
10612 return rc;
10613}
10614
10615/**
10616 * Ensures that the given medium is added to a media registry. If this machine
10617 * was created with 4.0 or later, then the machine registry is used. Otherwise
10618 * the global VirtualBox media registry is used.
10619 *
10620 * Caller must NOT hold machine lock, media tree or any medium locks!
10621 *
10622 * @param pMedium
10623 */
10624void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10625{
10626 /* Paranoia checks: do not hold machine or media tree locks. */
10627 AssertReturnVoid(!isWriteLockOnCurrentThread());
10628 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10629
10630 ComObjPtr<Medium> pBase;
10631 {
10632 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10633 pBase = pMedium->getBase();
10634 }
10635
10636 /* Paranoia checks: do not hold medium locks. */
10637 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10638 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10639
10640 // decide which medium registry to use now that the medium is attached:
10641 Guid uuid;
10642 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10643 // machine XML is VirtualBox 4.0 or higher:
10644 uuid = getId(); // machine UUID
10645 else
10646 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10647
10648 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10649 mParent->markRegistryModified(uuid);
10650
10651 /* For more complex hard disk structures it can happen that the base
10652 * medium isn't yet associated with any medium registry. Do that now. */
10653 if (pMedium != pBase)
10654 {
10655 if (pBase->addRegistry(uuid, true /* fRecurse */))
10656 mParent->markRegistryModified(uuid);
10657 }
10658}
10659
10660/**
10661 * Creates differencing hard disks for all normal hard disks attached to this
10662 * machine and a new set of attachments to refer to created disks.
10663 *
10664 * Used when taking a snapshot or when deleting the current state. Gets called
10665 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10666 *
10667 * This method assumes that mMediaData contains the original hard disk attachments
10668 * it needs to create diffs for. On success, these attachments will be replaced
10669 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10670 * called to delete created diffs which will also rollback mMediaData and restore
10671 * whatever was backed up before calling this method.
10672 *
10673 * Attachments with non-normal hard disks are left as is.
10674 *
10675 * If @a aOnline is @c false then the original hard disks that require implicit
10676 * diffs will be locked for reading. Otherwise it is assumed that they are
10677 * already locked for writing (when the VM was started). Note that in the latter
10678 * case it is responsibility of the caller to lock the newly created diffs for
10679 * writing if this method succeeds.
10680 *
10681 * @param aProgress Progress object to run (must contain at least as
10682 * many operations left as the number of hard disks
10683 * attached).
10684 * @param aOnline Whether the VM was online prior to this operation.
10685 *
10686 * @note The progress object is not marked as completed, neither on success nor
10687 * on failure. This is a responsibility of the caller.
10688 *
10689 * @note Locks this object and the media tree for writing.
10690 */
10691HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10692 ULONG aWeight,
10693 bool aOnline)
10694{
10695 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10696
10697 AutoCaller autoCaller(this);
10698 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10699
10700 AutoMultiWriteLock2 alock(this->lockHandle(),
10701 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10702
10703 /* must be in a protective state because we release the lock below */
10704 AssertReturn( mData->mMachineState == MachineState_Saving
10705 || mData->mMachineState == MachineState_LiveSnapshotting
10706 || mData->mMachineState == MachineState_RestoringSnapshot
10707 || mData->mMachineState == MachineState_DeletingSnapshot
10708 , E_FAIL);
10709
10710 HRESULT rc = S_OK;
10711
10712 // use appropriate locked media map (online or offline)
10713 MediumLockListMap lockedMediaOffline;
10714 MediumLockListMap *lockedMediaMap;
10715 if (aOnline)
10716 lockedMediaMap = &mData->mSession.mLockedMedia;
10717 else
10718 lockedMediaMap = &lockedMediaOffline;
10719
10720 try
10721 {
10722 if (!aOnline)
10723 {
10724 /* lock all attached hard disks early to detect "in use"
10725 * situations before creating actual diffs */
10726 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10727 it != mMediaData->mAttachments.end();
10728 ++it)
10729 {
10730 MediumAttachment* pAtt = *it;
10731 if (pAtt->getType() == DeviceType_HardDisk)
10732 {
10733 Medium* pMedium = pAtt->getMedium();
10734 Assert(pMedium);
10735
10736 MediumLockList *pMediumLockList(new MediumLockList());
10737 alock.release();
10738 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10739 false /* fMediumLockWrite */,
10740 NULL,
10741 *pMediumLockList);
10742 alock.acquire();
10743 if (FAILED(rc))
10744 {
10745 delete pMediumLockList;
10746 throw rc;
10747 }
10748 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10749 if (FAILED(rc))
10750 {
10751 throw setError(rc,
10752 tr("Collecting locking information for all attached media failed"));
10753 }
10754 }
10755 }
10756
10757 /* Now lock all media. If this fails, nothing is locked. */
10758 alock.release();
10759 rc = lockedMediaMap->Lock();
10760 alock.acquire();
10761 if (FAILED(rc))
10762 {
10763 throw setError(rc,
10764 tr("Locking of attached media failed"));
10765 }
10766 }
10767
10768 /* remember the current list (note that we don't use backup() since
10769 * mMediaData may be already backed up) */
10770 MediaData::AttachmentList atts = mMediaData->mAttachments;
10771
10772 /* start from scratch */
10773 mMediaData->mAttachments.clear();
10774
10775 /* go through remembered attachments and create diffs for normal hard
10776 * disks and attach them */
10777 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10778 it != atts.end();
10779 ++it)
10780 {
10781 MediumAttachment* pAtt = *it;
10782
10783 DeviceType_T devType = pAtt->getType();
10784 Medium* pMedium = pAtt->getMedium();
10785
10786 if ( devType != DeviceType_HardDisk
10787 || pMedium == NULL
10788 || pMedium->getType() != MediumType_Normal)
10789 {
10790 /* copy the attachment as is */
10791
10792 /** @todo the progress object created in Console::TakeSnaphot
10793 * only expects operations for hard disks. Later other
10794 * device types need to show up in the progress as well. */
10795 if (devType == DeviceType_HardDisk)
10796 {
10797 if (pMedium == NULL)
10798 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10799 aWeight); // weight
10800 else
10801 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10802 pMedium->getBase()->getName().c_str()).raw(),
10803 aWeight); // weight
10804 }
10805
10806 mMediaData->mAttachments.push_back(pAtt);
10807 continue;
10808 }
10809
10810 /* need a diff */
10811 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10812 pMedium->getBase()->getName().c_str()).raw(),
10813 aWeight); // weight
10814
10815 Utf8Str strFullSnapshotFolder;
10816 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10817
10818 ComObjPtr<Medium> diff;
10819 diff.createObject();
10820 // store the diff in the same registry as the parent
10821 // (this cannot fail here because we can't create implicit diffs for
10822 // unregistered images)
10823 Guid uuidRegistryParent;
10824 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10825 Assert(fInRegistry); NOREF(fInRegistry);
10826 rc = diff->init(mParent,
10827 pMedium->getPreferredDiffFormat(),
10828 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10829 uuidRegistryParent);
10830 if (FAILED(rc)) throw rc;
10831
10832 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10833 * the push_back? Looks like we're going to release medium with the
10834 * wrong kind of lock (general issue with if we fail anywhere at all)
10835 * and an orphaned VDI in the snapshots folder. */
10836
10837 /* update the appropriate lock list */
10838 MediumLockList *pMediumLockList;
10839 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10840 AssertComRCThrowRC(rc);
10841 if (aOnline)
10842 {
10843 alock.release();
10844 /* The currently attached medium will be read-only, change
10845 * the lock type to read. */
10846 rc = pMediumLockList->Update(pMedium, false);
10847 alock.acquire();
10848 AssertComRCThrowRC(rc);
10849 }
10850
10851 /* release the locks before the potentially lengthy operation */
10852 alock.release();
10853 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10854 pMediumLockList,
10855 NULL /* aProgress */,
10856 true /* aWait */);
10857 alock.acquire();
10858 if (FAILED(rc)) throw rc;
10859
10860 /* actual lock list update is done in Medium::commitMedia */
10861
10862 rc = diff->addBackReference(mData->mUuid);
10863 AssertComRCThrowRC(rc);
10864
10865 /* add a new attachment */
10866 ComObjPtr<MediumAttachment> attachment;
10867 attachment.createObject();
10868 rc = attachment->init(this,
10869 diff,
10870 pAtt->getControllerName(),
10871 pAtt->getPort(),
10872 pAtt->getDevice(),
10873 DeviceType_HardDisk,
10874 true /* aImplicit */,
10875 false /* aPassthrough */,
10876 false /* aTempEject */,
10877 pAtt->getNonRotational(),
10878 pAtt->getDiscard(),
10879 pAtt->getBandwidthGroup());
10880 if (FAILED(rc)) throw rc;
10881
10882 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10883 AssertComRCThrowRC(rc);
10884 mMediaData->mAttachments.push_back(attachment);
10885 }
10886 }
10887 catch (HRESULT aRC) { rc = aRC; }
10888
10889 /* unlock all hard disks we locked when there is no VM */
10890 if (!aOnline)
10891 {
10892 ErrorInfoKeeper eik;
10893
10894 HRESULT rc1 = lockedMediaMap->Clear();
10895 AssertComRC(rc1);
10896 }
10897
10898 return rc;
10899}
10900
10901/**
10902 * Deletes implicit differencing hard disks created either by
10903 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10904 *
10905 * Note that to delete hard disks created by #AttachDevice() this method is
10906 * called from #fixupMedia() when the changes are rolled back.
10907 *
10908 * @note Locks this object and the media tree for writing.
10909 */
10910HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10911{
10912 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10913
10914 AutoCaller autoCaller(this);
10915 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10916
10917 AutoMultiWriteLock2 alock(this->lockHandle(),
10918 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10919
10920 /* We absolutely must have backed up state. */
10921 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10922
10923 /* Check if there are any implicitly created diff images. */
10924 bool fImplicitDiffs = false;
10925 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10926 it != mMediaData->mAttachments.end();
10927 ++it)
10928 {
10929 const ComObjPtr<MediumAttachment> &pAtt = *it;
10930 if (pAtt->isImplicit())
10931 {
10932 fImplicitDiffs = true;
10933 break;
10934 }
10935 }
10936 /* If there is nothing to do, leave early. This saves lots of image locking
10937 * effort. It also avoids a MachineStateChanged event without real reason.
10938 * This is important e.g. when loading a VM config, because there should be
10939 * no events. Otherwise API clients can become thoroughly confused for
10940 * inaccessible VMs (the code for loading VM configs uses this method for
10941 * cleanup if the config makes no sense), as they take such events as an
10942 * indication that the VM is alive, and they would force the VM config to
10943 * be reread, leading to an endless loop. */
10944 if (!fImplicitDiffs)
10945 return S_OK;
10946
10947 HRESULT rc = S_OK;
10948 MachineState_T oldState = mData->mMachineState;
10949
10950 /* will release the lock before the potentially lengthy operation,
10951 * so protect with the special state (unless already protected) */
10952 if ( oldState != MachineState_Saving
10953 && oldState != MachineState_LiveSnapshotting
10954 && oldState != MachineState_RestoringSnapshot
10955 && oldState != MachineState_DeletingSnapshot
10956 && oldState != MachineState_DeletingSnapshotOnline
10957 && oldState != MachineState_DeletingSnapshotPaused
10958 )
10959 setMachineState(MachineState_SettingUp);
10960
10961 // use appropriate locked media map (online or offline)
10962 MediumLockListMap lockedMediaOffline;
10963 MediumLockListMap *lockedMediaMap;
10964 if (aOnline)
10965 lockedMediaMap = &mData->mSession.mLockedMedia;
10966 else
10967 lockedMediaMap = &lockedMediaOffline;
10968
10969 try
10970 {
10971 if (!aOnline)
10972 {
10973 /* lock all attached hard disks early to detect "in use"
10974 * situations before deleting actual diffs */
10975 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10976 it != mMediaData->mAttachments.end();
10977 ++it)
10978 {
10979 MediumAttachment* pAtt = *it;
10980 if (pAtt->getType() == DeviceType_HardDisk)
10981 {
10982 Medium* pMedium = pAtt->getMedium();
10983 Assert(pMedium);
10984
10985 MediumLockList *pMediumLockList(new MediumLockList());
10986 alock.release();
10987 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10988 false /* fMediumLockWrite */,
10989 NULL,
10990 *pMediumLockList);
10991 alock.acquire();
10992
10993 if (FAILED(rc))
10994 {
10995 delete pMediumLockList;
10996 throw rc;
10997 }
10998
10999 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11000 if (FAILED(rc))
11001 throw rc;
11002 }
11003 }
11004
11005 if (FAILED(rc))
11006 throw rc;
11007 } // end of offline
11008
11009 /* Lock lists are now up to date and include implicitly created media */
11010
11011 /* Go through remembered attachments and delete all implicitly created
11012 * diffs and fix up the attachment information */
11013 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11014 MediaData::AttachmentList implicitAtts;
11015 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11016 it != mMediaData->mAttachments.end();
11017 ++it)
11018 {
11019 ComObjPtr<MediumAttachment> pAtt = *it;
11020 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11021 if (pMedium.isNull())
11022 continue;
11023
11024 // Implicit attachments go on the list for deletion and back references are removed.
11025 if (pAtt->isImplicit())
11026 {
11027 /* Deassociate and mark for deletion */
11028 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11029 rc = pMedium->removeBackReference(mData->mUuid);
11030 if (FAILED(rc))
11031 throw rc;
11032 implicitAtts.push_back(pAtt);
11033 continue;
11034 }
11035
11036 /* Was this medium attached before? */
11037 if (!findAttachment(oldAtts, pMedium))
11038 {
11039 /* no: de-associate */
11040 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11041 rc = pMedium->removeBackReference(mData->mUuid);
11042 if (FAILED(rc))
11043 throw rc;
11044 continue;
11045 }
11046 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11047 }
11048
11049 /* If there are implicit attachments to delete, throw away the lock
11050 * map contents (which will unlock all media) since the medium
11051 * attachments will be rolled back. Below we need to completely
11052 * recreate the lock map anyway since it is infinitely complex to
11053 * do this incrementally (would need reconstructing each attachment
11054 * change, which would be extremely hairy). */
11055 if (implicitAtts.size() != 0)
11056 {
11057 ErrorInfoKeeper eik;
11058
11059 HRESULT rc1 = lockedMediaMap->Clear();
11060 AssertComRC(rc1);
11061 }
11062
11063 /* rollback hard disk changes */
11064 mMediaData.rollback();
11065
11066 MultiResult mrc(S_OK);
11067
11068 // Delete unused implicit diffs.
11069 if (implicitAtts.size() != 0)
11070 {
11071 alock.release();
11072
11073 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11074 it != implicitAtts.end();
11075 ++it)
11076 {
11077 // Remove medium associated with this attachment.
11078 ComObjPtr<MediumAttachment> pAtt = *it;
11079 Assert(pAtt);
11080 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11081 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11082 Assert(pMedium);
11083
11084 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11085 // continue on delete failure, just collect error messages
11086 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11087 mrc = rc;
11088 }
11089
11090 alock.acquire();
11091
11092 /* if there is a VM recreate media lock map as mentioned above,
11093 * otherwise it is a waste of time and we leave things unlocked */
11094 if (aOnline)
11095 {
11096 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11097 /* must never be NULL, but better safe than sorry */
11098 if (!pMachine.isNull())
11099 {
11100 alock.release();
11101 rc = mData->mSession.mMachine->lockMedia();
11102 alock.acquire();
11103 if (FAILED(rc))
11104 throw rc;
11105 }
11106 }
11107 }
11108 }
11109 catch (HRESULT aRC) {rc = aRC;}
11110
11111 if (mData->mMachineState == MachineState_SettingUp)
11112 setMachineState(oldState);
11113
11114 /* unlock all hard disks we locked when there is no VM */
11115 if (!aOnline)
11116 {
11117 ErrorInfoKeeper eik;
11118
11119 HRESULT rc1 = lockedMediaMap->Clear();
11120 AssertComRC(rc1);
11121 }
11122
11123 return rc;
11124}
11125
11126
11127/**
11128 * Looks through the given list of media attachments for one with the given parameters
11129 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11130 * can be searched as well if needed.
11131 *
11132 * @param list
11133 * @param aControllerName
11134 * @param aControllerPort
11135 * @param aDevice
11136 * @return
11137 */
11138MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11139 IN_BSTR aControllerName,
11140 LONG aControllerPort,
11141 LONG aDevice)
11142{
11143 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11144 it != ll.end();
11145 ++it)
11146 {
11147 MediumAttachment *pAttach = *it;
11148 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11149 return pAttach;
11150 }
11151
11152 return NULL;
11153}
11154
11155/**
11156 * Looks through the given list of media attachments for one with the given parameters
11157 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11158 * can be searched as well if needed.
11159 *
11160 * @param list
11161 * @param aControllerName
11162 * @param aControllerPort
11163 * @param aDevice
11164 * @return
11165 */
11166MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11167 ComObjPtr<Medium> pMedium)
11168{
11169 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11170 it != ll.end();
11171 ++it)
11172 {
11173 MediumAttachment *pAttach = *it;
11174 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11175 if (pMediumThis == pMedium)
11176 return pAttach;
11177 }
11178
11179 return NULL;
11180}
11181
11182/**
11183 * Looks through the given list of media attachments for one with the given parameters
11184 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11185 * can be searched as well if needed.
11186 *
11187 * @param list
11188 * @param aControllerName
11189 * @param aControllerPort
11190 * @param aDevice
11191 * @return
11192 */
11193MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11194 Guid &id)
11195{
11196 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11197 it != ll.end();
11198 ++it)
11199 {
11200 MediumAttachment *pAttach = *it;
11201 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11202 if (pMediumThis->getId() == id)
11203 return pAttach;
11204 }
11205
11206 return NULL;
11207}
11208
11209/**
11210 * Main implementation for Machine::DetachDevice. This also gets called
11211 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11212 *
11213 * @param pAttach Medium attachment to detach.
11214 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11215 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11216 * @return
11217 */
11218HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11219 AutoWriteLock &writeLock,
11220 Snapshot *pSnapshot)
11221{
11222 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11223 DeviceType_T mediumType = pAttach->getType();
11224
11225 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11226
11227 if (pAttach->isImplicit())
11228 {
11229 /* attempt to implicitly delete the implicitly created diff */
11230
11231 /// @todo move the implicit flag from MediumAttachment to Medium
11232 /// and forbid any hard disk operation when it is implicit. Or maybe
11233 /// a special media state for it to make it even more simple.
11234
11235 Assert(mMediaData.isBackedUp());
11236
11237 /* will release the lock before the potentially lengthy operation, so
11238 * protect with the special state */
11239 MachineState_T oldState = mData->mMachineState;
11240 setMachineState(MachineState_SettingUp);
11241
11242 writeLock.release();
11243
11244 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11245 true /*aWait*/);
11246
11247 writeLock.acquire();
11248
11249 setMachineState(oldState);
11250
11251 if (FAILED(rc)) return rc;
11252 }
11253
11254 setModified(IsModified_Storage);
11255 mMediaData.backup();
11256 mMediaData->mAttachments.remove(pAttach);
11257
11258 if (!oldmedium.isNull())
11259 {
11260 // if this is from a snapshot, do not defer detachment to commitMedia()
11261 if (pSnapshot)
11262 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11263 // else if non-hard disk media, do not defer detachment to commitMedia() either
11264 else if (mediumType != DeviceType_HardDisk)
11265 oldmedium->removeBackReference(mData->mUuid);
11266 }
11267
11268 return S_OK;
11269}
11270
11271/**
11272 * Goes thru all media of the given list and
11273 *
11274 * 1) calls detachDevice() on each of them for this machine and
11275 * 2) adds all Medium objects found in the process to the given list,
11276 * depending on cleanupMode.
11277 *
11278 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11279 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11280 * media to the list.
11281 *
11282 * This gets called from Machine::Unregister, both for the actual Machine and
11283 * the SnapshotMachine objects that might be found in the snapshots.
11284 *
11285 * Requires caller and locking. The machine lock must be passed in because it
11286 * will be passed on to detachDevice which needs it for temporary unlocking.
11287 *
11288 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11289 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11290 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11291 * otherwise no media get added.
11292 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11293 * @return
11294 */
11295HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11296 Snapshot *pSnapshot,
11297 CleanupMode_T cleanupMode,
11298 MediaList &llMedia)
11299{
11300 Assert(isWriteLockOnCurrentThread());
11301
11302 HRESULT rc;
11303
11304 // make a temporary list because detachDevice invalidates iterators into
11305 // mMediaData->mAttachments
11306 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11307
11308 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11309 it != llAttachments2.end();
11310 ++it)
11311 {
11312 ComObjPtr<MediumAttachment> &pAttach = *it;
11313 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11314
11315 if (!pMedium.isNull())
11316 {
11317 AutoCaller mac(pMedium);
11318 if (FAILED(mac.rc())) return mac.rc();
11319 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11320 DeviceType_T devType = pMedium->getDeviceType();
11321 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11322 && devType == DeviceType_HardDisk)
11323 || (cleanupMode == CleanupMode_Full)
11324 )
11325 {
11326 llMedia.push_back(pMedium);
11327 ComObjPtr<Medium> pParent = pMedium->getParent();
11328 /*
11329 * Search for medias which are not attached to any machine, but
11330 * in the chain to an attached disk. Mediums are only consided
11331 * if they are:
11332 * - have only one child
11333 * - no references to any machines
11334 * - are of normal medium type
11335 */
11336 while (!pParent.isNull())
11337 {
11338 AutoCaller mac1(pParent);
11339 if (FAILED(mac1.rc())) return mac1.rc();
11340 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11341 if (pParent->getChildren().size() == 1)
11342 {
11343 if ( pParent->getMachineBackRefCount() == 0
11344 && pParent->getType() == MediumType_Normal
11345 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11346 llMedia.push_back(pParent);
11347 }
11348 else
11349 break;
11350 pParent = pParent->getParent();
11351 }
11352 }
11353 }
11354
11355 // real machine: then we need to use the proper method
11356 rc = detachDevice(pAttach, writeLock, pSnapshot);
11357
11358 if (FAILED(rc))
11359 return rc;
11360 }
11361
11362 return S_OK;
11363}
11364
11365/**
11366 * Perform deferred hard disk detachments.
11367 *
11368 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11369 * backed up).
11370 *
11371 * If @a aOnline is @c true then this method will also unlock the old hard disks
11372 * for which the new implicit diffs were created and will lock these new diffs for
11373 * writing.
11374 *
11375 * @param aOnline Whether the VM was online prior to this operation.
11376 *
11377 * @note Locks this object for writing!
11378 */
11379void Machine::commitMedia(bool aOnline /*= false*/)
11380{
11381 AutoCaller autoCaller(this);
11382 AssertComRCReturnVoid(autoCaller.rc());
11383
11384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11385
11386 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11387
11388 HRESULT rc = S_OK;
11389
11390 /* no attach/detach operations -- nothing to do */
11391 if (!mMediaData.isBackedUp())
11392 return;
11393
11394 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11395 bool fMediaNeedsLocking = false;
11396
11397 /* enumerate new attachments */
11398 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11399 it != mMediaData->mAttachments.end();
11400 ++it)
11401 {
11402 MediumAttachment *pAttach = *it;
11403
11404 pAttach->commit();
11405
11406 Medium* pMedium = pAttach->getMedium();
11407 bool fImplicit = pAttach->isImplicit();
11408
11409 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11410 (pMedium) ? pMedium->getName().c_str() : "NULL",
11411 fImplicit));
11412
11413 /** @todo convert all this Machine-based voodoo to MediumAttachment
11414 * based commit logic. */
11415 if (fImplicit)
11416 {
11417 /* convert implicit attachment to normal */
11418 pAttach->setImplicit(false);
11419
11420 if ( aOnline
11421 && pMedium
11422 && pAttach->getType() == DeviceType_HardDisk
11423 )
11424 {
11425 ComObjPtr<Medium> parent = pMedium->getParent();
11426 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11427
11428 /* update the appropriate lock list */
11429 MediumLockList *pMediumLockList;
11430 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11431 AssertComRC(rc);
11432 if (pMediumLockList)
11433 {
11434 /* unlock if there's a need to change the locking */
11435 if (!fMediaNeedsLocking)
11436 {
11437 rc = mData->mSession.mLockedMedia.Unlock();
11438 AssertComRC(rc);
11439 fMediaNeedsLocking = true;
11440 }
11441 rc = pMediumLockList->Update(parent, false);
11442 AssertComRC(rc);
11443 rc = pMediumLockList->Append(pMedium, true);
11444 AssertComRC(rc);
11445 }
11446 }
11447
11448 continue;
11449 }
11450
11451 if (pMedium)
11452 {
11453 /* was this medium attached before? */
11454 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11455 oldIt != oldAtts.end();
11456 ++oldIt)
11457 {
11458 MediumAttachment *pOldAttach = *oldIt;
11459 if (pOldAttach->getMedium() == pMedium)
11460 {
11461 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11462
11463 /* yes: remove from old to avoid de-association */
11464 oldAtts.erase(oldIt);
11465 break;
11466 }
11467 }
11468 }
11469 }
11470
11471 /* enumerate remaining old attachments and de-associate from the
11472 * current machine state */
11473 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11474 it != oldAtts.end();
11475 ++it)
11476 {
11477 MediumAttachment *pAttach = *it;
11478 Medium* pMedium = pAttach->getMedium();
11479
11480 /* Detach only hard disks, since DVD/floppy media is detached
11481 * instantly in MountMedium. */
11482 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11483 {
11484 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11485
11486 /* now de-associate from the current machine state */
11487 rc = pMedium->removeBackReference(mData->mUuid);
11488 AssertComRC(rc);
11489
11490 if (aOnline)
11491 {
11492 /* unlock since medium is not used anymore */
11493 MediumLockList *pMediumLockList;
11494 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11495 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11496 {
11497 /* this happens for online snapshots, there the attachment
11498 * is changing, but only to a diff image created under
11499 * the old one, so there is no separate lock list */
11500 Assert(!pMediumLockList);
11501 }
11502 else
11503 {
11504 AssertComRC(rc);
11505 if (pMediumLockList)
11506 {
11507 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11508 AssertComRC(rc);
11509 }
11510 }
11511 }
11512 }
11513 }
11514
11515 /* take media locks again so that the locking state is consistent */
11516 if (fMediaNeedsLocking)
11517 {
11518 Assert(aOnline);
11519 rc = mData->mSession.mLockedMedia.Lock();
11520 AssertComRC(rc);
11521 }
11522
11523 /* commit the hard disk changes */
11524 mMediaData.commit();
11525
11526 if (isSessionMachine())
11527 {
11528 /*
11529 * Update the parent machine to point to the new owner.
11530 * This is necessary because the stored parent will point to the
11531 * session machine otherwise and cause crashes or errors later
11532 * when the session machine gets invalid.
11533 */
11534 /** @todo Change the MediumAttachment class to behave like any other
11535 * class in this regard by creating peer MediumAttachment
11536 * objects for session machines and share the data with the peer
11537 * machine.
11538 */
11539 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11540 it != mMediaData->mAttachments.end();
11541 ++it)
11542 {
11543 (*it)->updateParentMachine(mPeer);
11544 }
11545
11546 /* attach new data to the primary machine and reshare it */
11547 mPeer->mMediaData.attach(mMediaData);
11548 }
11549
11550 return;
11551}
11552
11553/**
11554 * Perform deferred deletion of implicitly created diffs.
11555 *
11556 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11557 * backed up).
11558 *
11559 * @note Locks this object for writing!
11560 */
11561void Machine::rollbackMedia()
11562{
11563 AutoCaller autoCaller(this);
11564 AssertComRCReturnVoid(autoCaller.rc());
11565
11566 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11567 LogFlowThisFunc(("Entering rollbackMedia\n"));
11568
11569 HRESULT rc = S_OK;
11570
11571 /* no attach/detach operations -- nothing to do */
11572 if (!mMediaData.isBackedUp())
11573 return;
11574
11575 /* enumerate new attachments */
11576 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11577 it != mMediaData->mAttachments.end();
11578 ++it)
11579 {
11580 MediumAttachment *pAttach = *it;
11581 /* Fix up the backrefs for DVD/floppy media. */
11582 if (pAttach->getType() != DeviceType_HardDisk)
11583 {
11584 Medium* pMedium = pAttach->getMedium();
11585 if (pMedium)
11586 {
11587 rc = pMedium->removeBackReference(mData->mUuid);
11588 AssertComRC(rc);
11589 }
11590 }
11591
11592 (*it)->rollback();
11593
11594 pAttach = *it;
11595 /* Fix up the backrefs for DVD/floppy media. */
11596 if (pAttach->getType() != DeviceType_HardDisk)
11597 {
11598 Medium* pMedium = pAttach->getMedium();
11599 if (pMedium)
11600 {
11601 rc = pMedium->addBackReference(mData->mUuid);
11602 AssertComRC(rc);
11603 }
11604 }
11605 }
11606
11607 /** @todo convert all this Machine-based voodoo to MediumAttachment
11608 * based rollback logic. */
11609 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11610
11611 return;
11612}
11613
11614/**
11615 * Returns true if the settings file is located in the directory named exactly
11616 * as the machine; this means, among other things, that the machine directory
11617 * should be auto-renamed.
11618 *
11619 * @param aSettingsDir if not NULL, the full machine settings file directory
11620 * name will be assigned there.
11621 *
11622 * @note Doesn't lock anything.
11623 * @note Not thread safe (must be called from this object's lock).
11624 */
11625bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11626{
11627 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11628 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11629 if (aSettingsDir)
11630 *aSettingsDir = strMachineDirName;
11631 strMachineDirName.stripPath(); // vmname
11632 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11633 strConfigFileOnly.stripPath() // vmname.vbox
11634 .stripExt(); // vmname
11635 /** @todo hack, make somehow use of ComposeMachineFilename */
11636 if (mUserData->s.fDirectoryIncludesUUID)
11637 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11638
11639 AssertReturn(!strMachineDirName.isEmpty(), false);
11640 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11641
11642 return strMachineDirName == strConfigFileOnly;
11643}
11644
11645/**
11646 * Discards all changes to machine settings.
11647 *
11648 * @param aNotify Whether to notify the direct session about changes or not.
11649 *
11650 * @note Locks objects for writing!
11651 */
11652void Machine::rollback(bool aNotify)
11653{
11654 AutoCaller autoCaller(this);
11655 AssertComRCReturn(autoCaller.rc(), (void)0);
11656
11657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11658
11659 if (!mStorageControllers.isNull())
11660 {
11661 if (mStorageControllers.isBackedUp())
11662 {
11663 /* unitialize all new devices (absent in the backed up list). */
11664 StorageControllerList::const_iterator it = mStorageControllers->begin();
11665 StorageControllerList *backedList = mStorageControllers.backedUpData();
11666 while (it != mStorageControllers->end())
11667 {
11668 if ( std::find(backedList->begin(), backedList->end(), *it)
11669 == backedList->end()
11670 )
11671 {
11672 (*it)->uninit();
11673 }
11674 ++it;
11675 }
11676
11677 /* restore the list */
11678 mStorageControllers.rollback();
11679 }
11680
11681 /* rollback any changes to devices after restoring the list */
11682 if (mData->flModifications & IsModified_Storage)
11683 {
11684 StorageControllerList::const_iterator it = mStorageControllers->begin();
11685 while (it != mStorageControllers->end())
11686 {
11687 (*it)->rollback();
11688 ++it;
11689 }
11690 }
11691 }
11692
11693 mUserData.rollback();
11694
11695 mHWData.rollback();
11696
11697 if (mData->flModifications & IsModified_Storage)
11698 rollbackMedia();
11699
11700 if (mBIOSSettings)
11701 mBIOSSettings->rollback();
11702
11703 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11704 mVRDEServer->rollback();
11705
11706 if (mAudioAdapter)
11707 mAudioAdapter->rollback();
11708
11709 if (mUSBController && (mData->flModifications & IsModified_USB))
11710 mUSBController->rollback();
11711
11712 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11713 mBandwidthControl->rollback();
11714
11715 if (!mHWData.isNull())
11716 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11717 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11718 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11719 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11720
11721 if (mData->flModifications & IsModified_NetworkAdapters)
11722 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11723 if ( mNetworkAdapters[slot]
11724 && mNetworkAdapters[slot]->isModified())
11725 {
11726 mNetworkAdapters[slot]->rollback();
11727 networkAdapters[slot] = mNetworkAdapters[slot];
11728 }
11729
11730 if (mData->flModifications & IsModified_SerialPorts)
11731 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11732 if ( mSerialPorts[slot]
11733 && mSerialPorts[slot]->isModified())
11734 {
11735 mSerialPorts[slot]->rollback();
11736 serialPorts[slot] = mSerialPorts[slot];
11737 }
11738
11739 if (mData->flModifications & IsModified_ParallelPorts)
11740 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11741 if ( mParallelPorts[slot]
11742 && mParallelPorts[slot]->isModified())
11743 {
11744 mParallelPorts[slot]->rollback();
11745 parallelPorts[slot] = mParallelPorts[slot];
11746 }
11747
11748 if (aNotify)
11749 {
11750 /* inform the direct session about changes */
11751
11752 ComObjPtr<Machine> that = this;
11753 uint32_t flModifications = mData->flModifications;
11754 alock.release();
11755
11756 if (flModifications & IsModified_SharedFolders)
11757 that->onSharedFolderChange();
11758
11759 if (flModifications & IsModified_VRDEServer)
11760 that->onVRDEServerChange(/* aRestart */ TRUE);
11761 if (flModifications & IsModified_USB)
11762 that->onUSBControllerChange();
11763
11764 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11765 if (networkAdapters[slot])
11766 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11767 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11768 if (serialPorts[slot])
11769 that->onSerialPortChange(serialPorts[slot]);
11770 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11771 if (parallelPorts[slot])
11772 that->onParallelPortChange(parallelPorts[slot]);
11773
11774 if (flModifications & IsModified_Storage)
11775 that->onStorageControllerChange();
11776
11777#if 0
11778 if (flModifications & IsModified_BandwidthControl)
11779 that->onBandwidthControlChange();
11780#endif
11781 }
11782}
11783
11784/**
11785 * Commits all the changes to machine settings.
11786 *
11787 * Note that this operation is supposed to never fail.
11788 *
11789 * @note Locks this object and children for writing.
11790 */
11791void Machine::commit()
11792{
11793 AutoCaller autoCaller(this);
11794 AssertComRCReturnVoid(autoCaller.rc());
11795
11796 AutoCaller peerCaller(mPeer);
11797 AssertComRCReturnVoid(peerCaller.rc());
11798
11799 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11800
11801 /*
11802 * use safe commit to ensure Snapshot machines (that share mUserData)
11803 * will still refer to a valid memory location
11804 */
11805 mUserData.commitCopy();
11806
11807 mHWData.commit();
11808
11809 if (mMediaData.isBackedUp())
11810 commitMedia(Global::IsOnline(mData->mMachineState));
11811
11812 mBIOSSettings->commit();
11813 mVRDEServer->commit();
11814 mAudioAdapter->commit();
11815 mUSBController->commit();
11816 mBandwidthControl->commit();
11817
11818 /* Since mNetworkAdapters is a list which might have been changed (resized)
11819 * without using the Backupable<> template we need to handle the copying
11820 * of the list entries manually, including the creation of peers for the
11821 * new objects. */
11822 bool commitNetworkAdapters = false;
11823 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11824 if (mPeer)
11825 {
11826 /* commit everything, even the ones which will go away */
11827 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11828 mNetworkAdapters[slot]->commit();
11829 /* copy over the new entries, creating a peer and uninit the original */
11830 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11831 for (size_t slot = 0; slot < newSize; slot++)
11832 {
11833 /* look if this adapter has a peer device */
11834 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11835 if (!peer)
11836 {
11837 /* no peer means the adapter is a newly created one;
11838 * create a peer owning data this data share it with */
11839 peer.createObject();
11840 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11841 }
11842 mPeer->mNetworkAdapters[slot] = peer;
11843 }
11844 /* uninit any no longer needed network adapters */
11845 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11846 mNetworkAdapters[slot]->uninit();
11847 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11848 {
11849 if (mPeer->mNetworkAdapters[slot])
11850 mPeer->mNetworkAdapters[slot]->uninit();
11851 }
11852 /* Keep the original network adapter count until this point, so that
11853 * discarding a chipset type change will not lose settings. */
11854 mNetworkAdapters.resize(newSize);
11855 mPeer->mNetworkAdapters.resize(newSize);
11856 }
11857 else
11858 {
11859 /* we have no peer (our parent is the newly created machine);
11860 * just commit changes to the network adapters */
11861 commitNetworkAdapters = true;
11862 }
11863 if (commitNetworkAdapters)
11864 {
11865 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11866 mNetworkAdapters[slot]->commit();
11867 }
11868
11869 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11870 mSerialPorts[slot]->commit();
11871 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11872 mParallelPorts[slot]->commit();
11873
11874 bool commitStorageControllers = false;
11875
11876 if (mStorageControllers.isBackedUp())
11877 {
11878 mStorageControllers.commit();
11879
11880 if (mPeer)
11881 {
11882 /* Commit all changes to new controllers (this will reshare data with
11883 * peers for those who have peers) */
11884 StorageControllerList *newList = new StorageControllerList();
11885 StorageControllerList::const_iterator it = mStorageControllers->begin();
11886 while (it != mStorageControllers->end())
11887 {
11888 (*it)->commit();
11889
11890 /* look if this controller has a peer device */
11891 ComObjPtr<StorageController> peer = (*it)->getPeer();
11892 if (!peer)
11893 {
11894 /* no peer means the device is a newly created one;
11895 * create a peer owning data this device share it with */
11896 peer.createObject();
11897 peer->init(mPeer, *it, true /* aReshare */);
11898 }
11899 else
11900 {
11901 /* remove peer from the old list */
11902 mPeer->mStorageControllers->remove(peer);
11903 }
11904 /* and add it to the new list */
11905 newList->push_back(peer);
11906
11907 ++it;
11908 }
11909
11910 /* uninit old peer's controllers that are left */
11911 it = mPeer->mStorageControllers->begin();
11912 while (it != mPeer->mStorageControllers->end())
11913 {
11914 (*it)->uninit();
11915 ++it;
11916 }
11917
11918 /* attach new list of controllers to our peer */
11919 mPeer->mStorageControllers.attach(newList);
11920 }
11921 else
11922 {
11923 /* we have no peer (our parent is the newly created machine);
11924 * just commit changes to devices */
11925 commitStorageControllers = true;
11926 }
11927 }
11928 else
11929 {
11930 /* the list of controllers itself is not changed,
11931 * just commit changes to controllers themselves */
11932 commitStorageControllers = true;
11933 }
11934
11935 if (commitStorageControllers)
11936 {
11937 StorageControllerList::const_iterator it = mStorageControllers->begin();
11938 while (it != mStorageControllers->end())
11939 {
11940 (*it)->commit();
11941 ++it;
11942 }
11943 }
11944
11945 if (isSessionMachine())
11946 {
11947 /* attach new data to the primary machine and reshare it */
11948 mPeer->mUserData.attach(mUserData);
11949 mPeer->mHWData.attach(mHWData);
11950 /* mMediaData is reshared by fixupMedia */
11951 // mPeer->mMediaData.attach(mMediaData);
11952 Assert(mPeer->mMediaData.data() == mMediaData.data());
11953 }
11954}
11955
11956/**
11957 * Copies all the hardware data from the given machine.
11958 *
11959 * Currently, only called when the VM is being restored from a snapshot. In
11960 * particular, this implies that the VM is not running during this method's
11961 * call.
11962 *
11963 * @note This method must be called from under this object's lock.
11964 *
11965 * @note This method doesn't call #commit(), so all data remains backed up and
11966 * unsaved.
11967 */
11968void Machine::copyFrom(Machine *aThat)
11969{
11970 AssertReturnVoid(!isSnapshotMachine());
11971 AssertReturnVoid(aThat->isSnapshotMachine());
11972
11973 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11974
11975 mHWData.assignCopy(aThat->mHWData);
11976
11977 // create copies of all shared folders (mHWData after attaching a copy
11978 // contains just references to original objects)
11979 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11980 it != mHWData->mSharedFolders.end();
11981 ++it)
11982 {
11983 ComObjPtr<SharedFolder> folder;
11984 folder.createObject();
11985 HRESULT rc = folder->initCopy(getMachine(), *it);
11986 AssertComRC(rc);
11987 *it = folder;
11988 }
11989
11990 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11991 mVRDEServer->copyFrom(aThat->mVRDEServer);
11992 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11993 mUSBController->copyFrom(aThat->mUSBController);
11994 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11995
11996 /* create private copies of all controllers */
11997 mStorageControllers.backup();
11998 mStorageControllers->clear();
11999 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12000 it != aThat->mStorageControllers->end();
12001 ++it)
12002 {
12003 ComObjPtr<StorageController> ctrl;
12004 ctrl.createObject();
12005 ctrl->initCopy(this, *it);
12006 mStorageControllers->push_back(ctrl);
12007 }
12008
12009 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12010 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12011 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12012 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12013 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12014 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12015 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12016}
12017
12018/**
12019 * Returns whether the given storage controller is hotplug capable.
12020 *
12021 * @returns true if the controller supports hotplugging
12022 * false otherwise.
12023 * @param enmCtrlType The controller type to check for.
12024 */
12025bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12026{
12027 switch (enmCtrlType)
12028 {
12029 case StorageControllerType_IntelAhci:
12030 return true;
12031 case StorageControllerType_LsiLogic:
12032 case StorageControllerType_LsiLogicSas:
12033 case StorageControllerType_BusLogic:
12034 case StorageControllerType_PIIX3:
12035 case StorageControllerType_PIIX4:
12036 case StorageControllerType_ICH6:
12037 case StorageControllerType_I82078:
12038 default:
12039 return false;
12040 }
12041}
12042
12043#ifdef VBOX_WITH_RESOURCE_USAGE_API
12044
12045void Machine::getDiskList(MediaList &list)
12046{
12047 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12048 it != mMediaData->mAttachments.end();
12049 ++it)
12050 {
12051 MediumAttachment* pAttach = *it;
12052 /* just in case */
12053 AssertStmt(pAttach, continue);
12054
12055 AutoCaller localAutoCallerA(pAttach);
12056 if (FAILED(localAutoCallerA.rc())) continue;
12057
12058 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12059
12060 if (pAttach->getType() == DeviceType_HardDisk)
12061 list.push_back(pAttach->getMedium());
12062 }
12063}
12064
12065void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12066{
12067 AssertReturnVoid(isWriteLockOnCurrentThread());
12068 AssertPtrReturnVoid(aCollector);
12069
12070 pm::CollectorHAL *hal = aCollector->getHAL();
12071 /* Create sub metrics */
12072 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12073 "Percentage of processor time spent in user mode by the VM process.");
12074 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12075 "Percentage of processor time spent in kernel mode by the VM process.");
12076 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12077 "Size of resident portion of VM process in memory.");
12078 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12079 "Actual size of all VM disks combined.");
12080 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12081 "Network receive rate.");
12082 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12083 "Network transmit rate.");
12084 /* Create and register base metrics */
12085 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12086 cpuLoadUser, cpuLoadKernel);
12087 aCollector->registerBaseMetric(cpuLoad);
12088 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12089 ramUsageUsed);
12090 aCollector->registerBaseMetric(ramUsage);
12091 MediaList disks;
12092 getDiskList(disks);
12093 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12094 diskUsageUsed);
12095 aCollector->registerBaseMetric(diskUsage);
12096
12097 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12098 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12099 new pm::AggregateAvg()));
12100 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12101 new pm::AggregateMin()));
12102 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12103 new pm::AggregateMax()));
12104 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12105 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12106 new pm::AggregateAvg()));
12107 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12108 new pm::AggregateMin()));
12109 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12110 new pm::AggregateMax()));
12111
12112 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12113 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12114 new pm::AggregateAvg()));
12115 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12116 new pm::AggregateMin()));
12117 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12118 new pm::AggregateMax()));
12119
12120 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12121 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12122 new pm::AggregateAvg()));
12123 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12124 new pm::AggregateMin()));
12125 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12126 new pm::AggregateMax()));
12127
12128
12129 /* Guest metrics collector */
12130 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12131 aCollector->registerGuest(mCollectorGuest);
12132 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12133 this, __PRETTY_FUNCTION__, mCollectorGuest));
12134
12135 /* Create sub metrics */
12136 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12137 "Percentage of processor time spent in user mode as seen by the guest.");
12138 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12139 "Percentage of processor time spent in kernel mode as seen by the guest.");
12140 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12141 "Percentage of processor time spent idling as seen by the guest.");
12142
12143 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12144 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12145 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12146 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12147 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12148 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12149
12150 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12151
12152 /* Create and register base metrics */
12153 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12154 machineNetRx, machineNetTx);
12155 aCollector->registerBaseMetric(machineNetRate);
12156
12157 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12158 guestLoadUser, guestLoadKernel, guestLoadIdle);
12159 aCollector->registerBaseMetric(guestCpuLoad);
12160
12161 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12162 guestMemTotal, guestMemFree,
12163 guestMemBalloon, guestMemShared,
12164 guestMemCache, guestPagedTotal);
12165 aCollector->registerBaseMetric(guestCpuMem);
12166
12167 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12168 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12169 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12170 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12171
12172 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12173 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12174 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12175 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12176
12177 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12178 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12179 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12180 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12181
12182 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12183 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12184 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12185 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12186
12187 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12188 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12189 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12190 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12191
12192 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12193 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12194 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12195 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12196
12197 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12198 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12199 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12200 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12201
12202 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12203 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12204 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12205 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12206
12207 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12208 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12209 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12210 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12211
12212 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12213 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12214 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12215 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12216
12217 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12218 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12219 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12220 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12221}
12222
12223void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12224{
12225 AssertReturnVoid(isWriteLockOnCurrentThread());
12226
12227 if (aCollector)
12228 {
12229 aCollector->unregisterMetricsFor(aMachine);
12230 aCollector->unregisterBaseMetricsFor(aMachine);
12231 }
12232}
12233
12234#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12235
12236
12237////////////////////////////////////////////////////////////////////////////////
12238
12239DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12240
12241HRESULT SessionMachine::FinalConstruct()
12242{
12243 LogFlowThisFunc(("\n"));
12244
12245#if defined(RT_OS_WINDOWS)
12246 mIPCSem = NULL;
12247#elif defined(RT_OS_OS2)
12248 mIPCSem = NULLHANDLE;
12249#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12250 mIPCSem = -1;
12251#else
12252# error "Port me!"
12253#endif
12254
12255 return BaseFinalConstruct();
12256}
12257
12258void SessionMachine::FinalRelease()
12259{
12260 LogFlowThisFunc(("\n"));
12261
12262 uninit(Uninit::Unexpected);
12263
12264 BaseFinalRelease();
12265}
12266
12267/**
12268 * @note Must be called only by Machine::openSession() from its own write lock.
12269 */
12270HRESULT SessionMachine::init(Machine *aMachine)
12271{
12272 LogFlowThisFuncEnter();
12273 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12274
12275 AssertReturn(aMachine, E_INVALIDARG);
12276
12277 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12278
12279 /* Enclose the state transition NotReady->InInit->Ready */
12280 AutoInitSpan autoInitSpan(this);
12281 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12282
12283 /* create the interprocess semaphore */
12284#if defined(RT_OS_WINDOWS)
12285 mIPCSemName = aMachine->mData->m_strConfigFileFull;
12286 for (size_t i = 0; i < mIPCSemName.length(); i++)
12287 if (mIPCSemName.raw()[i] == '\\')
12288 mIPCSemName.raw()[i] = '/';
12289 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12290 ComAssertMsgRet(mIPCSem,
12291 ("Cannot create IPC mutex '%ls', err=%d",
12292 mIPCSemName.raw(), ::GetLastError()),
12293 E_FAIL);
12294#elif defined(RT_OS_OS2)
12295 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12296 aMachine->mData->mUuid.raw());
12297 mIPCSemName = ipcSem;
12298 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12299 ComAssertMsgRet(arc == NO_ERROR,
12300 ("Cannot create IPC mutex '%s', arc=%ld",
12301 ipcSem.c_str(), arc),
12302 E_FAIL);
12303#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12304# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12305# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12306 /** @todo Check that this still works correctly. */
12307 AssertCompileSize(key_t, 8);
12308# else
12309 AssertCompileSize(key_t, 4);
12310# endif
12311 key_t key;
12312 mIPCSem = -1;
12313 mIPCKey = "0";
12314 for (uint32_t i = 0; i < 1 << 24; i++)
12315 {
12316 key = ((uint32_t)'V' << 24) | i;
12317 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12318 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12319 {
12320 mIPCSem = sem;
12321 if (sem >= 0)
12322 mIPCKey = BstrFmt("%u", key);
12323 break;
12324 }
12325 }
12326# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12327 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12328 char *pszSemName = NULL;
12329 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12330 key_t key = ::ftok(pszSemName, 'V');
12331 RTStrFree(pszSemName);
12332
12333 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12334# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12335
12336 int errnoSave = errno;
12337 if (mIPCSem < 0 && errnoSave == ENOSYS)
12338 {
12339 setError(E_FAIL,
12340 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12341 "support for SysV IPC. Check the host kernel configuration for "
12342 "CONFIG_SYSVIPC=y"));
12343 return E_FAIL;
12344 }
12345 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12346 * the IPC semaphores */
12347 if (mIPCSem < 0 && errnoSave == ENOSPC)
12348 {
12349#ifdef RT_OS_LINUX
12350 setError(E_FAIL,
12351 tr("Cannot create IPC semaphore because the system limit for the "
12352 "maximum number of semaphore sets (SEMMNI), or the system wide "
12353 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12354 "current set of SysV IPC semaphores can be determined from "
12355 "the file /proc/sysvipc/sem"));
12356#else
12357 setError(E_FAIL,
12358 tr("Cannot create IPC semaphore because the system-imposed limit "
12359 "on the maximum number of allowed semaphores or semaphore "
12360 "identifiers system-wide would be exceeded"));
12361#endif
12362 return E_FAIL;
12363 }
12364 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12365 E_FAIL);
12366 /* set the initial value to 1 */
12367 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12368 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12369 E_FAIL);
12370#else
12371# error "Port me!"
12372#endif
12373
12374 /* memorize the peer Machine */
12375 unconst(mPeer) = aMachine;
12376 /* share the parent pointer */
12377 unconst(mParent) = aMachine->mParent;
12378
12379 /* take the pointers to data to share */
12380 mData.share(aMachine->mData);
12381 mSSData.share(aMachine->mSSData);
12382
12383 mUserData.share(aMachine->mUserData);
12384 mHWData.share(aMachine->mHWData);
12385 mMediaData.share(aMachine->mMediaData);
12386
12387 mStorageControllers.allocate();
12388 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12389 it != aMachine->mStorageControllers->end();
12390 ++it)
12391 {
12392 ComObjPtr<StorageController> ctl;
12393 ctl.createObject();
12394 ctl->init(this, *it);
12395 mStorageControllers->push_back(ctl);
12396 }
12397
12398 unconst(mBIOSSettings).createObject();
12399 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12400 /* create another VRDEServer object that will be mutable */
12401 unconst(mVRDEServer).createObject();
12402 mVRDEServer->init(this, aMachine->mVRDEServer);
12403 /* create another audio adapter object that will be mutable */
12404 unconst(mAudioAdapter).createObject();
12405 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12406 /* create a list of serial ports that will be mutable */
12407 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12408 {
12409 unconst(mSerialPorts[slot]).createObject();
12410 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12411 }
12412 /* create a list of parallel ports that will be mutable */
12413 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12414 {
12415 unconst(mParallelPorts[slot]).createObject();
12416 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12417 }
12418 /* create another USB controller object that will be mutable */
12419 unconst(mUSBController).createObject();
12420 mUSBController->init(this, aMachine->mUSBController);
12421
12422 /* create a list of network adapters that will be mutable */
12423 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12424 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12425 {
12426 unconst(mNetworkAdapters[slot]).createObject();
12427 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12428 }
12429
12430 /* create another bandwidth control object that will be mutable */
12431 unconst(mBandwidthControl).createObject();
12432 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12433
12434 /* default is to delete saved state on Saved -> PoweredOff transition */
12435 mRemoveSavedState = true;
12436
12437 /* Confirm a successful initialization when it's the case */
12438 autoInitSpan.setSucceeded();
12439
12440 LogFlowThisFuncLeave();
12441 return S_OK;
12442}
12443
12444/**
12445 * Uninitializes this session object. If the reason is other than
12446 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12447 *
12448 * @param aReason uninitialization reason
12449 *
12450 * @note Locks mParent + this object for writing.
12451 */
12452void SessionMachine::uninit(Uninit::Reason aReason)
12453{
12454 LogFlowThisFuncEnter();
12455 LogFlowThisFunc(("reason=%d\n", aReason));
12456
12457 /*
12458 * Strongly reference ourselves to prevent this object deletion after
12459 * mData->mSession.mMachine.setNull() below (which can release the last
12460 * reference and call the destructor). Important: this must be done before
12461 * accessing any members (and before AutoUninitSpan that does it as well).
12462 * This self reference will be released as the very last step on return.
12463 */
12464 ComObjPtr<SessionMachine> selfRef = this;
12465
12466 /* Enclose the state transition Ready->InUninit->NotReady */
12467 AutoUninitSpan autoUninitSpan(this);
12468 if (autoUninitSpan.uninitDone())
12469 {
12470 LogFlowThisFunc(("Already uninitialized\n"));
12471 LogFlowThisFuncLeave();
12472 return;
12473 }
12474
12475 if (autoUninitSpan.initFailed())
12476 {
12477 /* We've been called by init() because it's failed. It's not really
12478 * necessary (nor it's safe) to perform the regular uninit sequence
12479 * below, the following is enough.
12480 */
12481 LogFlowThisFunc(("Initialization failed.\n"));
12482#if defined(RT_OS_WINDOWS)
12483 if (mIPCSem)
12484 ::CloseHandle(mIPCSem);
12485 mIPCSem = NULL;
12486#elif defined(RT_OS_OS2)
12487 if (mIPCSem != NULLHANDLE)
12488 ::DosCloseMutexSem(mIPCSem);
12489 mIPCSem = NULLHANDLE;
12490#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12491 if (mIPCSem >= 0)
12492 ::semctl(mIPCSem, 0, IPC_RMID);
12493 mIPCSem = -1;
12494# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12495 mIPCKey = "0";
12496# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12497#else
12498# error "Port me!"
12499#endif
12500 uninitDataAndChildObjects();
12501 mData.free();
12502 unconst(mParent) = NULL;
12503 unconst(mPeer) = NULL;
12504 LogFlowThisFuncLeave();
12505 return;
12506 }
12507
12508 MachineState_T lastState;
12509 {
12510 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12511 lastState = mData->mMachineState;
12512 }
12513 NOREF(lastState);
12514
12515#ifdef VBOX_WITH_USB
12516 // release all captured USB devices, but do this before requesting the locks below
12517 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12518 {
12519 /* Console::captureUSBDevices() is called in the VM process only after
12520 * setting the machine state to Starting or Restoring.
12521 * Console::detachAllUSBDevices() will be called upon successful
12522 * termination. So, we need to release USB devices only if there was
12523 * an abnormal termination of a running VM.
12524 *
12525 * This is identical to SessionMachine::DetachAllUSBDevices except
12526 * for the aAbnormal argument. */
12527 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12528 AssertComRC(rc);
12529 NOREF(rc);
12530
12531 USBProxyService *service = mParent->host()->usbProxyService();
12532 if (service)
12533 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12534 }
12535#endif /* VBOX_WITH_USB */
12536
12537 // we need to lock this object in uninit() because the lock is shared
12538 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12539 // and others need mParent lock, and USB needs host lock.
12540 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12541
12542#if 0
12543 // Trigger async cleanup tasks, avoid doing things here which are not
12544 // vital to be done immediately and maybe need more locks. This calls
12545 // Machine::unregisterMetrics().
12546 mParent->onMachineUninit(mPeer);
12547#else
12548 /*
12549 * It is safe to call Machine::unregisterMetrics() here because
12550 * PerformanceCollector::samplerCallback no longer accesses guest methods
12551 * holding the lock.
12552 */
12553 unregisterMetrics(mParent->performanceCollector(), mPeer);
12554#endif
12555 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12556 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12557 this, __PRETTY_FUNCTION__, mCollectorGuest));
12558 if (mCollectorGuest)
12559 {
12560 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12561 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12562 mCollectorGuest = NULL;
12563 }
12564
12565 if (aReason == Uninit::Abnormal)
12566 {
12567 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12568 Global::IsOnlineOrTransient(lastState)));
12569
12570 /* reset the state to Aborted */
12571 if (mData->mMachineState != MachineState_Aborted)
12572 setMachineState(MachineState_Aborted);
12573 }
12574
12575 // any machine settings modified?
12576 if (mData->flModifications)
12577 {
12578 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12579 rollback(false /* aNotify */);
12580 }
12581
12582 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12583 || !mConsoleTaskData.mSnapshot);
12584 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12585 {
12586 LogWarningThisFunc(("canceling failed save state request!\n"));
12587 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12588 }
12589 else if (!mConsoleTaskData.mSnapshot.isNull())
12590 {
12591 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12592
12593 /* delete all differencing hard disks created (this will also attach
12594 * their parents back by rolling back mMediaData) */
12595 rollbackMedia();
12596
12597 // delete the saved state file (it might have been already created)
12598 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12599 // think it's still in use
12600 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12601 mConsoleTaskData.mSnapshot->uninit();
12602 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12603 }
12604
12605 if (!mData->mSession.mType.isEmpty())
12606 {
12607 /* mType is not null when this machine's process has been started by
12608 * Machine::LaunchVMProcess(), therefore it is our child. We
12609 * need to queue the PID to reap the process (and avoid zombies on
12610 * Linux). */
12611 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12612 mParent->addProcessToReap(mData->mSession.mPID);
12613 }
12614
12615 mData->mSession.mPID = NIL_RTPROCESS;
12616
12617 if (aReason == Uninit::Unexpected)
12618 {
12619 /* Uninitialization didn't come from #checkForDeath(), so tell the
12620 * client watcher thread to update the set of machines that have open
12621 * sessions. */
12622 mParent->updateClientWatcher();
12623 }
12624
12625 /* uninitialize all remote controls */
12626 if (mData->mSession.mRemoteControls.size())
12627 {
12628 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12629 mData->mSession.mRemoteControls.size()));
12630
12631 Data::Session::RemoteControlList::iterator it =
12632 mData->mSession.mRemoteControls.begin();
12633 while (it != mData->mSession.mRemoteControls.end())
12634 {
12635 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12636 HRESULT rc = (*it)->Uninitialize();
12637 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12638 if (FAILED(rc))
12639 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12640 ++it;
12641 }
12642 mData->mSession.mRemoteControls.clear();
12643 }
12644
12645 /*
12646 * An expected uninitialization can come only from #checkForDeath().
12647 * Otherwise it means that something's gone really wrong (for example,
12648 * the Session implementation has released the VirtualBox reference
12649 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12650 * etc). However, it's also possible, that the client releases the IPC
12651 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12652 * but the VirtualBox release event comes first to the server process.
12653 * This case is practically possible, so we should not assert on an
12654 * unexpected uninit, just log a warning.
12655 */
12656
12657 if ((aReason == Uninit::Unexpected))
12658 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12659
12660 if (aReason != Uninit::Normal)
12661 {
12662 mData->mSession.mDirectControl.setNull();
12663 }
12664 else
12665 {
12666 /* this must be null here (see #OnSessionEnd()) */
12667 Assert(mData->mSession.mDirectControl.isNull());
12668 Assert(mData->mSession.mState == SessionState_Unlocking);
12669 Assert(!mData->mSession.mProgress.isNull());
12670 }
12671 if (mData->mSession.mProgress)
12672 {
12673 if (aReason == Uninit::Normal)
12674 mData->mSession.mProgress->notifyComplete(S_OK);
12675 else
12676 mData->mSession.mProgress->notifyComplete(E_FAIL,
12677 COM_IIDOF(ISession),
12678 getComponentName(),
12679 tr("The VM session was aborted"));
12680 mData->mSession.mProgress.setNull();
12681 }
12682
12683 /* remove the association between the peer machine and this session machine */
12684 Assert( (SessionMachine*)mData->mSession.mMachine == this
12685 || aReason == Uninit::Unexpected);
12686
12687 /* reset the rest of session data */
12688 mData->mSession.mMachine.setNull();
12689 mData->mSession.mState = SessionState_Unlocked;
12690 mData->mSession.mType.setNull();
12691
12692 /* close the interprocess semaphore before leaving the exclusive lock */
12693#if defined(RT_OS_WINDOWS)
12694 if (mIPCSem)
12695 ::CloseHandle(mIPCSem);
12696 mIPCSem = NULL;
12697#elif defined(RT_OS_OS2)
12698 if (mIPCSem != NULLHANDLE)
12699 ::DosCloseMutexSem(mIPCSem);
12700 mIPCSem = NULLHANDLE;
12701#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12702 if (mIPCSem >= 0)
12703 ::semctl(mIPCSem, 0, IPC_RMID);
12704 mIPCSem = -1;
12705# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12706 mIPCKey = "0";
12707# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12708#else
12709# error "Port me!"
12710#endif
12711
12712 /* fire an event */
12713 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12714
12715 uninitDataAndChildObjects();
12716
12717 /* free the essential data structure last */
12718 mData.free();
12719
12720 /* release the exclusive lock before setting the below two to NULL */
12721 multilock.release();
12722
12723 unconst(mParent) = NULL;
12724 unconst(mPeer) = NULL;
12725
12726 LogFlowThisFuncLeave();
12727}
12728
12729// util::Lockable interface
12730////////////////////////////////////////////////////////////////////////////////
12731
12732/**
12733 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12734 * with the primary Machine instance (mPeer).
12735 */
12736RWLockHandle *SessionMachine::lockHandle() const
12737{
12738 AssertReturn(mPeer != NULL, NULL);
12739 return mPeer->lockHandle();
12740}
12741
12742// IInternalMachineControl methods
12743////////////////////////////////////////////////////////////////////////////////
12744
12745/**
12746 * Passes collected guest statistics to performance collector object
12747 */
12748STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12749 ULONG aCpuKernel, ULONG aCpuIdle,
12750 ULONG aMemTotal, ULONG aMemFree,
12751 ULONG aMemBalloon, ULONG aMemShared,
12752 ULONG aMemCache, ULONG aPageTotal,
12753 ULONG aAllocVMM, ULONG aFreeVMM,
12754 ULONG aBalloonedVMM, ULONG aSharedVMM,
12755 ULONG aVmNetRx, ULONG aVmNetTx)
12756{
12757 if (mCollectorGuest)
12758 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12759 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12760 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12761 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12762
12763 return S_OK;
12764}
12765
12766/**
12767 * @note Locks this object for writing.
12768 */
12769STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12770{
12771 AutoCaller autoCaller(this);
12772 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12773
12774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12775
12776 mRemoveSavedState = aRemove;
12777
12778 return S_OK;
12779}
12780
12781/**
12782 * @note Locks the same as #setMachineState() does.
12783 */
12784STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12785{
12786 return setMachineState(aMachineState);
12787}
12788
12789/**
12790 * @note Locks this object for reading.
12791 */
12792STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12793{
12794 AutoCaller autoCaller(this);
12795 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12796
12797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12798
12799#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12800 mIPCSemName.cloneTo(aId);
12801 return S_OK;
12802#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12803# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12804 mIPCKey.cloneTo(aId);
12805# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12806 mData->m_strConfigFileFull.cloneTo(aId);
12807# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12808 return S_OK;
12809#else
12810# error "Port me!"
12811#endif
12812}
12813
12814/**
12815 * @note Locks this object for writing.
12816 */
12817STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12818{
12819 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12820 AutoCaller autoCaller(this);
12821 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12822
12823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12824
12825 if (mData->mSession.mState != SessionState_Locked)
12826 return VBOX_E_INVALID_OBJECT_STATE;
12827
12828 if (!mData->mSession.mProgress.isNull())
12829 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12830
12831 LogFlowThisFunc(("returns S_OK.\n"));
12832 return S_OK;
12833}
12834
12835/**
12836 * @note Locks this object for writing.
12837 */
12838STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12839{
12840 AutoCaller autoCaller(this);
12841 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12842
12843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12844
12845 if (mData->mSession.mState != SessionState_Locked)
12846 return VBOX_E_INVALID_OBJECT_STATE;
12847
12848 /* Finalize the LaunchVMProcess progress object. */
12849 if (mData->mSession.mProgress)
12850 {
12851 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12852 mData->mSession.mProgress.setNull();
12853 }
12854
12855 if (SUCCEEDED((HRESULT)iResult))
12856 {
12857#ifdef VBOX_WITH_RESOURCE_USAGE_API
12858 /* The VM has been powered up successfully, so it makes sense
12859 * now to offer the performance metrics for a running machine
12860 * object. Doing it earlier wouldn't be safe. */
12861 registerMetrics(mParent->performanceCollector(), mPeer,
12862 mData->mSession.mPID);
12863#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12864 }
12865
12866 return S_OK;
12867}
12868
12869/**
12870 * @note Locks this object for writing.
12871 */
12872STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12873{
12874 LogFlowThisFuncEnter();
12875
12876 CheckComArgOutPointerValid(aProgress);
12877
12878 AutoCaller autoCaller(this);
12879 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12880
12881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12882
12883 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12884 E_FAIL);
12885
12886 /* create a progress object to track operation completion */
12887 ComObjPtr<Progress> pProgress;
12888 pProgress.createObject();
12889 pProgress->init(getVirtualBox(),
12890 static_cast<IMachine *>(this) /* aInitiator */,
12891 Bstr(tr("Stopping the virtual machine")).raw(),
12892 FALSE /* aCancelable */);
12893
12894 /* fill in the console task data */
12895 mConsoleTaskData.mLastState = mData->mMachineState;
12896 mConsoleTaskData.mProgress = pProgress;
12897
12898 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12899 setMachineState(MachineState_Stopping);
12900
12901 pProgress.queryInterfaceTo(aProgress);
12902
12903 return S_OK;
12904}
12905
12906/**
12907 * @note Locks this object for writing.
12908 */
12909STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12910{
12911 LogFlowThisFuncEnter();
12912
12913 AutoCaller autoCaller(this);
12914 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12915
12916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12917
12918 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12919 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12920 && mConsoleTaskData.mLastState != MachineState_Null,
12921 E_FAIL);
12922
12923 /*
12924 * On failure, set the state to the state we had when BeginPoweringDown()
12925 * was called (this is expected by Console::PowerDown() and the associated
12926 * task). On success the VM process already changed the state to
12927 * MachineState_PoweredOff, so no need to do anything.
12928 */
12929 if (FAILED(iResult))
12930 setMachineState(mConsoleTaskData.mLastState);
12931
12932 /* notify the progress object about operation completion */
12933 Assert(mConsoleTaskData.mProgress);
12934 if (SUCCEEDED(iResult))
12935 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12936 else
12937 {
12938 Utf8Str strErrMsg(aErrMsg);
12939 if (strErrMsg.length())
12940 mConsoleTaskData.mProgress->notifyComplete(iResult,
12941 COM_IIDOF(ISession),
12942 getComponentName(),
12943 strErrMsg.c_str());
12944 else
12945 mConsoleTaskData.mProgress->notifyComplete(iResult);
12946 }
12947
12948 /* clear out the temporary saved state data */
12949 mConsoleTaskData.mLastState = MachineState_Null;
12950 mConsoleTaskData.mProgress.setNull();
12951
12952 LogFlowThisFuncLeave();
12953 return S_OK;
12954}
12955
12956
12957/**
12958 * Goes through the USB filters of the given machine to see if the given
12959 * device matches any filter or not.
12960 *
12961 * @note Locks the same as USBController::hasMatchingFilter() does.
12962 */
12963STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12964 BOOL *aMatched,
12965 ULONG *aMaskedIfs)
12966{
12967 LogFlowThisFunc(("\n"));
12968
12969 CheckComArgNotNull(aUSBDevice);
12970 CheckComArgOutPointerValid(aMatched);
12971
12972 AutoCaller autoCaller(this);
12973 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12974
12975#ifdef VBOX_WITH_USB
12976 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12977#else
12978 NOREF(aUSBDevice);
12979 NOREF(aMaskedIfs);
12980 *aMatched = FALSE;
12981#endif
12982
12983 return S_OK;
12984}
12985
12986/**
12987 * @note Locks the same as Host::captureUSBDevice() does.
12988 */
12989STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12990{
12991 LogFlowThisFunc(("\n"));
12992
12993 AutoCaller autoCaller(this);
12994 AssertComRCReturnRC(autoCaller.rc());
12995
12996#ifdef VBOX_WITH_USB
12997 /* if captureDeviceForVM() fails, it must have set extended error info */
12998 clearError();
12999 MultiResult rc = mParent->host()->checkUSBProxyService();
13000 if (FAILED(rc)) return rc;
13001
13002 USBProxyService *service = mParent->host()->usbProxyService();
13003 AssertReturn(service, E_FAIL);
13004 return service->captureDeviceForVM(this, Guid(aId).ref());
13005#else
13006 NOREF(aId);
13007 return E_NOTIMPL;
13008#endif
13009}
13010
13011/**
13012 * @note Locks the same as Host::detachUSBDevice() does.
13013 */
13014STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13015{
13016 LogFlowThisFunc(("\n"));
13017
13018 AutoCaller autoCaller(this);
13019 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13020
13021#ifdef VBOX_WITH_USB
13022 USBProxyService *service = mParent->host()->usbProxyService();
13023 AssertReturn(service, E_FAIL);
13024 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13025#else
13026 NOREF(aId);
13027 NOREF(aDone);
13028 return E_NOTIMPL;
13029#endif
13030}
13031
13032/**
13033 * Inserts all machine filters to the USB proxy service and then calls
13034 * Host::autoCaptureUSBDevices().
13035 *
13036 * Called by Console from the VM process upon VM startup.
13037 *
13038 * @note Locks what called methods lock.
13039 */
13040STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13041{
13042 LogFlowThisFunc(("\n"));
13043
13044 AutoCaller autoCaller(this);
13045 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13046
13047#ifdef VBOX_WITH_USB
13048 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
13049 AssertComRC(rc);
13050 NOREF(rc);
13051
13052 USBProxyService *service = mParent->host()->usbProxyService();
13053 AssertReturn(service, E_FAIL);
13054 return service->autoCaptureDevicesForVM(this);
13055#else
13056 return S_OK;
13057#endif
13058}
13059
13060/**
13061 * Removes all machine filters from the USB proxy service and then calls
13062 * Host::detachAllUSBDevices().
13063 *
13064 * Called by Console from the VM process upon normal VM termination or by
13065 * SessionMachine::uninit() upon abnormal VM termination (from under the
13066 * Machine/SessionMachine lock).
13067 *
13068 * @note Locks what called methods lock.
13069 */
13070STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13071{
13072 LogFlowThisFunc(("\n"));
13073
13074 AutoCaller autoCaller(this);
13075 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13076
13077#ifdef VBOX_WITH_USB
13078 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
13079 AssertComRC(rc);
13080 NOREF(rc);
13081
13082 USBProxyService *service = mParent->host()->usbProxyService();
13083 AssertReturn(service, E_FAIL);
13084 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13085#else
13086 NOREF(aDone);
13087 return S_OK;
13088#endif
13089}
13090
13091/**
13092 * @note Locks this object for writing.
13093 */
13094STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13095 IProgress **aProgress)
13096{
13097 LogFlowThisFuncEnter();
13098
13099 AssertReturn(aSession, E_INVALIDARG);
13100 AssertReturn(aProgress, E_INVALIDARG);
13101
13102 AutoCaller autoCaller(this);
13103
13104 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13105 /*
13106 * We don't assert below because it might happen that a non-direct session
13107 * informs us it is closed right after we've been uninitialized -- it's ok.
13108 */
13109 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13110
13111 /* get IInternalSessionControl interface */
13112 ComPtr<IInternalSessionControl> control(aSession);
13113
13114 ComAssertRet(!control.isNull(), E_INVALIDARG);
13115
13116 /* Creating a Progress object requires the VirtualBox lock, and
13117 * thus locking it here is required by the lock order rules. */
13118 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13119
13120 if (control == mData->mSession.mDirectControl)
13121 {
13122 ComAssertRet(aProgress, E_POINTER);
13123
13124 /* The direct session is being normally closed by the client process
13125 * ----------------------------------------------------------------- */
13126
13127 /* go to the closing state (essential for all open*Session() calls and
13128 * for #checkForDeath()) */
13129 Assert(mData->mSession.mState == SessionState_Locked);
13130 mData->mSession.mState = SessionState_Unlocking;
13131
13132 /* set direct control to NULL to release the remote instance */
13133 mData->mSession.mDirectControl.setNull();
13134 LogFlowThisFunc(("Direct control is set to NULL\n"));
13135
13136 if (mData->mSession.mProgress)
13137 {
13138 /* finalize the progress, someone might wait if a frontend
13139 * closes the session before powering on the VM. */
13140 mData->mSession.mProgress->notifyComplete(E_FAIL,
13141 COM_IIDOF(ISession),
13142 getComponentName(),
13143 tr("The VM session was closed before any attempt to power it on"));
13144 mData->mSession.mProgress.setNull();
13145 }
13146
13147 /* Create the progress object the client will use to wait until
13148 * #checkForDeath() is called to uninitialize this session object after
13149 * it releases the IPC semaphore.
13150 * Note! Because we're "reusing" mProgress here, this must be a proxy
13151 * object just like for LaunchVMProcess. */
13152 Assert(mData->mSession.mProgress.isNull());
13153 ComObjPtr<ProgressProxy> progress;
13154 progress.createObject();
13155 ComPtr<IUnknown> pPeer(mPeer);
13156 progress->init(mParent, pPeer,
13157 Bstr(tr("Closing session")).raw(),
13158 FALSE /* aCancelable */);
13159 progress.queryInterfaceTo(aProgress);
13160 mData->mSession.mProgress = progress;
13161 }
13162 else
13163 {
13164 /* the remote session is being normally closed */
13165 Data::Session::RemoteControlList::iterator it =
13166 mData->mSession.mRemoteControls.begin();
13167 while (it != mData->mSession.mRemoteControls.end())
13168 {
13169 if (control == *it)
13170 break;
13171 ++it;
13172 }
13173 BOOL found = it != mData->mSession.mRemoteControls.end();
13174 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13175 E_INVALIDARG);
13176 // This MUST be erase(it), not remove(*it) as the latter triggers a
13177 // very nasty use after free due to the place where the value "lives".
13178 mData->mSession.mRemoteControls.erase(it);
13179 }
13180
13181 /* signal the client watcher thread, because the client is going away */
13182 mParent->updateClientWatcher();
13183
13184 LogFlowThisFuncLeave();
13185 return S_OK;
13186}
13187
13188/**
13189 * @note Locks this object for writing.
13190 */
13191STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13192{
13193 LogFlowThisFuncEnter();
13194
13195 CheckComArgOutPointerValid(aProgress);
13196 CheckComArgOutPointerValid(aStateFilePath);
13197
13198 AutoCaller autoCaller(this);
13199 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13200
13201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13202
13203 AssertReturn( mData->mMachineState == MachineState_Paused
13204 && mConsoleTaskData.mLastState == MachineState_Null
13205 && mConsoleTaskData.strStateFilePath.isEmpty(),
13206 E_FAIL);
13207
13208 /* create a progress object to track operation completion */
13209 ComObjPtr<Progress> pProgress;
13210 pProgress.createObject();
13211 pProgress->init(getVirtualBox(),
13212 static_cast<IMachine *>(this) /* aInitiator */,
13213 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13214 FALSE /* aCancelable */);
13215
13216 Utf8Str strStateFilePath;
13217 /* stateFilePath is null when the machine is not running */
13218 if (mData->mMachineState == MachineState_Paused)
13219 composeSavedStateFilename(strStateFilePath);
13220
13221 /* fill in the console task data */
13222 mConsoleTaskData.mLastState = mData->mMachineState;
13223 mConsoleTaskData.strStateFilePath = strStateFilePath;
13224 mConsoleTaskData.mProgress = pProgress;
13225
13226 /* set the state to Saving (this is expected by Console::SaveState()) */
13227 setMachineState(MachineState_Saving);
13228
13229 strStateFilePath.cloneTo(aStateFilePath);
13230 pProgress.queryInterfaceTo(aProgress);
13231
13232 return S_OK;
13233}
13234
13235/**
13236 * @note Locks mParent + this object for writing.
13237 */
13238STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13239{
13240 LogFlowThisFunc(("\n"));
13241
13242 AutoCaller autoCaller(this);
13243 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13244
13245 /* endSavingState() need mParent lock */
13246 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13247
13248 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13249 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13250 && mConsoleTaskData.mLastState != MachineState_Null
13251 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13252 E_FAIL);
13253
13254 /*
13255 * On failure, set the state to the state we had when BeginSavingState()
13256 * was called (this is expected by Console::SaveState() and the associated
13257 * task). On success the VM process already changed the state to
13258 * MachineState_Saved, so no need to do anything.
13259 */
13260 if (FAILED(iResult))
13261 setMachineState(mConsoleTaskData.mLastState);
13262
13263 return endSavingState(iResult, aErrMsg);
13264}
13265
13266/**
13267 * @note Locks this object for writing.
13268 */
13269STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13270{
13271 LogFlowThisFunc(("\n"));
13272
13273 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13274
13275 AutoCaller autoCaller(this);
13276 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13277
13278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13279
13280 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13281 || mData->mMachineState == MachineState_Teleported
13282 || mData->mMachineState == MachineState_Aborted
13283 , E_FAIL); /** @todo setError. */
13284
13285 Utf8Str stateFilePathFull = aSavedStateFile;
13286 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13287 if (RT_FAILURE(vrc))
13288 return setError(VBOX_E_FILE_ERROR,
13289 tr("Invalid saved state file path '%ls' (%Rrc)"),
13290 aSavedStateFile,
13291 vrc);
13292
13293 mSSData->strStateFilePath = stateFilePathFull;
13294
13295 /* The below setMachineState() will detect the state transition and will
13296 * update the settings file */
13297
13298 return setMachineState(MachineState_Saved);
13299}
13300
13301STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13302 ComSafeArrayOut(BSTR, aValues),
13303 ComSafeArrayOut(LONG64, aTimestamps),
13304 ComSafeArrayOut(BSTR, aFlags))
13305{
13306 LogFlowThisFunc(("\n"));
13307
13308#ifdef VBOX_WITH_GUEST_PROPS
13309 using namespace guestProp;
13310
13311 AutoCaller autoCaller(this);
13312 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13313
13314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13315
13316 CheckComArgOutSafeArrayPointerValid(aNames);
13317 CheckComArgOutSafeArrayPointerValid(aValues);
13318 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13319 CheckComArgOutSafeArrayPointerValid(aFlags);
13320
13321 size_t cEntries = mHWData->mGuestProperties.size();
13322 com::SafeArray<BSTR> names(cEntries);
13323 com::SafeArray<BSTR> values(cEntries);
13324 com::SafeArray<LONG64> timestamps(cEntries);
13325 com::SafeArray<BSTR> flags(cEntries);
13326 unsigned i = 0;
13327 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13328 it != mHWData->mGuestProperties.end();
13329 ++it)
13330 {
13331 char szFlags[MAX_FLAGS_LEN + 1];
13332 it->first.cloneTo(&names[i]);
13333 it->second.strValue.cloneTo(&values[i]);
13334 timestamps[i] = it->second.mTimestamp;
13335 /* If it is NULL, keep it NULL. */
13336 if (it->second.mFlags)
13337 {
13338 writeFlags(it->second.mFlags, szFlags);
13339 Bstr(szFlags).cloneTo(&flags[i]);
13340 }
13341 else
13342 flags[i] = NULL;
13343 ++i;
13344 }
13345 names.detachTo(ComSafeArrayOutArg(aNames));
13346 values.detachTo(ComSafeArrayOutArg(aValues));
13347 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13348 flags.detachTo(ComSafeArrayOutArg(aFlags));
13349 return S_OK;
13350#else
13351 ReturnComNotImplemented();
13352#endif
13353}
13354
13355STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13356 IN_BSTR aValue,
13357 LONG64 aTimestamp,
13358 IN_BSTR aFlags)
13359{
13360 LogFlowThisFunc(("\n"));
13361
13362#ifdef VBOX_WITH_GUEST_PROPS
13363 using namespace guestProp;
13364
13365 CheckComArgStrNotEmptyOrNull(aName);
13366 CheckComArgNotNull(aValue);
13367 CheckComArgNotNull(aFlags);
13368
13369 try
13370 {
13371 /*
13372 * Convert input up front.
13373 */
13374 Utf8Str utf8Name(aName);
13375 uint32_t fFlags = NILFLAG;
13376 if (aFlags)
13377 {
13378 Utf8Str utf8Flags(aFlags);
13379 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13380 AssertRCReturn(vrc, E_INVALIDARG);
13381 }
13382
13383 /*
13384 * Now grab the object lock, validate the state and do the update.
13385 */
13386 AutoCaller autoCaller(this);
13387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13388
13389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13390
13391 switch (mData->mMachineState)
13392 {
13393 case MachineState_Paused:
13394 case MachineState_Running:
13395 case MachineState_Teleporting:
13396 case MachineState_TeleportingPausedVM:
13397 case MachineState_LiveSnapshotting:
13398 case MachineState_DeletingSnapshotOnline:
13399 case MachineState_DeletingSnapshotPaused:
13400 case MachineState_Saving:
13401 case MachineState_Stopping:
13402 break;
13403
13404 default:
13405 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13406 VBOX_E_INVALID_VM_STATE);
13407 }
13408
13409 setModified(IsModified_MachineData);
13410 mHWData.backup();
13411
13412 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13413 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13414 if (it != mHWData->mGuestProperties.end())
13415 {
13416 if (!fDelete)
13417 {
13418 it->second.strValue = aValue;
13419 it->second.mTimestamp = aTimestamp;
13420 it->second.mFlags = fFlags;
13421 }
13422 else
13423 mHWData->mGuestProperties.erase(it);
13424
13425 mData->mGuestPropertiesModified = TRUE;
13426 }
13427 else if (!fDelete)
13428 {
13429 HWData::GuestProperty prop;
13430 prop.strValue = aValue;
13431 prop.mTimestamp = aTimestamp;
13432 prop.mFlags = fFlags;
13433
13434 mHWData->mGuestProperties[utf8Name] = prop;
13435 mData->mGuestPropertiesModified = TRUE;
13436 }
13437
13438 /*
13439 * Send a callback notification if appropriate
13440 */
13441 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13442 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13443 RTSTR_MAX,
13444 utf8Name.c_str(),
13445 RTSTR_MAX, NULL)
13446 )
13447 {
13448 alock.release();
13449
13450 mParent->onGuestPropertyChange(mData->mUuid,
13451 aName,
13452 aValue,
13453 aFlags);
13454 }
13455 }
13456 catch (...)
13457 {
13458 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13459 }
13460 return S_OK;
13461#else
13462 ReturnComNotImplemented();
13463#endif
13464}
13465
13466STDMETHODIMP SessionMachine::LockMedia()
13467{
13468 AutoCaller autoCaller(this);
13469 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13470
13471 AutoMultiWriteLock2 alock(this->lockHandle(),
13472 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13473
13474 AssertReturn( mData->mMachineState == MachineState_Starting
13475 || mData->mMachineState == MachineState_Restoring
13476 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13477
13478 clearError();
13479 alock.release();
13480 return lockMedia();
13481}
13482
13483STDMETHODIMP SessionMachine::UnlockMedia()
13484{
13485 unlockMedia();
13486 return S_OK;
13487}
13488
13489STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13490 IMediumAttachment **aNewAttachment)
13491{
13492 CheckComArgNotNull(aAttachment);
13493 CheckComArgOutPointerValid(aNewAttachment);
13494
13495 AutoCaller autoCaller(this);
13496 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13497
13498 // request the host lock first, since might be calling Host methods for getting host drives;
13499 // next, protect the media tree all the while we're in here, as well as our member variables
13500 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13501 this->lockHandle(),
13502 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13503
13504 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13505
13506 Bstr ctrlName;
13507 LONG lPort;
13508 LONG lDevice;
13509 bool fTempEject;
13510 {
13511 AutoCaller autoAttachCaller(this);
13512 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13513
13514 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13515
13516 /* Need to query the details first, as the IMediumAttachment reference
13517 * might be to the original settings, which we are going to change. */
13518 ctrlName = pAttach->getControllerName();
13519 lPort = pAttach->getPort();
13520 lDevice = pAttach->getDevice();
13521 fTempEject = pAttach->getTempEject();
13522 }
13523
13524 if (!fTempEject)
13525 {
13526 /* Remember previously mounted medium. The medium before taking the
13527 * backup is not necessarily the same thing. */
13528 ComObjPtr<Medium> oldmedium;
13529 oldmedium = pAttach->getMedium();
13530
13531 setModified(IsModified_Storage);
13532 mMediaData.backup();
13533
13534 // The backup operation makes the pAttach reference point to the
13535 // old settings. Re-get the correct reference.
13536 pAttach = findAttachment(mMediaData->mAttachments,
13537 ctrlName.raw(),
13538 lPort,
13539 lDevice);
13540
13541 {
13542 AutoCaller autoAttachCaller(this);
13543 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13544
13545 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13546 if (!oldmedium.isNull())
13547 oldmedium->removeBackReference(mData->mUuid);
13548
13549 pAttach->updateMedium(NULL);
13550 pAttach->updateEjected();
13551 }
13552
13553 setModified(IsModified_Storage);
13554 }
13555 else
13556 {
13557 {
13558 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13559 pAttach->updateEjected();
13560 }
13561 }
13562
13563 pAttach.queryInterfaceTo(aNewAttachment);
13564
13565 return S_OK;
13566}
13567
13568// public methods only for internal purposes
13569/////////////////////////////////////////////////////////////////////////////
13570
13571/**
13572 * Called from the client watcher thread to check for expected or unexpected
13573 * death of the client process that has a direct session to this machine.
13574 *
13575 * On Win32 and on OS/2, this method is called only when we've got the
13576 * mutex (i.e. the client has either died or terminated normally) so it always
13577 * returns @c true (the client is terminated, the session machine is
13578 * uninitialized).
13579 *
13580 * On other platforms, the method returns @c true if the client process has
13581 * terminated normally or abnormally and the session machine was uninitialized,
13582 * and @c false if the client process is still alive.
13583 *
13584 * @note Locks this object for writing.
13585 */
13586bool SessionMachine::checkForDeath()
13587{
13588 Uninit::Reason reason;
13589 bool terminated = false;
13590
13591 /* Enclose autoCaller with a block because calling uninit() from under it
13592 * will deadlock. */
13593 {
13594 AutoCaller autoCaller(this);
13595 if (!autoCaller.isOk())
13596 {
13597 /* return true if not ready, to cause the client watcher to exclude
13598 * the corresponding session from watching */
13599 LogFlowThisFunc(("Already uninitialized!\n"));
13600 return true;
13601 }
13602
13603 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13604
13605 /* Determine the reason of death: if the session state is Closing here,
13606 * everything is fine. Otherwise it means that the client did not call
13607 * OnSessionEnd() before it released the IPC semaphore. This may happen
13608 * either because the client process has abnormally terminated, or
13609 * because it simply forgot to call ISession::Close() before exiting. We
13610 * threat the latter also as an abnormal termination (see
13611 * Session::uninit() for details). */
13612 reason = mData->mSession.mState == SessionState_Unlocking ?
13613 Uninit::Normal :
13614 Uninit::Abnormal;
13615
13616#if defined(RT_OS_WINDOWS)
13617
13618 AssertMsg(mIPCSem, ("semaphore must be created"));
13619
13620 /* release the IPC mutex */
13621 ::ReleaseMutex(mIPCSem);
13622
13623 terminated = true;
13624
13625#elif defined(RT_OS_OS2)
13626
13627 AssertMsg(mIPCSem, ("semaphore must be created"));
13628
13629 /* release the IPC mutex */
13630 ::DosReleaseMutexSem(mIPCSem);
13631
13632 terminated = true;
13633
13634#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13635
13636 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13637
13638 int val = ::semctl(mIPCSem, 0, GETVAL);
13639 if (val > 0)
13640 {
13641 /* the semaphore is signaled, meaning the session is terminated */
13642 terminated = true;
13643 }
13644
13645#else
13646# error "Port me!"
13647#endif
13648
13649 } /* AutoCaller block */
13650
13651 if (terminated)
13652 uninit(reason);
13653
13654 return terminated;
13655}
13656
13657/**
13658 * @note Locks this object for reading.
13659 */
13660HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13661{
13662 LogFlowThisFunc(("\n"));
13663
13664 AutoCaller autoCaller(this);
13665 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13666
13667 ComPtr<IInternalSessionControl> directControl;
13668 {
13669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13670 directControl = mData->mSession.mDirectControl;
13671 }
13672
13673 /* ignore notifications sent after #OnSessionEnd() is called */
13674 if (!directControl)
13675 return S_OK;
13676
13677 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13678}
13679
13680/**
13681 * @note Locks this object for reading.
13682 */
13683HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13684 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13685{
13686 LogFlowThisFunc(("\n"));
13687
13688 AutoCaller autoCaller(this);
13689 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13690
13691 ComPtr<IInternalSessionControl> directControl;
13692 {
13693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13694 directControl = mData->mSession.mDirectControl;
13695 }
13696
13697 /* ignore notifications sent after #OnSessionEnd() is called */
13698 if (!directControl)
13699 return S_OK;
13700 /*
13701 * instead acting like callback we ask IVirtualBox deliver corresponding event
13702 */
13703
13704 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13705 return S_OK;
13706}
13707
13708/**
13709 * @note Locks this object for reading.
13710 */
13711HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13712{
13713 LogFlowThisFunc(("\n"));
13714
13715 AutoCaller autoCaller(this);
13716 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13717
13718 ComPtr<IInternalSessionControl> directControl;
13719 {
13720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13721 directControl = mData->mSession.mDirectControl;
13722 }
13723
13724 /* ignore notifications sent after #OnSessionEnd() is called */
13725 if (!directControl)
13726 return S_OK;
13727
13728 return directControl->OnSerialPortChange(serialPort);
13729}
13730
13731/**
13732 * @note Locks this object for reading.
13733 */
13734HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13735{
13736 LogFlowThisFunc(("\n"));
13737
13738 AutoCaller autoCaller(this);
13739 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13740
13741 ComPtr<IInternalSessionControl> directControl;
13742 {
13743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13744 directControl = mData->mSession.mDirectControl;
13745 }
13746
13747 /* ignore notifications sent after #OnSessionEnd() is called */
13748 if (!directControl)
13749 return S_OK;
13750
13751 return directControl->OnParallelPortChange(parallelPort);
13752}
13753
13754/**
13755 * @note Locks this object for reading.
13756 */
13757HRESULT SessionMachine::onStorageControllerChange()
13758{
13759 LogFlowThisFunc(("\n"));
13760
13761 AutoCaller autoCaller(this);
13762 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13763
13764 ComPtr<IInternalSessionControl> directControl;
13765 {
13766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13767 directControl = mData->mSession.mDirectControl;
13768 }
13769
13770 /* ignore notifications sent after #OnSessionEnd() is called */
13771 if (!directControl)
13772 return S_OK;
13773
13774 return directControl->OnStorageControllerChange();
13775}
13776
13777/**
13778 * @note Locks this object for reading.
13779 */
13780HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13781{
13782 LogFlowThisFunc(("\n"));
13783
13784 AutoCaller autoCaller(this);
13785 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13786
13787 ComPtr<IInternalSessionControl> directControl;
13788 {
13789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13790 directControl = mData->mSession.mDirectControl;
13791 }
13792
13793 /* ignore notifications sent after #OnSessionEnd() is called */
13794 if (!directControl)
13795 return S_OK;
13796
13797 return directControl->OnMediumChange(aAttachment, aForce);
13798}
13799
13800/**
13801 * @note Locks this object for reading.
13802 */
13803HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13804{
13805 LogFlowThisFunc(("\n"));
13806
13807 AutoCaller autoCaller(this);
13808 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13809
13810 ComPtr<IInternalSessionControl> directControl;
13811 {
13812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13813 directControl = mData->mSession.mDirectControl;
13814 }
13815
13816 /* ignore notifications sent after #OnSessionEnd() is called */
13817 if (!directControl)
13818 return S_OK;
13819
13820 return directControl->OnCPUChange(aCPU, aRemove);
13821}
13822
13823HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13824{
13825 LogFlowThisFunc(("\n"));
13826
13827 AutoCaller autoCaller(this);
13828 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13829
13830 ComPtr<IInternalSessionControl> directControl;
13831 {
13832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13833 directControl = mData->mSession.mDirectControl;
13834 }
13835
13836 /* ignore notifications sent after #OnSessionEnd() is called */
13837 if (!directControl)
13838 return S_OK;
13839
13840 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13841}
13842
13843/**
13844 * @note Locks this object for reading.
13845 */
13846HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13847{
13848 LogFlowThisFunc(("\n"));
13849
13850 AutoCaller autoCaller(this);
13851 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13852
13853 ComPtr<IInternalSessionControl> directControl;
13854 {
13855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13856 directControl = mData->mSession.mDirectControl;
13857 }
13858
13859 /* ignore notifications sent after #OnSessionEnd() is called */
13860 if (!directControl)
13861 return S_OK;
13862
13863 return directControl->OnVRDEServerChange(aRestart);
13864}
13865
13866/**
13867 * @note Locks this object for reading.
13868 */
13869HRESULT SessionMachine::onVideoCaptureChange()
13870{
13871 LogFlowThisFunc(("\n"));
13872
13873 AutoCaller autoCaller(this);
13874 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13875
13876 ComPtr<IInternalSessionControl> directControl;
13877 {
13878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13879 directControl = mData->mSession.mDirectControl;
13880 }
13881
13882 /* ignore notifications sent after #OnSessionEnd() is called */
13883 if (!directControl)
13884 return S_OK;
13885
13886 return directControl->OnVideoCaptureChange();
13887}
13888
13889/**
13890 * @note Locks this object for reading.
13891 */
13892HRESULT SessionMachine::onUSBControllerChange()
13893{
13894 LogFlowThisFunc(("\n"));
13895
13896 AutoCaller autoCaller(this);
13897 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13898
13899 ComPtr<IInternalSessionControl> directControl;
13900 {
13901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13902 directControl = mData->mSession.mDirectControl;
13903 }
13904
13905 /* ignore notifications sent after #OnSessionEnd() is called */
13906 if (!directControl)
13907 return S_OK;
13908
13909 return directControl->OnUSBControllerChange();
13910}
13911
13912/**
13913 * @note Locks this object for reading.
13914 */
13915HRESULT SessionMachine::onSharedFolderChange()
13916{
13917 LogFlowThisFunc(("\n"));
13918
13919 AutoCaller autoCaller(this);
13920 AssertComRCReturnRC(autoCaller.rc());
13921
13922 ComPtr<IInternalSessionControl> directControl;
13923 {
13924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13925 directControl = mData->mSession.mDirectControl;
13926 }
13927
13928 /* ignore notifications sent after #OnSessionEnd() is called */
13929 if (!directControl)
13930 return S_OK;
13931
13932 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13933}
13934
13935/**
13936 * @note Locks this object for reading.
13937 */
13938HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13939{
13940 LogFlowThisFunc(("\n"));
13941
13942 AutoCaller autoCaller(this);
13943 AssertComRCReturnRC(autoCaller.rc());
13944
13945 ComPtr<IInternalSessionControl> directControl;
13946 {
13947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13948 directControl = mData->mSession.mDirectControl;
13949 }
13950
13951 /* ignore notifications sent after #OnSessionEnd() is called */
13952 if (!directControl)
13953 return S_OK;
13954
13955 return directControl->OnClipboardModeChange(aClipboardMode);
13956}
13957
13958/**
13959 * @note Locks this object for reading.
13960 */
13961HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13962{
13963 LogFlowThisFunc(("\n"));
13964
13965 AutoCaller autoCaller(this);
13966 AssertComRCReturnRC(autoCaller.rc());
13967
13968 ComPtr<IInternalSessionControl> directControl;
13969 {
13970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13971 directControl = mData->mSession.mDirectControl;
13972 }
13973
13974 /* ignore notifications sent after #OnSessionEnd() is called */
13975 if (!directControl)
13976 return S_OK;
13977
13978 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13979}
13980
13981/**
13982 * @note Locks this object for reading.
13983 */
13984HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13985{
13986 LogFlowThisFunc(("\n"));
13987
13988 AutoCaller autoCaller(this);
13989 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13990
13991 ComPtr<IInternalSessionControl> directControl;
13992 {
13993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13994 directControl = mData->mSession.mDirectControl;
13995 }
13996
13997 /* ignore notifications sent after #OnSessionEnd() is called */
13998 if (!directControl)
13999 return S_OK;
14000
14001 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14002}
14003
14004/**
14005 * @note Locks this object for reading.
14006 */
14007HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14008{
14009 LogFlowThisFunc(("\n"));
14010
14011 AutoCaller autoCaller(this);
14012 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14013
14014 ComPtr<IInternalSessionControl> directControl;
14015 {
14016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14017 directControl = mData->mSession.mDirectControl;
14018 }
14019
14020 /* ignore notifications sent after #OnSessionEnd() is called */
14021 if (!directControl)
14022 return S_OK;
14023
14024 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14025}
14026
14027/**
14028 * Returns @c true if this machine's USB controller reports it has a matching
14029 * filter for the given USB device and @c false otherwise.
14030 *
14031 * @note locks this object for reading.
14032 */
14033bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14034{
14035 AutoCaller autoCaller(this);
14036 /* silently return if not ready -- this method may be called after the
14037 * direct machine session has been called */
14038 if (!autoCaller.isOk())
14039 return false;
14040
14041#ifdef VBOX_WITH_USB
14042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14043
14044 switch (mData->mMachineState)
14045 {
14046 case MachineState_Starting:
14047 case MachineState_Restoring:
14048 case MachineState_TeleportingIn:
14049 case MachineState_Paused:
14050 case MachineState_Running:
14051 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14052 * elsewhere... */
14053 alock.release();
14054 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
14055 default: break;
14056 }
14057#else
14058 NOREF(aDevice);
14059 NOREF(aMaskedIfs);
14060#endif
14061 return false;
14062}
14063
14064/**
14065 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14066 */
14067HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14068 IVirtualBoxErrorInfo *aError,
14069 ULONG aMaskedIfs)
14070{
14071 LogFlowThisFunc(("\n"));
14072
14073 AutoCaller autoCaller(this);
14074
14075 /* This notification may happen after the machine object has been
14076 * uninitialized (the session was closed), so don't assert. */
14077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14078
14079 ComPtr<IInternalSessionControl> directControl;
14080 {
14081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14082 directControl = mData->mSession.mDirectControl;
14083 }
14084
14085 /* fail on notifications sent after #OnSessionEnd() is called, it is
14086 * expected by the caller */
14087 if (!directControl)
14088 return E_FAIL;
14089
14090 /* No locks should be held at this point. */
14091 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14092 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14093
14094 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14095}
14096
14097/**
14098 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14099 */
14100HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14101 IVirtualBoxErrorInfo *aError)
14102{
14103 LogFlowThisFunc(("\n"));
14104
14105 AutoCaller autoCaller(this);
14106
14107 /* This notification may happen after the machine object has been
14108 * uninitialized (the session was closed), so don't assert. */
14109 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14110
14111 ComPtr<IInternalSessionControl> directControl;
14112 {
14113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14114 directControl = mData->mSession.mDirectControl;
14115 }
14116
14117 /* fail on notifications sent after #OnSessionEnd() is called, it is
14118 * expected by the caller */
14119 if (!directControl)
14120 return E_FAIL;
14121
14122 /* No locks should be held at this point. */
14123 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14124 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14125
14126 return directControl->OnUSBDeviceDetach(aId, aError);
14127}
14128
14129// protected methods
14130/////////////////////////////////////////////////////////////////////////////
14131
14132/**
14133 * Helper method to finalize saving the state.
14134 *
14135 * @note Must be called from under this object's lock.
14136 *
14137 * @param aRc S_OK if the snapshot has been taken successfully
14138 * @param aErrMsg human readable error message for failure
14139 *
14140 * @note Locks mParent + this objects for writing.
14141 */
14142HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14143{
14144 LogFlowThisFuncEnter();
14145
14146 AutoCaller autoCaller(this);
14147 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14148
14149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14150
14151 HRESULT rc = S_OK;
14152
14153 if (SUCCEEDED(aRc))
14154 {
14155 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14156
14157 /* save all VM settings */
14158 rc = saveSettings(NULL);
14159 // no need to check whether VirtualBox.xml needs saving also since
14160 // we can't have a name change pending at this point
14161 }
14162 else
14163 {
14164 // delete the saved state file (it might have been already created);
14165 // we need not check whether this is shared with a snapshot here because
14166 // we certainly created this saved state file here anew
14167 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14168 }
14169
14170 /* notify the progress object about operation completion */
14171 Assert(mConsoleTaskData.mProgress);
14172 if (SUCCEEDED(aRc))
14173 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14174 else
14175 {
14176 if (aErrMsg.length())
14177 mConsoleTaskData.mProgress->notifyComplete(aRc,
14178 COM_IIDOF(ISession),
14179 getComponentName(),
14180 aErrMsg.c_str());
14181 else
14182 mConsoleTaskData.mProgress->notifyComplete(aRc);
14183 }
14184
14185 /* clear out the temporary saved state data */
14186 mConsoleTaskData.mLastState = MachineState_Null;
14187 mConsoleTaskData.strStateFilePath.setNull();
14188 mConsoleTaskData.mProgress.setNull();
14189
14190 LogFlowThisFuncLeave();
14191 return rc;
14192}
14193
14194/**
14195 * Deletes the given file if it is no longer in use by either the current machine state
14196 * (if the machine is "saved") or any of the machine's snapshots.
14197 *
14198 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14199 * but is different for each SnapshotMachine. When calling this, the order of calling this
14200 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14201 * is therefore critical. I know, it's all rather messy.
14202 *
14203 * @param strStateFile
14204 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14205 */
14206void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14207 Snapshot *pSnapshotToIgnore)
14208{
14209 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14210 if ( (strStateFile.isNotEmpty())
14211 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14212 )
14213 // ... and it must also not be shared with other snapshots
14214 if ( !mData->mFirstSnapshot
14215 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14216 // this checks the SnapshotMachine's state file paths
14217 )
14218 RTFileDelete(strStateFile.c_str());
14219}
14220
14221/**
14222 * Locks the attached media.
14223 *
14224 * All attached hard disks are locked for writing and DVD/floppy are locked for
14225 * reading. Parents of attached hard disks (if any) are locked for reading.
14226 *
14227 * This method also performs accessibility check of all media it locks: if some
14228 * media is inaccessible, the method will return a failure and a bunch of
14229 * extended error info objects per each inaccessible medium.
14230 *
14231 * Note that this method is atomic: if it returns a success, all media are
14232 * locked as described above; on failure no media is locked at all (all
14233 * succeeded individual locks will be undone).
14234 *
14235 * The caller is responsible for doing the necessary state sanity checks.
14236 *
14237 * The locks made by this method must be undone by calling #unlockMedia() when
14238 * no more needed.
14239 */
14240HRESULT SessionMachine::lockMedia()
14241{
14242 AutoCaller autoCaller(this);
14243 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14244
14245 AutoMultiWriteLock2 alock(this->lockHandle(),
14246 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14247
14248 /* bail out if trying to lock things with already set up locking */
14249 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14250
14251 MultiResult mrc(S_OK);
14252
14253 /* Collect locking information for all medium objects attached to the VM. */
14254 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14255 it != mMediaData->mAttachments.end();
14256 ++it)
14257 {
14258 MediumAttachment* pAtt = *it;
14259 DeviceType_T devType = pAtt->getType();
14260 Medium *pMedium = pAtt->getMedium();
14261
14262 MediumLockList *pMediumLockList(new MediumLockList());
14263 // There can be attachments without a medium (floppy/dvd), and thus
14264 // it's impossible to create a medium lock list. It still makes sense
14265 // to have the empty medium lock list in the map in case a medium is
14266 // attached later.
14267 if (pMedium != NULL)
14268 {
14269 MediumType_T mediumType = pMedium->getType();
14270 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14271 || mediumType == MediumType_Shareable;
14272 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14273
14274 alock.release();
14275 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14276 !fIsReadOnlyLock /* fMediumLockWrite */,
14277 NULL,
14278 *pMediumLockList);
14279 alock.acquire();
14280 if (FAILED(mrc))
14281 {
14282 delete pMediumLockList;
14283 mData->mSession.mLockedMedia.Clear();
14284 break;
14285 }
14286 }
14287
14288 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14289 if (FAILED(rc))
14290 {
14291 mData->mSession.mLockedMedia.Clear();
14292 mrc = setError(rc,
14293 tr("Collecting locking information for all attached media failed"));
14294 break;
14295 }
14296 }
14297
14298 if (SUCCEEDED(mrc))
14299 {
14300 /* Now lock all media. If this fails, nothing is locked. */
14301 alock.release();
14302 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14303 alock.acquire();
14304 if (FAILED(rc))
14305 {
14306 mrc = setError(rc,
14307 tr("Locking of attached media failed"));
14308 }
14309 }
14310
14311 return mrc;
14312}
14313
14314/**
14315 * Undoes the locks made by by #lockMedia().
14316 */
14317void SessionMachine::unlockMedia()
14318{
14319 AutoCaller autoCaller(this);
14320 AssertComRCReturnVoid(autoCaller.rc());
14321
14322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14323
14324 /* we may be holding important error info on the current thread;
14325 * preserve it */
14326 ErrorInfoKeeper eik;
14327
14328 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14329 AssertComRC(rc);
14330}
14331
14332/**
14333 * Helper to change the machine state (reimplementation).
14334 *
14335 * @note Locks this object for writing.
14336 * @note This method must not call saveSettings or SaveSettings, otherwise
14337 * it can cause crashes in random places due to unexpectedly committing
14338 * the current settings. The caller is responsible for that. The call
14339 * to saveStateSettings is fine, because this method does not commit.
14340 */
14341HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14342{
14343 LogFlowThisFuncEnter();
14344 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14345
14346 AutoCaller autoCaller(this);
14347 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14348
14349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14350
14351 MachineState_T oldMachineState = mData->mMachineState;
14352
14353 AssertMsgReturn(oldMachineState != aMachineState,
14354 ("oldMachineState=%s, aMachineState=%s\n",
14355 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14356 E_FAIL);
14357
14358 HRESULT rc = S_OK;
14359
14360 int stsFlags = 0;
14361 bool deleteSavedState = false;
14362
14363 /* detect some state transitions */
14364
14365 if ( ( oldMachineState == MachineState_Saved
14366 && aMachineState == MachineState_Restoring)
14367 || ( ( oldMachineState == MachineState_PoweredOff
14368 || oldMachineState == MachineState_Teleported
14369 || oldMachineState == MachineState_Aborted
14370 )
14371 && ( aMachineState == MachineState_TeleportingIn
14372 || aMachineState == MachineState_Starting
14373 )
14374 )
14375 )
14376 {
14377 /* The EMT thread is about to start */
14378
14379 /* Nothing to do here for now... */
14380
14381 /// @todo NEWMEDIA don't let mDVDDrive and other children
14382 /// change anything when in the Starting/Restoring state
14383 }
14384 else if ( ( oldMachineState == MachineState_Running
14385 || oldMachineState == MachineState_Paused
14386 || oldMachineState == MachineState_Teleporting
14387 || oldMachineState == MachineState_LiveSnapshotting
14388 || oldMachineState == MachineState_Stuck
14389 || oldMachineState == MachineState_Starting
14390 || oldMachineState == MachineState_Stopping
14391 || oldMachineState == MachineState_Saving
14392 || oldMachineState == MachineState_Restoring
14393 || oldMachineState == MachineState_TeleportingPausedVM
14394 || oldMachineState == MachineState_TeleportingIn
14395 )
14396 && ( aMachineState == MachineState_PoweredOff
14397 || aMachineState == MachineState_Saved
14398 || aMachineState == MachineState_Teleported
14399 || aMachineState == MachineState_Aborted
14400 )
14401 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14402 * snapshot */
14403 && ( mConsoleTaskData.mSnapshot.isNull()
14404 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14405 )
14406 )
14407 {
14408 /* The EMT thread has just stopped, unlock attached media. Note that as
14409 * opposed to locking that is done from Console, we do unlocking here
14410 * because the VM process may have aborted before having a chance to
14411 * properly unlock all media it locked. */
14412
14413 unlockMedia();
14414 }
14415
14416 if (oldMachineState == MachineState_Restoring)
14417 {
14418 if (aMachineState != MachineState_Saved)
14419 {
14420 /*
14421 * delete the saved state file once the machine has finished
14422 * restoring from it (note that Console sets the state from
14423 * Restoring to Saved if the VM couldn't restore successfully,
14424 * to give the user an ability to fix an error and retry --
14425 * we keep the saved state file in this case)
14426 */
14427 deleteSavedState = true;
14428 }
14429 }
14430 else if ( oldMachineState == MachineState_Saved
14431 && ( aMachineState == MachineState_PoweredOff
14432 || aMachineState == MachineState_Aborted
14433 || aMachineState == MachineState_Teleported
14434 )
14435 )
14436 {
14437 /*
14438 * delete the saved state after Console::ForgetSavedState() is called
14439 * or if the VM process (owning a direct VM session) crashed while the
14440 * VM was Saved
14441 */
14442
14443 /// @todo (dmik)
14444 // Not sure that deleting the saved state file just because of the
14445 // client death before it attempted to restore the VM is a good
14446 // thing. But when it crashes we need to go to the Aborted state
14447 // which cannot have the saved state file associated... The only
14448 // way to fix this is to make the Aborted condition not a VM state
14449 // but a bool flag: i.e., when a crash occurs, set it to true and
14450 // change the state to PoweredOff or Saved depending on the
14451 // saved state presence.
14452
14453 deleteSavedState = true;
14454 mData->mCurrentStateModified = TRUE;
14455 stsFlags |= SaveSTS_CurStateModified;
14456 }
14457
14458 if ( aMachineState == MachineState_Starting
14459 || aMachineState == MachineState_Restoring
14460 || aMachineState == MachineState_TeleportingIn
14461 )
14462 {
14463 /* set the current state modified flag to indicate that the current
14464 * state is no more identical to the state in the
14465 * current snapshot */
14466 if (!mData->mCurrentSnapshot.isNull())
14467 {
14468 mData->mCurrentStateModified = TRUE;
14469 stsFlags |= SaveSTS_CurStateModified;
14470 }
14471 }
14472
14473 if (deleteSavedState)
14474 {
14475 if (mRemoveSavedState)
14476 {
14477 Assert(!mSSData->strStateFilePath.isEmpty());
14478
14479 // it is safe to delete the saved state file if ...
14480 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14481 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14482 // ... none of the snapshots share the saved state file
14483 )
14484 RTFileDelete(mSSData->strStateFilePath.c_str());
14485 }
14486
14487 mSSData->strStateFilePath.setNull();
14488 stsFlags |= SaveSTS_StateFilePath;
14489 }
14490
14491 /* redirect to the underlying peer machine */
14492 mPeer->setMachineState(aMachineState);
14493
14494 if ( aMachineState == MachineState_PoweredOff
14495 || aMachineState == MachineState_Teleported
14496 || aMachineState == MachineState_Aborted
14497 || aMachineState == MachineState_Saved)
14498 {
14499 /* the machine has stopped execution
14500 * (or the saved state file was adopted) */
14501 stsFlags |= SaveSTS_StateTimeStamp;
14502 }
14503
14504 if ( ( oldMachineState == MachineState_PoweredOff
14505 || oldMachineState == MachineState_Aborted
14506 || oldMachineState == MachineState_Teleported
14507 )
14508 && aMachineState == MachineState_Saved)
14509 {
14510 /* the saved state file was adopted */
14511 Assert(!mSSData->strStateFilePath.isEmpty());
14512 stsFlags |= SaveSTS_StateFilePath;
14513 }
14514
14515#ifdef VBOX_WITH_GUEST_PROPS
14516 if ( aMachineState == MachineState_PoweredOff
14517 || aMachineState == MachineState_Aborted
14518 || aMachineState == MachineState_Teleported)
14519 {
14520 /* Make sure any transient guest properties get removed from the
14521 * property store on shutdown. */
14522
14523 HWData::GuestPropertyMap::const_iterator it;
14524 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14525 if (!fNeedsSaving)
14526 for (it = mHWData->mGuestProperties.begin();
14527 it != mHWData->mGuestProperties.end(); ++it)
14528 if ( (it->second.mFlags & guestProp::TRANSIENT)
14529 || (it->second.mFlags & guestProp::TRANSRESET))
14530 {
14531 fNeedsSaving = true;
14532 break;
14533 }
14534 if (fNeedsSaving)
14535 {
14536 mData->mCurrentStateModified = TRUE;
14537 stsFlags |= SaveSTS_CurStateModified;
14538 }
14539 }
14540#endif
14541
14542 rc = saveStateSettings(stsFlags);
14543
14544 if ( ( oldMachineState != MachineState_PoweredOff
14545 && oldMachineState != MachineState_Aborted
14546 && oldMachineState != MachineState_Teleported
14547 )
14548 && ( aMachineState == MachineState_PoweredOff
14549 || aMachineState == MachineState_Aborted
14550 || aMachineState == MachineState_Teleported
14551 )
14552 )
14553 {
14554 /* we've been shut down for any reason */
14555 /* no special action so far */
14556 }
14557
14558 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14559 LogFlowThisFuncLeave();
14560 return rc;
14561}
14562
14563/**
14564 * Sends the current machine state value to the VM process.
14565 *
14566 * @note Locks this object for reading, then calls a client process.
14567 */
14568HRESULT SessionMachine::updateMachineStateOnClient()
14569{
14570 AutoCaller autoCaller(this);
14571 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14572
14573 ComPtr<IInternalSessionControl> directControl;
14574 {
14575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14576 AssertReturn(!!mData, E_FAIL);
14577 directControl = mData->mSession.mDirectControl;
14578
14579 /* directControl may be already set to NULL here in #OnSessionEnd()
14580 * called too early by the direct session process while there is still
14581 * some operation (like deleting the snapshot) in progress. The client
14582 * process in this case is waiting inside Session::close() for the
14583 * "end session" process object to complete, while #uninit() called by
14584 * #checkForDeath() on the Watcher thread is waiting for the pending
14585 * operation to complete. For now, we accept this inconsistent behavior
14586 * and simply do nothing here. */
14587
14588 if (mData->mSession.mState == SessionState_Unlocking)
14589 return S_OK;
14590
14591 AssertReturn(!directControl.isNull(), E_FAIL);
14592 }
14593
14594 return directControl->UpdateMachineState(mData->mMachineState);
14595}
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