VirtualBox

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

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

Main/VBoxManage: allow to enable video recording at VM runtime

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

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