VirtualBox

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

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

Main/MachineImpl: prevent changing video recording settings only if recording is currently enabled AND the VM is running

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 491.4 KB
Line 
1/* $Id: MachineImpl.cpp 46532 2013-06-13 13:57:26Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76#include <iprt/base64.h>
77
78#include <VBox/com/array.h>
79#include <VBox/com/list.h>
80
81#include <VBox/err.h>
82#include <VBox/param.h>
83#include <VBox/settings.h>
84#include <VBox/vmm/ssm.h>
85
86#ifdef VBOX_WITH_GUEST_PROPS
87# include <VBox/HostServices/GuestPropertySvc.h>
88# include <VBox/com/array.h>
89#endif
90
91#include "VBox/com/MultiResult.h"
92
93#include <algorithm>
94
95#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
96# define HOSTSUFF_EXE ".exe"
97#else /* !RT_OS_WINDOWS */
98# define HOSTSUFF_EXE ""
99#endif /* !RT_OS_WINDOWS */
100
101// defines / prototypes
102/////////////////////////////////////////////////////////////////////////////
103
104/////////////////////////////////////////////////////////////////////////////
105// Machine::Data structure
106/////////////////////////////////////////////////////////////////////////////
107
108Machine::Data::Data()
109{
110 mRegistered = FALSE;
111 pMachineConfigFile = NULL;
112 /* Contains hints on what has changed when the user is using the VM (config
113 * changes, running the VM, ...). This is used to decide if a config needs
114 * to be written to disk. */
115 flModifications = 0;
116 /* VM modification usually also trigger setting the current state to
117 * "Modified". Although this is not always the case. An e.g. is the VM
118 * initialization phase or when snapshot related data is changed. The
119 * actually behavior is controlled by the following flag. */
120 m_fAllowStateModification = false;
121 mAccessible = FALSE;
122 /* mUuid is initialized in Machine::init() */
123
124 mMachineState = MachineState_PoweredOff;
125 RTTimeNow(&mLastStateChange);
126
127 mMachineStateDeps = 0;
128 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
129 mMachineStateChangePending = 0;
130
131 mCurrentStateModified = TRUE;
132 mGuestPropertiesModified = FALSE;
133
134 mSession.mPID = NIL_RTPROCESS;
135 mSession.mState = SessionState_Unlocked;
136}
137
138Machine::Data::~Data()
139{
140 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
141 {
142 RTSemEventMultiDestroy(mMachineStateDepsSem);
143 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
144 }
145 if (pMachineConfigFile)
146 {
147 delete pMachineConfigFile;
148 pMachineConfigFile = NULL;
149 }
150}
151
152/////////////////////////////////////////////////////////////////////////////
153// Machine::HWData structure
154/////////////////////////////////////////////////////////////////////////////
155
156Machine::HWData::HWData()
157{
158 /* default values for a newly created machine */
159 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
160 mMemorySize = 128;
161 mCPUCount = 1;
162 mCPUHotPlugEnabled = false;
163 mMemoryBalloonSize = 0;
164 mPageFusionEnabled = false;
165 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
166 mVRAMSize = 8;
167 mAccelerate3DEnabled = false;
168 mAccelerate2DVideoEnabled = false;
169 mMonitorCount = 1;
170 mVideoCaptureFile = "Test.webm";
171 mVideoCaptureWidth = 1024;
172 mVideoCaptureHeight = 768;
173 mVideoCaptureRate = 512;
174 mVideoCaptureFps = 25;
175 mVideoCaptureEnabled = false;
176 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
177 maVideoCaptureScreens[i] = true;
178
179 mHWVirtExEnabled = true;
180 mHWVirtExNestedPagingEnabled = true;
181#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
182 mHWVirtExLargePagesEnabled = true;
183#else
184 /* Not supported on 32 bits hosts. */
185 mHWVirtExLargePagesEnabled = false;
186#endif
187 mHWVirtExVPIDEnabled = true;
188 mHWVirtExUXEnabled = true;
189 mHWVirtExForceEnabled = false;
190#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
191 mHWVirtExExclusive = false;
192#else
193 mHWVirtExExclusive = true;
194#endif
195#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
196 mPAEEnabled = true;
197#else
198 mPAEEnabled = false;
199#endif
200 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
201 mSyntheticCpu = false;
202 mHPETEnabled = false;
203
204 /* default boot order: floppy - DVD - HDD */
205 mBootOrder[0] = DeviceType_Floppy;
206 mBootOrder[1] = DeviceType_DVD;
207 mBootOrder[2] = DeviceType_HardDisk;
208 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
209 mBootOrder[i] = DeviceType_Null;
210
211 mClipboardMode = ClipboardMode_Disabled;
212 mDragAndDropMode = DragAndDropMode_Disabled;
213 mGuestPropertyNotificationPatterns = "";
214
215 mFirmwareType = FirmwareType_BIOS;
216 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
217 mPointingHIDType = PointingHIDType_PS2Mouse;
218 mChipsetType = ChipsetType_PIIX3;
219 mEmulatedUSBWebcamEnabled = FALSE;
220 mEmulatedUSBCardReaderEnabled = FALSE;
221
222 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
223 mCPUAttached[i] = false;
224
225 mIOCacheEnabled = true;
226 mIOCacheSize = 5; /* 5MB */
227
228 /* Maximum CPU execution cap by default. */
229 mCpuExecutionCap = 100;
230}
231
232Machine::HWData::~HWData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine::HDData structure
238/////////////////////////////////////////////////////////////////////////////
239
240Machine::MediaData::MediaData()
241{
242}
243
244Machine::MediaData::~MediaData()
245{
246}
247
248/////////////////////////////////////////////////////////////////////////////
249// Machine class
250/////////////////////////////////////////////////////////////////////////////
251
252// constructor / destructor
253/////////////////////////////////////////////////////////////////////////////
254
255Machine::Machine()
256 : mCollectorGuest(NULL),
257 mPeer(NULL),
258 mParent(NULL),
259 mSerialPorts(),
260 mParallelPorts(),
261 uRegistryNeedsSaving(0)
262{}
263
264Machine::~Machine()
265{}
266
267HRESULT Machine::FinalConstruct()
268{
269 LogFlowThisFunc(("\n"));
270 return BaseFinalConstruct();
271}
272
273void Machine::FinalRelease()
274{
275 LogFlowThisFunc(("\n"));
276 uninit();
277 BaseFinalRelease();
278}
279
280/**
281 * Initializes a new machine instance; this init() variant creates a new, empty machine.
282 * This gets called from VirtualBox::CreateMachine().
283 *
284 * @param aParent Associated parent object
285 * @param strConfigFile Local file system path to the VM settings file (can
286 * be relative to the VirtualBox config directory).
287 * @param strName name for the machine
288 * @param llGroups list of groups for the machine
289 * @param aOsType OS Type of this machine or NULL.
290 * @param aId UUID for the new machine.
291 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
292 *
293 * @return Success indicator. if not S_OK, the machine object is invalid
294 */
295HRESULT Machine::init(VirtualBox *aParent,
296 const Utf8Str &strConfigFile,
297 const Utf8Str &strName,
298 const StringsList &llGroups,
299 GuestOSType *aOsType,
300 const Guid &aId,
301 bool fForceOverwrite,
302 bool fDirectoryIncludesUUID)
303{
304 LogFlowThisFuncEnter();
305 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
306
307 /* Enclose the state transition NotReady->InInit->Ready */
308 AutoInitSpan autoInitSpan(this);
309 AssertReturn(autoInitSpan.isOk(), E_FAIL);
310
311 HRESULT rc = initImpl(aParent, strConfigFile);
312 if (FAILED(rc)) return rc;
313
314 rc = tryCreateMachineConfigFile(fForceOverwrite);
315 if (FAILED(rc)) return rc;
316
317 if (SUCCEEDED(rc))
318 {
319 // create an empty machine config
320 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
321
322 rc = initDataAndChildObjects();
323 }
324
325 if (SUCCEEDED(rc))
326 {
327 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
328 mData->mAccessible = TRUE;
329
330 unconst(mData->mUuid) = aId;
331
332 mUserData->s.strName = strName;
333
334 mUserData->s.llGroups = llGroups;
335
336 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
337 // the "name sync" flag determines whether the machine directory gets renamed along
338 // with the machine file; say so if the settings file name is the same as the
339 // settings file parent directory (machine directory)
340 mUserData->s.fNameSync = isInOwnDir();
341
342 // initialize the default snapshots folder
343 rc = COMSETTER(SnapshotFolder)(NULL);
344 AssertComRC(rc);
345
346 if (aOsType)
347 {
348 /* Store OS type */
349 mUserData->s.strOsType = aOsType->id();
350
351 /* Apply BIOS defaults */
352 mBIOSSettings->applyDefaults(aOsType);
353
354 /* Apply network adapters defaults */
355 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
356 mNetworkAdapters[slot]->applyDefaults(aOsType);
357
358 /* Apply serial port defaults */
359 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
360 mSerialPorts[slot]->applyDefaults(aOsType);
361
362 /* Let the OS type select 64-bit ness. */
363 mHWData->mLongMode = aOsType->is64Bit()
364 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
365 }
366
367 /* At this point the changing of the current state modification
368 * flag is allowed. */
369 allowStateModification();
370
371 /* commit all changes made during the initialization */
372 commit();
373 }
374
375 /* Confirm a successful initialization when it's the case */
376 if (SUCCEEDED(rc))
377 {
378 if (mData->mAccessible)
379 autoInitSpan.setSucceeded();
380 else
381 autoInitSpan.setLimited();
382 }
383
384 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
385 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
386 mData->mRegistered,
387 mData->mAccessible,
388 rc));
389
390 LogFlowThisFuncLeave();
391
392 return rc;
393}
394
395/**
396 * Initializes a new instance with data from machine XML (formerly Init_Registered).
397 * Gets called in two modes:
398 *
399 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
400 * UUID is specified and we mark the machine as "registered";
401 *
402 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
403 * and the machine remains unregistered until RegisterMachine() is called.
404 *
405 * @param aParent Associated parent object
406 * @param aConfigFile Local file system path to the VM settings file (can
407 * be relative to the VirtualBox config directory).
408 * @param aId UUID of the machine or NULL (see above).
409 *
410 * @return Success indicator. if not S_OK, the machine object is invalid
411 */
412HRESULT Machine::initFromSettings(VirtualBox *aParent,
413 const Utf8Str &strConfigFile,
414 const Guid *aId)
415{
416 LogFlowThisFuncEnter();
417 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
418
419 /* Enclose the state transition NotReady->InInit->Ready */
420 AutoInitSpan autoInitSpan(this);
421 AssertReturn(autoInitSpan.isOk(), E_FAIL);
422
423 HRESULT rc = initImpl(aParent, strConfigFile);
424 if (FAILED(rc)) return rc;
425
426 if (aId)
427 {
428 // loading a registered VM:
429 unconst(mData->mUuid) = *aId;
430 mData->mRegistered = TRUE;
431 // now load the settings from XML:
432 rc = registeredInit();
433 // this calls initDataAndChildObjects() and loadSettings()
434 }
435 else
436 {
437 // opening an unregistered VM (VirtualBox::OpenMachine()):
438 rc = initDataAndChildObjects();
439
440 if (SUCCEEDED(rc))
441 {
442 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
443 mData->mAccessible = TRUE;
444
445 try
446 {
447 // load and parse machine XML; this will throw on XML or logic errors
448 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
449
450 // reject VM UUID duplicates, they can happen if someone
451 // tries to register an already known VM config again
452 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
453 true /* fPermitInaccessible */,
454 false /* aDoSetError */,
455 NULL) != VBOX_E_OBJECT_NOT_FOUND)
456 {
457 throw setError(E_FAIL,
458 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
459 mData->m_strConfigFile.c_str());
460 }
461
462 // use UUID from machine config
463 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
464
465 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
466 NULL /* puuidRegistry */);
467 if (FAILED(rc)) throw rc;
468
469 /* At this point the changing of the current state modification
470 * flag is allowed. */
471 allowStateModification();
472
473 commit();
474 }
475 catch (HRESULT err)
476 {
477 /* we assume that error info is set by the thrower */
478 rc = err;
479 }
480 catch (...)
481 {
482 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
483 }
484 }
485 }
486
487 /* Confirm a successful initialization when it's the case */
488 if (SUCCEEDED(rc))
489 {
490 if (mData->mAccessible)
491 autoInitSpan.setSucceeded();
492 else
493 {
494 autoInitSpan.setLimited();
495
496 // uninit media from this machine's media registry, or else
497 // reloading the settings will fail
498 mParent->unregisterMachineMedia(getId());
499 }
500 }
501
502 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
503 "rc=%08X\n",
504 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
505 mData->mRegistered, mData->mAccessible, rc));
506
507 LogFlowThisFuncLeave();
508
509 return rc;
510}
511
512/**
513 * Initializes a new instance from a machine config that is already in memory
514 * (import OVF case). Since we are importing, the UUID in the machine
515 * config is ignored and we always generate a fresh one.
516 *
517 * @param strName Name for the new machine; this overrides what is specified in config and is used
518 * for the settings file as well.
519 * @param config Machine configuration loaded and parsed from XML.
520 *
521 * @return Success indicator. if not S_OK, the machine object is invalid
522 */
523HRESULT Machine::init(VirtualBox *aParent,
524 const Utf8Str &strName,
525 const settings::MachineConfigFile &config)
526{
527 LogFlowThisFuncEnter();
528
529 /* Enclose the state transition NotReady->InInit->Ready */
530 AutoInitSpan autoInitSpan(this);
531 AssertReturn(autoInitSpan.isOk(), E_FAIL);
532
533 Utf8Str strConfigFile;
534 aParent->getDefaultMachineFolder(strConfigFile);
535 strConfigFile.append(RTPATH_DELIMITER);
536 strConfigFile.append(strName);
537 strConfigFile.append(RTPATH_DELIMITER);
538 strConfigFile.append(strName);
539 strConfigFile.append(".vbox");
540
541 HRESULT rc = initImpl(aParent, strConfigFile);
542 if (FAILED(rc)) return rc;
543
544 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
545 if (FAILED(rc)) return rc;
546
547 rc = initDataAndChildObjects();
548
549 if (SUCCEEDED(rc))
550 {
551 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
552 mData->mAccessible = TRUE;
553
554 // create empty machine config for instance data
555 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
556
557 // generate fresh UUID, ignore machine config
558 unconst(mData->mUuid).create();
559
560 rc = loadMachineDataFromSettings(config,
561 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
562
563 // override VM name as well, it may be different
564 mUserData->s.strName = strName;
565
566 if (SUCCEEDED(rc))
567 {
568 /* At this point the changing of the current state modification
569 * flag is allowed. */
570 allowStateModification();
571
572 /* commit all changes made during the initialization */
573 commit();
574 }
575 }
576
577 /* Confirm a successful initialization when it's the case */
578 if (SUCCEEDED(rc))
579 {
580 if (mData->mAccessible)
581 autoInitSpan.setSucceeded();
582 else
583 {
584 autoInitSpan.setLimited();
585
586 // uninit media from this machine's media registry, or else
587 // reloading the settings will fail
588 mParent->unregisterMachineMedia(getId());
589 }
590 }
591
592 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
593 "rc=%08X\n",
594 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
595 mData->mRegistered, mData->mAccessible, rc));
596
597 LogFlowThisFuncLeave();
598
599 return rc;
600}
601
602/**
603 * Shared code between the various init() implementations.
604 * @param aParent
605 * @return
606 */
607HRESULT Machine::initImpl(VirtualBox *aParent,
608 const Utf8Str &strConfigFile)
609{
610 LogFlowThisFuncEnter();
611
612 AssertReturn(aParent, E_INVALIDARG);
613 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
614
615 HRESULT rc = S_OK;
616
617 /* share the parent weakly */
618 unconst(mParent) = aParent;
619
620 /* allocate the essential machine data structure (the rest will be
621 * allocated later by initDataAndChildObjects() */
622 mData.allocate();
623
624 /* memorize the config file name (as provided) */
625 mData->m_strConfigFile = strConfigFile;
626
627 /* get the full file name */
628 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
629 if (RT_FAILURE(vrc1))
630 return setError(VBOX_E_FILE_ERROR,
631 tr("Invalid machine settings file name '%s' (%Rrc)"),
632 strConfigFile.c_str(),
633 vrc1);
634
635 LogFlowThisFuncLeave();
636
637 return rc;
638}
639
640/**
641 * Tries to create a machine settings file in the path stored in the machine
642 * instance data. Used when a new machine is created to fail gracefully if
643 * the settings file could not be written (e.g. because machine dir is read-only).
644 * @return
645 */
646HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
647{
648 HRESULT rc = S_OK;
649
650 // when we create a new machine, we must be able to create the settings file
651 RTFILE f = NIL_RTFILE;
652 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
653 if ( RT_SUCCESS(vrc)
654 || vrc == VERR_SHARING_VIOLATION
655 )
656 {
657 if (RT_SUCCESS(vrc))
658 RTFileClose(f);
659 if (!fForceOverwrite)
660 rc = setError(VBOX_E_FILE_ERROR,
661 tr("Machine settings file '%s' already exists"),
662 mData->m_strConfigFileFull.c_str());
663 else
664 {
665 /* try to delete the config file, as otherwise the creation
666 * of a new settings file will fail. */
667 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
668 if (RT_FAILURE(vrc2))
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Could not delete the existing settings file '%s' (%Rrc)"),
671 mData->m_strConfigFileFull.c_str(), vrc2);
672 }
673 }
674 else if ( vrc != VERR_FILE_NOT_FOUND
675 && vrc != VERR_PATH_NOT_FOUND
676 )
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Invalid machine settings file name '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(),
680 vrc);
681 return rc;
682}
683
684/**
685 * Initializes the registered machine by loading the settings file.
686 * This method is separated from #init() in order to make it possible to
687 * retry the operation after VirtualBox startup instead of refusing to
688 * startup the whole VirtualBox server in case if the settings file of some
689 * registered VM is invalid or inaccessible.
690 *
691 * @note Must be always called from this object's write lock
692 * (unless called from #init() that doesn't need any locking).
693 * @note Locks the mUSBController method for writing.
694 * @note Subclasses must not call this method.
695 */
696HRESULT Machine::registeredInit()
697{
698 AssertReturn(!isSessionMachine(), E_FAIL);
699 AssertReturn(!isSnapshotMachine(), E_FAIL);
700 AssertReturn(mData->mUuid.isValid(), E_FAIL);
701 AssertReturn(!mData->mAccessible, E_FAIL);
702
703 HRESULT rc = initDataAndChildObjects();
704
705 if (SUCCEEDED(rc))
706 {
707 /* Temporarily reset the registered flag in order to let setters
708 * potentially called from loadSettings() succeed (isMutable() used in
709 * all setters will return FALSE for a Machine instance if mRegistered
710 * is TRUE). */
711 mData->mRegistered = FALSE;
712
713 try
714 {
715 // load and parse machine XML; this will throw on XML or logic errors
716 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
717
718 if (mData->mUuid != mData->pMachineConfigFile->uuid)
719 throw setError(E_FAIL,
720 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
721 mData->pMachineConfigFile->uuid.raw(),
722 mData->m_strConfigFileFull.c_str(),
723 mData->mUuid.toString().c_str(),
724 mParent->settingsFilePath().c_str());
725
726 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
727 NULL /* const Guid *puuidRegistry */);
728 if (FAILED(rc)) throw rc;
729 }
730 catch (HRESULT err)
731 {
732 /* we assume that error info is set by the thrower */
733 rc = err;
734 }
735 catch (...)
736 {
737 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
738 }
739
740 /* Restore the registered flag (even on failure) */
741 mData->mRegistered = TRUE;
742 }
743
744 if (SUCCEEDED(rc))
745 {
746 /* Set mAccessible to TRUE only if we successfully locked and loaded
747 * the settings file */
748 mData->mAccessible = TRUE;
749
750 /* commit all changes made during loading the settings file */
751 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
752 /// @todo r=klaus for some reason the settings loading logic backs up
753 // the settings, and therefore a commit is needed. Should probably be changed.
754 }
755 else
756 {
757 /* If the machine is registered, then, instead of returning a
758 * failure, we mark it as inaccessible and set the result to
759 * success to give it a try later */
760
761 /* fetch the current error info */
762 mData->mAccessError = com::ErrorInfo();
763 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
764 mData->mUuid.raw(),
765 mData->mAccessError.getText().raw()));
766
767 /* rollback all changes */
768 rollback(false /* aNotify */);
769
770 // uninit media from this machine's media registry, or else
771 // reloading the settings will fail
772 mParent->unregisterMachineMedia(getId());
773
774 /* uninitialize the common part to make sure all data is reset to
775 * default (null) values */
776 uninitDataAndChildObjects();
777
778 rc = S_OK;
779 }
780
781 return rc;
782}
783
784/**
785 * Uninitializes the instance.
786 * Called either from FinalRelease() or by the parent when it gets destroyed.
787 *
788 * @note The caller of this method must make sure that this object
789 * a) doesn't have active callers on the current thread and b) is not locked
790 * by the current thread; otherwise uninit() will hang either a) due to
791 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
792 * a dead-lock caused by this thread waiting for all callers on the other
793 * threads are done but preventing them from doing so by holding a lock.
794 */
795void Machine::uninit()
796{
797 LogFlowThisFuncEnter();
798
799 Assert(!isWriteLockOnCurrentThread());
800
801 Assert(!uRegistryNeedsSaving);
802 if (uRegistryNeedsSaving)
803 {
804 AutoCaller autoCaller(this);
805 if (SUCCEEDED(autoCaller.rc()))
806 {
807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
808 saveSettings(NULL, Machine::SaveS_Force);
809 }
810 }
811
812 /* Enclose the state transition Ready->InUninit->NotReady */
813 AutoUninitSpan autoUninitSpan(this);
814 if (autoUninitSpan.uninitDone())
815 return;
816
817 Assert(!isSnapshotMachine());
818 Assert(!isSessionMachine());
819 Assert(!!mData);
820
821 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
822 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
823
824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
825
826 if (!mData->mSession.mMachine.isNull())
827 {
828 /* Theoretically, this can only happen if the VirtualBox server has been
829 * terminated while there were clients running that owned open direct
830 * sessions. Since in this case we are definitely called by
831 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
832 * won't happen on the client watcher thread (because it does
833 * VirtualBox::addCaller() for the duration of the
834 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
835 * cannot happen until the VirtualBox caller is released). This is
836 * important, because SessionMachine::uninit() cannot correctly operate
837 * after we return from this method (it expects the Machine instance is
838 * still valid). We'll call it ourselves below.
839 */
840 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
841 (SessionMachine*)mData->mSession.mMachine));
842
843 if (Global::IsOnlineOrTransient(mData->mMachineState))
844 {
845 LogWarningThisFunc(("Setting state to Aborted!\n"));
846 /* set machine state using SessionMachine reimplementation */
847 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
848 }
849
850 /*
851 * Uninitialize SessionMachine using public uninit() to indicate
852 * an unexpected uninitialization.
853 */
854 mData->mSession.mMachine->uninit();
855 /* SessionMachine::uninit() must set mSession.mMachine to null */
856 Assert(mData->mSession.mMachine.isNull());
857 }
858
859 // uninit media from this machine's media registry, if they're still there
860 Guid uuidMachine(getId());
861
862 /* the lock is no more necessary (SessionMachine is uninitialized) */
863 alock.release();
864
865 /* XXX This will fail with
866 * "cannot be closed because it is still attached to 1 virtual machines"
867 * because at this point we did not call uninitDataAndChildObjects() yet
868 * and therefore also removeBackReference() for all these mediums was not called! */
869
870 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
871 mParent->unregisterMachineMedia(uuidMachine);
872
873 // has machine been modified?
874 if (mData->flModifications)
875 {
876 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
877 rollback(false /* aNotify */);
878 }
879
880 if (mData->mAccessible)
881 uninitDataAndChildObjects();
882
883 /* free the essential data structure last */
884 mData.free();
885
886 LogFlowThisFuncLeave();
887}
888
889// IMachine properties
890/////////////////////////////////////////////////////////////////////////////
891
892STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
893{
894 CheckComArgOutPointerValid(aParent);
895
896 AutoLimitedCaller autoCaller(this);
897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
898
899 /* mParent is constant during life time, no need to lock */
900 ComObjPtr<VirtualBox> pVirtualBox(mParent);
901 pVirtualBox.queryInterfaceTo(aParent);
902
903 return S_OK;
904}
905
906STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
907{
908 CheckComArgOutPointerValid(aAccessible);
909
910 AutoLimitedCaller autoCaller(this);
911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
912
913 LogFlowThisFunc(("ENTER\n"));
914
915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
916
917 HRESULT rc = S_OK;
918
919 if (!mData->mAccessible)
920 {
921 /* try to initialize the VM once more if not accessible */
922
923 AutoReinitSpan autoReinitSpan(this);
924 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
925
926#ifdef DEBUG
927 LogFlowThisFunc(("Dumping media backreferences\n"));
928 mParent->dumpAllBackRefs();
929#endif
930
931 if (mData->pMachineConfigFile)
932 {
933 // reset the XML file to force loadSettings() (called from registeredInit())
934 // to parse it again; the file might have changed
935 delete mData->pMachineConfigFile;
936 mData->pMachineConfigFile = NULL;
937 }
938
939 rc = registeredInit();
940
941 if (SUCCEEDED(rc) && mData->mAccessible)
942 {
943 autoReinitSpan.setSucceeded();
944
945 /* make sure interesting parties will notice the accessibility
946 * state change */
947 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
948 mParent->onMachineDataChange(mData->mUuid);
949 }
950 }
951
952 if (SUCCEEDED(rc))
953 *aAccessible = mData->mAccessible;
954
955 LogFlowThisFuncLeave();
956
957 return rc;
958}
959
960STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
961{
962 CheckComArgOutPointerValid(aAccessError);
963
964 AutoLimitedCaller autoCaller(this);
965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
966
967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
968
969 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
970 {
971 /* return shortly */
972 aAccessError = NULL;
973 return S_OK;
974 }
975
976 HRESULT rc = S_OK;
977
978 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
979 rc = errorInfo.createObject();
980 if (SUCCEEDED(rc))
981 {
982 errorInfo->init(mData->mAccessError.getResultCode(),
983 mData->mAccessError.getInterfaceID().ref(),
984 Utf8Str(mData->mAccessError.getComponent()).c_str(),
985 Utf8Str(mData->mAccessError.getText()));
986 rc = errorInfo.queryInterfaceTo(aAccessError);
987 }
988
989 return rc;
990}
991
992STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
993{
994 CheckComArgOutPointerValid(aName);
995
996 AutoCaller autoCaller(this);
997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
998
999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 mUserData->s.strName.cloneTo(aName);
1002
1003 return S_OK;
1004}
1005
1006STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1007{
1008 CheckComArgStrNotEmptyOrNull(aName);
1009
1010 AutoCaller autoCaller(this);
1011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1012
1013 // prohibit setting a UUID only as the machine name, or else it can
1014 // never be found by findMachine()
1015 Guid test(aName);
1016
1017 if (test.isValid())
1018 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1019
1020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 HRESULT rc = checkStateDependency(MutableStateDep);
1023 if (FAILED(rc)) return rc;
1024
1025 setModified(IsModified_MachineData);
1026 mUserData.backup();
1027 mUserData->s.strName = aName;
1028
1029 return S_OK;
1030}
1031
1032STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1033{
1034 CheckComArgOutPointerValid(aDescription);
1035
1036 AutoCaller autoCaller(this);
1037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1038
1039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 mUserData->s.strDescription.cloneTo(aDescription);
1042
1043 return S_OK;
1044}
1045
1046STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1047{
1048 AutoCaller autoCaller(this);
1049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1050
1051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1052
1053 // this can be done in principle in any state as it doesn't affect the VM
1054 // significantly, but play safe by not messing around while complex
1055 // activities are going on
1056 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1057 if (FAILED(rc)) return rc;
1058
1059 setModified(IsModified_MachineData);
1060 mUserData.backup();
1061 mUserData->s.strDescription = aDescription;
1062
1063 return S_OK;
1064}
1065
1066STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1067{
1068 CheckComArgOutPointerValid(aId);
1069
1070 AutoLimitedCaller autoCaller(this);
1071 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1072
1073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1074
1075 mData->mUuid.toUtf16().cloneTo(aId);
1076
1077 return S_OK;
1078}
1079
1080STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1081{
1082 CheckComArgOutSafeArrayPointerValid(aGroups);
1083
1084 AutoCaller autoCaller(this);
1085 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1086
1087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1088 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1089 size_t i = 0;
1090 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1091 it != mUserData->s.llGroups.end();
1092 ++it, i++)
1093 {
1094 Bstr tmp = *it;
1095 tmp.cloneTo(&groups[i]);
1096 }
1097 groups.detachTo(ComSafeArrayOutArg(aGroups));
1098
1099 return S_OK;
1100}
1101
1102STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1103{
1104 AutoCaller autoCaller(this);
1105 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1106
1107 StringsList llGroups;
1108 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1109 if (FAILED(rc))
1110 return rc;
1111
1112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1113
1114 // changing machine groups is possible while the VM is offline
1115 rc = checkStateDependency(OfflineStateDep);
1116 if (FAILED(rc)) return rc;
1117
1118 setModified(IsModified_MachineData);
1119 mUserData.backup();
1120 mUserData->s.llGroups = llGroups;
1121
1122 return S_OK;
1123}
1124
1125STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1126{
1127 CheckComArgOutPointerValid(aOSTypeId);
1128
1129 AutoCaller autoCaller(this);
1130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1131
1132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1133
1134 mUserData->s.strOsType.cloneTo(aOSTypeId);
1135
1136 return S_OK;
1137}
1138
1139STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1140{
1141 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1142
1143 AutoCaller autoCaller(this);
1144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1145
1146 /* look up the object by Id to check it is valid */
1147 ComPtr<IGuestOSType> guestOSType;
1148 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1149 if (FAILED(rc)) return rc;
1150
1151 /* when setting, always use the "etalon" value for consistency -- lookup
1152 * by ID is case-insensitive and the input value may have different case */
1153 Bstr osTypeId;
1154 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1155 if (FAILED(rc)) return rc;
1156
1157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1158
1159 rc = checkStateDependency(MutableStateDep);
1160 if (FAILED(rc)) return rc;
1161
1162 setModified(IsModified_MachineData);
1163 mUserData.backup();
1164 mUserData->s.strOsType = osTypeId;
1165
1166 return S_OK;
1167}
1168
1169
1170STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1171{
1172 CheckComArgOutPointerValid(aFirmwareType);
1173
1174 AutoCaller autoCaller(this);
1175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1176
1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 *aFirmwareType = mHWData->mFirmwareType;
1180
1181 return S_OK;
1182}
1183
1184STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1185{
1186 AutoCaller autoCaller(this);
1187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 setModified(IsModified_MachineData);
1194 mHWData.backup();
1195 mHWData->mFirmwareType = aFirmwareType;
1196
1197 return S_OK;
1198}
1199
1200STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1201{
1202 CheckComArgOutPointerValid(aKeyboardHIDType);
1203
1204 AutoCaller autoCaller(this);
1205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1206
1207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1208
1209 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1210
1211 return S_OK;
1212}
1213
1214STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1215{
1216 AutoCaller autoCaller(this);
1217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1219
1220 HRESULT rc = checkStateDependency(MutableStateDep);
1221 if (FAILED(rc)) return rc;
1222
1223 setModified(IsModified_MachineData);
1224 mHWData.backup();
1225 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1226
1227 return S_OK;
1228}
1229
1230STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1231{
1232 CheckComArgOutPointerValid(aPointingHIDType);
1233
1234 AutoCaller autoCaller(this);
1235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1236
1237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1238
1239 *aPointingHIDType = mHWData->mPointingHIDType;
1240
1241 return S_OK;
1242}
1243
1244STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1245{
1246 AutoCaller autoCaller(this);
1247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1249
1250 HRESULT rc = checkStateDependency(MutableStateDep);
1251 if (FAILED(rc)) return rc;
1252
1253 setModified(IsModified_MachineData);
1254 mHWData.backup();
1255 mHWData->mPointingHIDType = aPointingHIDType;
1256
1257 return S_OK;
1258}
1259
1260STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1261{
1262 CheckComArgOutPointerValid(aChipsetType);
1263
1264 AutoCaller autoCaller(this);
1265 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1266
1267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1268
1269 *aChipsetType = mHWData->mChipsetType;
1270
1271 return S_OK;
1272}
1273
1274STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1275{
1276 AutoCaller autoCaller(this);
1277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1279
1280 HRESULT rc = checkStateDependency(MutableStateDep);
1281 if (FAILED(rc)) return rc;
1282
1283 if (aChipsetType != mHWData->mChipsetType)
1284 {
1285 setModified(IsModified_MachineData);
1286 mHWData.backup();
1287 mHWData->mChipsetType = aChipsetType;
1288
1289 // Resize network adapter array, to be finalized on commit/rollback.
1290 // We must not throw away entries yet, otherwise settings are lost
1291 // without a way to roll back.
1292 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1293 size_t oldCount = mNetworkAdapters.size();
1294 if (newCount > oldCount)
1295 {
1296 mNetworkAdapters.resize(newCount);
1297 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1298 {
1299 unconst(mNetworkAdapters[slot]).createObject();
1300 mNetworkAdapters[slot]->init(this, slot);
1301 }
1302 }
1303 }
1304
1305 return S_OK;
1306}
1307
1308STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1309{
1310 CheckComArgOutPointerValid(aHWVersion);
1311
1312 AutoCaller autoCaller(this);
1313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1314
1315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1316
1317 mHWData->mHWVersion.cloneTo(aHWVersion);
1318
1319 return S_OK;
1320}
1321
1322STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1323{
1324 /* check known version */
1325 Utf8Str hwVersion = aHWVersion;
1326 if ( hwVersion.compare("1") != 0
1327 && hwVersion.compare("2") != 0)
1328 return setError(E_INVALIDARG,
1329 tr("Invalid hardware version: %ls\n"), aHWVersion);
1330
1331 AutoCaller autoCaller(this);
1332 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1333
1334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1335
1336 HRESULT rc = checkStateDependency(MutableStateDep);
1337 if (FAILED(rc)) return rc;
1338
1339 setModified(IsModified_MachineData);
1340 mHWData.backup();
1341 mHWData->mHWVersion = hwVersion;
1342
1343 return S_OK;
1344}
1345
1346STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1347{
1348 CheckComArgOutPointerValid(aUUID);
1349
1350 AutoCaller autoCaller(this);
1351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1352
1353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1354
1355 if (mHWData->mHardwareUUID.isValid())
1356 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1357 else
1358 mData->mUuid.toUtf16().cloneTo(aUUID);
1359
1360 return S_OK;
1361}
1362
1363STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1364{
1365 Guid hardwareUUID(aUUID);
1366 if (!hardwareUUID.isValid())
1367 return E_INVALIDARG;
1368
1369 AutoCaller autoCaller(this);
1370 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1371
1372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1373
1374 HRESULT rc = checkStateDependency(MutableStateDep);
1375 if (FAILED(rc)) return rc;
1376
1377 setModified(IsModified_MachineData);
1378 mHWData.backup();
1379 if (hardwareUUID == mData->mUuid)
1380 mHWData->mHardwareUUID.clear();
1381 else
1382 mHWData->mHardwareUUID = hardwareUUID;
1383
1384 return S_OK;
1385}
1386
1387STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1388{
1389 CheckComArgOutPointerValid(memorySize);
1390
1391 AutoCaller autoCaller(this);
1392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1393
1394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 *memorySize = mHWData->mMemorySize;
1397
1398 return S_OK;
1399}
1400
1401STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1402{
1403 /* check RAM limits */
1404 if ( memorySize < MM_RAM_MIN_IN_MB
1405 || memorySize > MM_RAM_MAX_IN_MB
1406 )
1407 return setError(E_INVALIDARG,
1408 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1409 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1410
1411 AutoCaller autoCaller(this);
1412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1413
1414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 HRESULT rc = checkStateDependency(MutableStateDep);
1417 if (FAILED(rc)) return rc;
1418
1419 setModified(IsModified_MachineData);
1420 mHWData.backup();
1421 mHWData->mMemorySize = memorySize;
1422
1423 return S_OK;
1424}
1425
1426STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1427{
1428 CheckComArgOutPointerValid(CPUCount);
1429
1430 AutoCaller autoCaller(this);
1431 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1432
1433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1434
1435 *CPUCount = mHWData->mCPUCount;
1436
1437 return S_OK;
1438}
1439
1440STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1441{
1442 /* check CPU limits */
1443 if ( CPUCount < SchemaDefs::MinCPUCount
1444 || CPUCount > SchemaDefs::MaxCPUCount
1445 )
1446 return setError(E_INVALIDARG,
1447 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1448 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1449
1450 AutoCaller autoCaller(this);
1451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1452
1453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1454
1455 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1456 if (mHWData->mCPUHotPlugEnabled)
1457 {
1458 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1459 {
1460 if (mHWData->mCPUAttached[idx])
1461 return setError(E_INVALIDARG,
1462 tr("There is still a CPU attached to socket %lu."
1463 "Detach the CPU before removing the socket"),
1464 CPUCount, idx+1);
1465 }
1466 }
1467
1468 HRESULT rc = checkStateDependency(MutableStateDep);
1469 if (FAILED(rc)) return rc;
1470
1471 setModified(IsModified_MachineData);
1472 mHWData.backup();
1473 mHWData->mCPUCount = CPUCount;
1474
1475 return S_OK;
1476}
1477
1478STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1479{
1480 CheckComArgOutPointerValid(aExecutionCap);
1481
1482 AutoCaller autoCaller(this);
1483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1484
1485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 *aExecutionCap = mHWData->mCpuExecutionCap;
1488
1489 return S_OK;
1490}
1491
1492STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1493{
1494 HRESULT rc = S_OK;
1495
1496 /* check throttle limits */
1497 if ( aExecutionCap < 1
1498 || aExecutionCap > 100
1499 )
1500 return setError(E_INVALIDARG,
1501 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1502 aExecutionCap, 1, 100);
1503
1504 AutoCaller autoCaller(this);
1505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1506
1507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1508
1509 alock.release();
1510 rc = onCPUExecutionCapChange(aExecutionCap);
1511 alock.acquire();
1512 if (FAILED(rc)) return rc;
1513
1514 setModified(IsModified_MachineData);
1515 mHWData.backup();
1516 mHWData->mCpuExecutionCap = aExecutionCap;
1517
1518 /** Save settings if online - @todo why is this required? -- @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 ( Global::IsOnline(mData->mMachineState)
1810 && mHWData->mVideoCaptureEnabled)
1811 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1812
1813 if (strFile.isEmpty())
1814 strFile = "VideoCap.webm";
1815
1816 setModified(IsModified_MachineData);
1817 mHWData.backup();
1818 mHWData->mVideoCaptureFile = strFile;
1819
1820 return S_OK;
1821}
1822
1823STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1824{
1825 AutoCaller autoCaller(this);
1826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1827
1828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1829 *aHorzRes = mHWData->mVideoCaptureWidth;
1830 return S_OK;
1831}
1832
1833STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1834{
1835 AutoCaller autoCaller(this);
1836 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1837
1838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 if ( Global::IsOnline(mData->mMachineState)
1841 && mHWData->mVideoCaptureEnabled)
1842 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1843
1844 setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mVideoCaptureWidth = aHorzRes;
1847
1848 return S_OK;
1849}
1850
1851STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1852{
1853 AutoCaller autoCaller(this);
1854 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1855
1856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1857 *aVertRes = mHWData->mVideoCaptureHeight;
1858 return S_OK;
1859}
1860
1861STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1862{
1863 AutoCaller autoCaller(this);
1864 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1865
1866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1867
1868 if ( Global::IsOnline(mData->mMachineState)
1869 && mHWData->mVideoCaptureEnabled)
1870 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1871
1872 setModified(IsModified_MachineData);
1873 mHWData.backup();
1874 mHWData->mVideoCaptureHeight = aVertRes;
1875
1876 return S_OK;
1877}
1878
1879STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1880{
1881 AutoCaller autoCaller(this);
1882 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1883
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885 *aRate = mHWData->mVideoCaptureRate;
1886 return S_OK;
1887}
1888
1889STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1890{
1891 AutoCaller autoCaller(this);
1892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1893
1894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1895
1896 if ( Global::IsOnline(mData->mMachineState)
1897 && mHWData->mVideoCaptureEnabled)
1898 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1899
1900 setModified(IsModified_MachineData);
1901 mHWData.backup();
1902 mHWData->mVideoCaptureRate = aRate;
1903
1904 return S_OK;
1905}
1906
1907STDMETHODIMP Machine::COMGETTER(VideoCaptureFps)(ULONG *aFps)
1908{
1909 AutoCaller autoCaller(this);
1910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1911
1912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1913 *aFps = mHWData->mVideoCaptureFps;
1914 return S_OK;
1915}
1916
1917STDMETHODIMP Machine::COMSETTER(VideoCaptureFps)(ULONG aFps)
1918{
1919 AutoCaller autoCaller(this);
1920 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1921
1922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1923
1924 if ( Global::IsOnline(mData->mMachineState)
1925 && mHWData->mVideoCaptureEnabled)
1926 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1927
1928 setModified(IsModified_MachineData);
1929 mHWData.backup();
1930 mHWData->mVideoCaptureFps = aFps;
1931
1932 return S_OK;
1933}
1934
1935STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1936{
1937 CheckComArgOutPointerValid(aGraphicsControllerType);
1938
1939 AutoCaller autoCaller(this);
1940 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1941
1942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1943
1944 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1945
1946 return S_OK;
1947}
1948
1949STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1950{
1951 switch (aGraphicsControllerType)
1952 {
1953 case GraphicsControllerType_Null:
1954 case GraphicsControllerType_VBoxVGA:
1955 break;
1956 default:
1957 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1958 }
1959
1960 AutoCaller autoCaller(this);
1961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1962
1963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1964
1965 HRESULT rc = checkStateDependency(MutableStateDep);
1966 if (FAILED(rc)) return rc;
1967
1968 setModified(IsModified_MachineData);
1969 mHWData.backup();
1970 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1971
1972 return S_OK;
1973}
1974
1975STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1976{
1977 CheckComArgOutPointerValid(memorySize);
1978
1979 AutoCaller autoCaller(this);
1980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1981
1982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1983
1984 *memorySize = mHWData->mVRAMSize;
1985
1986 return S_OK;
1987}
1988
1989STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1990{
1991 /* check VRAM limits */
1992 if (memorySize < SchemaDefs::MinGuestVRAM ||
1993 memorySize > SchemaDefs::MaxGuestVRAM)
1994 return setError(E_INVALIDARG,
1995 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1996 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1997
1998 AutoCaller autoCaller(this);
1999 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2000
2001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2002
2003 HRESULT rc = checkStateDependency(MutableStateDep);
2004 if (FAILED(rc)) return rc;
2005
2006 setModified(IsModified_MachineData);
2007 mHWData.backup();
2008 mHWData->mVRAMSize = memorySize;
2009
2010 return S_OK;
2011}
2012
2013/** @todo this method should not be public */
2014STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2015{
2016 CheckComArgOutPointerValid(memoryBalloonSize);
2017
2018 AutoCaller autoCaller(this);
2019 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2020
2021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2022
2023 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2024
2025 return S_OK;
2026}
2027
2028/**
2029 * Set the memory balloon size.
2030 *
2031 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2032 * we have to make sure that we never call IGuest from here.
2033 */
2034STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2035{
2036 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2037#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2038 /* check limits */
2039 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2040 return setError(E_INVALIDARG,
2041 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2042 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2043
2044 AutoCaller autoCaller(this);
2045 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2046
2047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2048
2049 setModified(IsModified_MachineData);
2050 mHWData.backup();
2051 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2052
2053 return S_OK;
2054#else
2055 NOREF(memoryBalloonSize);
2056 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2057#endif
2058}
2059
2060STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2061{
2062 CheckComArgOutPointerValid(aEnabled);
2063
2064 AutoCaller autoCaller(this);
2065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2066
2067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2068
2069 *aEnabled = mHWData->mPageFusionEnabled;
2070 return S_OK;
2071}
2072
2073STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2074{
2075#ifdef VBOX_WITH_PAGE_SHARING
2076 AutoCaller autoCaller(this);
2077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2078
2079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2080
2081 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2082 setModified(IsModified_MachineData);
2083 mHWData.backup();
2084 mHWData->mPageFusionEnabled = aEnabled;
2085 return S_OK;
2086#else
2087 NOREF(aEnabled);
2088 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2089#endif
2090}
2091
2092STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2093{
2094 CheckComArgOutPointerValid(aEnabled);
2095
2096 AutoCaller autoCaller(this);
2097 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2098
2099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2100
2101 *aEnabled = mHWData->mAccelerate3DEnabled;
2102
2103 return S_OK;
2104}
2105
2106STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2107{
2108 AutoCaller autoCaller(this);
2109 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2110
2111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2112
2113 HRESULT rc = checkStateDependency(MutableStateDep);
2114 if (FAILED(rc)) return rc;
2115
2116 /** @todo check validity! */
2117
2118 setModified(IsModified_MachineData);
2119 mHWData.backup();
2120 mHWData->mAccelerate3DEnabled = enable;
2121
2122 return S_OK;
2123}
2124
2125
2126STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2127{
2128 CheckComArgOutPointerValid(aEnabled);
2129
2130 AutoCaller autoCaller(this);
2131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2132
2133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2136
2137 return S_OK;
2138}
2139
2140STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2141{
2142 AutoCaller autoCaller(this);
2143 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2144
2145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2146
2147 HRESULT rc = checkStateDependency(MutableStateDep);
2148 if (FAILED(rc)) return rc;
2149
2150 /** @todo check validity! */
2151
2152 setModified(IsModified_MachineData);
2153 mHWData.backup();
2154 mHWData->mAccelerate2DVideoEnabled = enable;
2155
2156 return S_OK;
2157}
2158
2159STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2160{
2161 CheckComArgOutPointerValid(monitorCount);
2162
2163 AutoCaller autoCaller(this);
2164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2165
2166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2167
2168 *monitorCount = mHWData->mMonitorCount;
2169
2170 return S_OK;
2171}
2172
2173STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2174{
2175 /* make sure monitor count is a sensible number */
2176 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2177 return setError(E_INVALIDARG,
2178 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2179 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2180
2181 AutoCaller autoCaller(this);
2182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2183
2184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2185
2186 HRESULT rc = checkStateDependency(MutableStateDep);
2187 if (FAILED(rc)) return rc;
2188
2189 setModified(IsModified_MachineData);
2190 mHWData.backup();
2191 mHWData->mMonitorCount = monitorCount;
2192
2193 return S_OK;
2194}
2195
2196STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2197{
2198 CheckComArgOutPointerValid(biosSettings);
2199
2200 AutoCaller autoCaller(this);
2201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2202
2203 /* mBIOSSettings is constant during life time, no need to lock */
2204 mBIOSSettings.queryInterfaceTo(biosSettings);
2205
2206 return S_OK;
2207}
2208
2209STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2210{
2211 CheckComArgOutPointerValid(aVal);
2212
2213 AutoCaller autoCaller(this);
2214 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2215
2216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2217
2218 switch (property)
2219 {
2220 case CPUPropertyType_PAE:
2221 *aVal = mHWData->mPAEEnabled;
2222 break;
2223
2224 case CPUPropertyType_Synthetic:
2225 *aVal = mHWData->mSyntheticCpu;
2226 break;
2227
2228 case CPUPropertyType_LongMode:
2229 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2230 *aVal = TRUE;
2231 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2232 *aVal = FALSE;
2233#if HC_ARCH_BITS == 64
2234 else
2235 *aVal = TRUE;
2236#else
2237 else
2238 {
2239 *aVal = FALSE;
2240
2241 ComPtr<IGuestOSType> ptrGuestOSType;
2242 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2243 if (SUCCEEDED(hrc2))
2244 {
2245 BOOL fIs64Bit = FALSE;
2246 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2247 if (SUCCEEDED(hrc2) && fIs64Bit)
2248 {
2249 ComObjPtr<Host> ptrHost = mParent->host();
2250 alock.release();
2251
2252 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2253 if (FAILED(hrc2))
2254 *aVal = FALSE;
2255 }
2256 }
2257 }
2258#endif
2259 break;
2260
2261 default:
2262 return E_INVALIDARG;
2263 }
2264 return S_OK;
2265}
2266
2267STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2268{
2269 AutoCaller autoCaller(this);
2270 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2271
2272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2273
2274 HRESULT rc = checkStateDependency(MutableStateDep);
2275 if (FAILED(rc)) return rc;
2276
2277 switch (property)
2278 {
2279 case CPUPropertyType_PAE:
2280 setModified(IsModified_MachineData);
2281 mHWData.backup();
2282 mHWData->mPAEEnabled = !!aVal;
2283 break;
2284
2285 case CPUPropertyType_Synthetic:
2286 setModified(IsModified_MachineData);
2287 mHWData.backup();
2288 mHWData->mSyntheticCpu = !!aVal;
2289 break;
2290
2291 case CPUPropertyType_LongMode:
2292 setModified(IsModified_MachineData);
2293 mHWData.backup();
2294 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2295 break;
2296
2297 default:
2298 return E_INVALIDARG;
2299 }
2300 return S_OK;
2301}
2302
2303STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2304{
2305 CheckComArgOutPointerValid(aValEax);
2306 CheckComArgOutPointerValid(aValEbx);
2307 CheckComArgOutPointerValid(aValEcx);
2308 CheckComArgOutPointerValid(aValEdx);
2309
2310 AutoCaller autoCaller(this);
2311 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2312
2313 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2314
2315 switch(aId)
2316 {
2317 case 0x0:
2318 case 0x1:
2319 case 0x2:
2320 case 0x3:
2321 case 0x4:
2322 case 0x5:
2323 case 0x6:
2324 case 0x7:
2325 case 0x8:
2326 case 0x9:
2327 case 0xA:
2328 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2329 return E_INVALIDARG;
2330
2331 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2332 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2333 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2334 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2335 break;
2336
2337 case 0x80000000:
2338 case 0x80000001:
2339 case 0x80000002:
2340 case 0x80000003:
2341 case 0x80000004:
2342 case 0x80000005:
2343 case 0x80000006:
2344 case 0x80000007:
2345 case 0x80000008:
2346 case 0x80000009:
2347 case 0x8000000A:
2348 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2349 return E_INVALIDARG;
2350
2351 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2352 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2353 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2354 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2355 break;
2356
2357 default:
2358 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2359 }
2360 return S_OK;
2361}
2362
2363STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2364{
2365 AutoCaller autoCaller(this);
2366 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2367
2368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2369
2370 HRESULT rc = checkStateDependency(MutableStateDep);
2371 if (FAILED(rc)) return rc;
2372
2373 switch(aId)
2374 {
2375 case 0x0:
2376 case 0x1:
2377 case 0x2:
2378 case 0x3:
2379 case 0x4:
2380 case 0x5:
2381 case 0x6:
2382 case 0x7:
2383 case 0x8:
2384 case 0x9:
2385 case 0xA:
2386 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2387 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2388 setModified(IsModified_MachineData);
2389 mHWData.backup();
2390 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2391 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2392 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2393 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2394 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2395 break;
2396
2397 case 0x80000000:
2398 case 0x80000001:
2399 case 0x80000002:
2400 case 0x80000003:
2401 case 0x80000004:
2402 case 0x80000005:
2403 case 0x80000006:
2404 case 0x80000007:
2405 case 0x80000008:
2406 case 0x80000009:
2407 case 0x8000000A:
2408 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2409 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2410 setModified(IsModified_MachineData);
2411 mHWData.backup();
2412 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2413 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2414 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2415 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2416 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2417 break;
2418
2419 default:
2420 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2421 }
2422 return S_OK;
2423}
2424
2425STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2426{
2427 AutoCaller autoCaller(this);
2428 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2429
2430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2431
2432 HRESULT rc = checkStateDependency(MutableStateDep);
2433 if (FAILED(rc)) return rc;
2434
2435 switch(aId)
2436 {
2437 case 0x0:
2438 case 0x1:
2439 case 0x2:
2440 case 0x3:
2441 case 0x4:
2442 case 0x5:
2443 case 0x6:
2444 case 0x7:
2445 case 0x8:
2446 case 0x9:
2447 case 0xA:
2448 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2449 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2450 setModified(IsModified_MachineData);
2451 mHWData.backup();
2452 /* Invalidate leaf. */
2453 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2454 break;
2455
2456 case 0x80000000:
2457 case 0x80000001:
2458 case 0x80000002:
2459 case 0x80000003:
2460 case 0x80000004:
2461 case 0x80000005:
2462 case 0x80000006:
2463 case 0x80000007:
2464 case 0x80000008:
2465 case 0x80000009:
2466 case 0x8000000A:
2467 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2468 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2469 setModified(IsModified_MachineData);
2470 mHWData.backup();
2471 /* Invalidate leaf. */
2472 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2473 break;
2474
2475 default:
2476 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2477 }
2478 return S_OK;
2479}
2480
2481STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2482{
2483 AutoCaller autoCaller(this);
2484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2485
2486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2487
2488 HRESULT rc = checkStateDependency(MutableStateDep);
2489 if (FAILED(rc)) return rc;
2490
2491 setModified(IsModified_MachineData);
2492 mHWData.backup();
2493
2494 /* Invalidate all standard leafs. */
2495 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2496 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2497
2498 /* Invalidate all extended leafs. */
2499 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2500 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2501
2502 return S_OK;
2503}
2504
2505STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2506{
2507 CheckComArgOutPointerValid(aVal);
2508
2509 AutoCaller autoCaller(this);
2510 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2511
2512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2513
2514 switch(property)
2515 {
2516 case HWVirtExPropertyType_Enabled:
2517 *aVal = mHWData->mHWVirtExEnabled;
2518 break;
2519
2520 case HWVirtExPropertyType_Exclusive:
2521 *aVal = mHWData->mHWVirtExExclusive;
2522 break;
2523
2524 case HWVirtExPropertyType_VPID:
2525 *aVal = mHWData->mHWVirtExVPIDEnabled;
2526 break;
2527
2528 case HWVirtExPropertyType_NestedPaging:
2529 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2530 break;
2531
2532 case HWVirtExPropertyType_UnrestrictedExecution:
2533 *aVal = mHWData->mHWVirtExUXEnabled;
2534 break;
2535
2536 case HWVirtExPropertyType_LargePages:
2537 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2538#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2539 *aVal = FALSE;
2540#endif
2541 break;
2542
2543 case HWVirtExPropertyType_Force:
2544 *aVal = mHWData->mHWVirtExForceEnabled;
2545 break;
2546
2547 default:
2548 return E_INVALIDARG;
2549 }
2550 return S_OK;
2551}
2552
2553STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2554{
2555 AutoCaller autoCaller(this);
2556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2557
2558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2559
2560 HRESULT rc = checkStateDependency(MutableStateDep);
2561 if (FAILED(rc)) return rc;
2562
2563 switch(property)
2564 {
2565 case HWVirtExPropertyType_Enabled:
2566 setModified(IsModified_MachineData);
2567 mHWData.backup();
2568 mHWData->mHWVirtExEnabled = !!aVal;
2569 break;
2570
2571 case HWVirtExPropertyType_Exclusive:
2572 setModified(IsModified_MachineData);
2573 mHWData.backup();
2574 mHWData->mHWVirtExExclusive = !!aVal;
2575 break;
2576
2577 case HWVirtExPropertyType_VPID:
2578 setModified(IsModified_MachineData);
2579 mHWData.backup();
2580 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2581 break;
2582
2583 case HWVirtExPropertyType_NestedPaging:
2584 setModified(IsModified_MachineData);
2585 mHWData.backup();
2586 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2587 break;
2588
2589 case HWVirtExPropertyType_UnrestrictedExecution:
2590 setModified(IsModified_MachineData);
2591 mHWData.backup();
2592 mHWData->mHWVirtExUXEnabled = !!aVal;
2593 break;
2594
2595 case HWVirtExPropertyType_LargePages:
2596 setModified(IsModified_MachineData);
2597 mHWData.backup();
2598 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2599 break;
2600
2601 case HWVirtExPropertyType_Force:
2602 setModified(IsModified_MachineData);
2603 mHWData.backup();
2604 mHWData->mHWVirtExForceEnabled = !!aVal;
2605 break;
2606
2607 default:
2608 return E_INVALIDARG;
2609 }
2610
2611 return S_OK;
2612}
2613
2614STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2615{
2616 CheckComArgOutPointerValid(aSnapshotFolder);
2617
2618 AutoCaller autoCaller(this);
2619 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2620
2621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2622
2623 Utf8Str strFullSnapshotFolder;
2624 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2625 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2626
2627 return S_OK;
2628}
2629
2630STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2631{
2632 /* @todo (r=dmik):
2633 * 1. Allow to change the name of the snapshot folder containing snapshots
2634 * 2. Rename the folder on disk instead of just changing the property
2635 * value (to be smart and not to leave garbage). Note that it cannot be
2636 * done here because the change may be rolled back. Thus, the right
2637 * place is #saveSettings().
2638 */
2639
2640 AutoCaller autoCaller(this);
2641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2642
2643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2644
2645 HRESULT rc = checkStateDependency(MutableStateDep);
2646 if (FAILED(rc)) return rc;
2647
2648 if (!mData->mCurrentSnapshot.isNull())
2649 return setError(E_FAIL,
2650 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2651
2652 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2653
2654 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2655 if (strSnapshotFolder.isEmpty())
2656 strSnapshotFolder = "Snapshots";
2657 int vrc = calculateFullPath(strSnapshotFolder,
2658 strSnapshotFolder);
2659 if (RT_FAILURE(vrc))
2660 return setError(E_FAIL,
2661 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2662 aSnapshotFolder, vrc);
2663
2664 setModified(IsModified_MachineData);
2665 mUserData.backup();
2666
2667 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2668
2669 return S_OK;
2670}
2671
2672STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2673{
2674 CheckComArgOutSafeArrayPointerValid(aAttachments);
2675
2676 AutoCaller autoCaller(this);
2677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2678
2679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2680
2681 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2682 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2683
2684 return S_OK;
2685}
2686
2687STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2688{
2689 CheckComArgOutPointerValid(vrdeServer);
2690
2691 AutoCaller autoCaller(this);
2692 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2693
2694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2695
2696 Assert(!!mVRDEServer);
2697 mVRDEServer.queryInterfaceTo(vrdeServer);
2698
2699 return S_OK;
2700}
2701
2702STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2703{
2704 CheckComArgOutPointerValid(audioAdapter);
2705
2706 AutoCaller autoCaller(this);
2707 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2708
2709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2710
2711 mAudioAdapter.queryInterfaceTo(audioAdapter);
2712 return S_OK;
2713}
2714
2715STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2716{
2717#ifdef VBOX_WITH_VUSB
2718 CheckComArgOutPointerValid(aUSBController);
2719
2720 AutoCaller autoCaller(this);
2721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2722
2723 clearError();
2724 MultiResult rc(S_OK);
2725
2726# ifdef VBOX_WITH_USB
2727 rc = mParent->host()->checkUSBProxyService();
2728 if (FAILED(rc)) return rc;
2729# endif
2730
2731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2732
2733 return rc = mUSBController.queryInterfaceTo(aUSBController);
2734#else
2735 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2736 * extended error info to indicate that USB is simply not available
2737 * (w/o treating it as a failure), for example, as in OSE */
2738 NOREF(aUSBController);
2739 ReturnComNotImplemented();
2740#endif /* VBOX_WITH_VUSB */
2741}
2742
2743STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2744{
2745 CheckComArgOutPointerValid(aFilePath);
2746
2747 AutoLimitedCaller autoCaller(this);
2748 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2749
2750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2751
2752 mData->m_strConfigFileFull.cloneTo(aFilePath);
2753 return S_OK;
2754}
2755
2756STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2757{
2758 CheckComArgOutPointerValid(aModified);
2759
2760 AutoCaller autoCaller(this);
2761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2762
2763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2764
2765 HRESULT rc = checkStateDependency(MutableStateDep);
2766 if (FAILED(rc)) return rc;
2767
2768 if (!mData->pMachineConfigFile->fileExists())
2769 // this is a new machine, and no config file exists yet:
2770 *aModified = TRUE;
2771 else
2772 *aModified = (mData->flModifications != 0);
2773
2774 return S_OK;
2775}
2776
2777STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2778{
2779 CheckComArgOutPointerValid(aSessionState);
2780
2781 AutoCaller autoCaller(this);
2782 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2783
2784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2785
2786 *aSessionState = mData->mSession.mState;
2787
2788 return S_OK;
2789}
2790
2791STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2792{
2793 CheckComArgOutPointerValid(aSessionType);
2794
2795 AutoCaller autoCaller(this);
2796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2797
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 mData->mSession.mType.cloneTo(aSessionType);
2801
2802 return S_OK;
2803}
2804
2805STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2806{
2807 CheckComArgOutPointerValid(aSessionPID);
2808
2809 AutoCaller autoCaller(this);
2810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2811
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 *aSessionPID = mData->mSession.mPID;
2815
2816 return S_OK;
2817}
2818
2819STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2820{
2821 CheckComArgOutPointerValid(machineState);
2822
2823 AutoCaller autoCaller(this);
2824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2825
2826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 *machineState = mData->mMachineState;
2829
2830 return S_OK;
2831}
2832
2833STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2834{
2835 CheckComArgOutPointerValid(aLastStateChange);
2836
2837 AutoCaller autoCaller(this);
2838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2839
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2843
2844 return S_OK;
2845}
2846
2847STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2848{
2849 CheckComArgOutPointerValid(aStateFilePath);
2850
2851 AutoCaller autoCaller(this);
2852 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2853
2854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2857
2858 return S_OK;
2859}
2860
2861STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2862{
2863 CheckComArgOutPointerValid(aLogFolder);
2864
2865 AutoCaller autoCaller(this);
2866 AssertComRCReturnRC(autoCaller.rc());
2867
2868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 Utf8Str logFolder;
2871 getLogFolder(logFolder);
2872 logFolder.cloneTo(aLogFolder);
2873
2874 return S_OK;
2875}
2876
2877STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2878{
2879 CheckComArgOutPointerValid(aCurrentSnapshot);
2880
2881 AutoCaller autoCaller(this);
2882 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2883
2884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2885
2886 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2887
2888 return S_OK;
2889}
2890
2891STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2892{
2893 CheckComArgOutPointerValid(aSnapshotCount);
2894
2895 AutoCaller autoCaller(this);
2896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2897
2898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2901 ? 0
2902 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2903
2904 return S_OK;
2905}
2906
2907STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2908{
2909 CheckComArgOutPointerValid(aCurrentStateModified);
2910
2911 AutoCaller autoCaller(this);
2912 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2913
2914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2915
2916 /* Note: for machines with no snapshots, we always return FALSE
2917 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2918 * reasons :) */
2919
2920 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2921 ? FALSE
2922 : mData->mCurrentStateModified;
2923
2924 return S_OK;
2925}
2926
2927STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2928{
2929 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2930
2931 AutoCaller autoCaller(this);
2932 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2933
2934 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2935
2936 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2937 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2938
2939 return S_OK;
2940}
2941
2942STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2943{
2944 CheckComArgOutPointerValid(aClipboardMode);
2945
2946 AutoCaller autoCaller(this);
2947 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2948
2949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2950
2951 *aClipboardMode = mHWData->mClipboardMode;
2952
2953 return S_OK;
2954}
2955
2956STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2957{
2958 HRESULT rc = S_OK;
2959
2960 AutoCaller autoCaller(this);
2961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2962
2963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2964
2965 alock.release();
2966 rc = onClipboardModeChange(aClipboardMode);
2967 alock.acquire();
2968 if (FAILED(rc)) return rc;
2969
2970 setModified(IsModified_MachineData);
2971 mHWData.backup();
2972 mHWData->mClipboardMode = aClipboardMode;
2973
2974 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2975 if (Global::IsOnline(mData->mMachineState))
2976 saveSettings(NULL);
2977
2978 return S_OK;
2979}
2980
2981STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2982{
2983 CheckComArgOutPointerValid(aDragAndDropMode);
2984
2985 AutoCaller autoCaller(this);
2986 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2987
2988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 *aDragAndDropMode = mHWData->mDragAndDropMode;
2991
2992 return S_OK;
2993}
2994
2995STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2996{
2997 HRESULT rc = S_OK;
2998
2999 AutoCaller autoCaller(this);
3000 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3001
3002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 alock.release();
3005 rc = onDragAndDropModeChange(aDragAndDropMode);
3006 alock.acquire();
3007 if (FAILED(rc)) return rc;
3008
3009 setModified(IsModified_MachineData);
3010 mHWData.backup();
3011 mHWData->mDragAndDropMode = aDragAndDropMode;
3012
3013 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3014 if (Global::IsOnline(mData->mMachineState))
3015 saveSettings(NULL);
3016
3017 return S_OK;
3018}
3019
3020STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3021{
3022 CheckComArgOutPointerValid(aPatterns);
3023
3024 AutoCaller autoCaller(this);
3025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3026
3027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3028
3029 try
3030 {
3031 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3032 }
3033 catch (...)
3034 {
3035 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3036 }
3037
3038 return S_OK;
3039}
3040
3041STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3042{
3043 AutoCaller autoCaller(this);
3044 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3045
3046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3047
3048 HRESULT rc = checkStateDependency(MutableStateDep);
3049 if (FAILED(rc)) return rc;
3050
3051 setModified(IsModified_MachineData);
3052 mHWData.backup();
3053 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3054 return rc;
3055}
3056
3057STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3058{
3059 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3060
3061 AutoCaller autoCaller(this);
3062 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3063
3064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3065
3066 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3067 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3068
3069 return S_OK;
3070}
3071
3072STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3073{
3074 CheckComArgOutPointerValid(aEnabled);
3075
3076 AutoCaller autoCaller(this);
3077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3078
3079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3080
3081 *aEnabled = mUserData->s.fTeleporterEnabled;
3082
3083 return S_OK;
3084}
3085
3086STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3087{
3088 AutoCaller autoCaller(this);
3089 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3090
3091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3092
3093 /* Only allow it to be set to true when PoweredOff or Aborted.
3094 (Clearing it is always permitted.) */
3095 if ( aEnabled
3096 && mData->mRegistered
3097 && ( !isSessionMachine()
3098 || ( mData->mMachineState != MachineState_PoweredOff
3099 && mData->mMachineState != MachineState_Teleported
3100 && mData->mMachineState != MachineState_Aborted
3101 )
3102 )
3103 )
3104 return setError(VBOX_E_INVALID_VM_STATE,
3105 tr("The machine is not powered off (state is %s)"),
3106 Global::stringifyMachineState(mData->mMachineState));
3107
3108 setModified(IsModified_MachineData);
3109 mUserData.backup();
3110 mUserData->s.fTeleporterEnabled = !!aEnabled;
3111
3112 return S_OK;
3113}
3114
3115STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3116{
3117 CheckComArgOutPointerValid(aPort);
3118
3119 AutoCaller autoCaller(this);
3120 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3121
3122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3123
3124 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3125
3126 return S_OK;
3127}
3128
3129STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3130{
3131 if (aPort >= _64K)
3132 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3133
3134 AutoCaller autoCaller(this);
3135 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3136
3137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 HRESULT rc = checkStateDependency(MutableStateDep);
3140 if (FAILED(rc)) return rc;
3141
3142 setModified(IsModified_MachineData);
3143 mUserData.backup();
3144 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3145
3146 return S_OK;
3147}
3148
3149STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3150{
3151 CheckComArgOutPointerValid(aAddress);
3152
3153 AutoCaller autoCaller(this);
3154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3155
3156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3157
3158 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3159
3160 return S_OK;
3161}
3162
3163STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3164{
3165 AutoCaller autoCaller(this);
3166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3167
3168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 HRESULT rc = checkStateDependency(MutableStateDep);
3171 if (FAILED(rc)) return rc;
3172
3173 setModified(IsModified_MachineData);
3174 mUserData.backup();
3175 mUserData->s.strTeleporterAddress = aAddress;
3176
3177 return S_OK;
3178}
3179
3180STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3181{
3182 CheckComArgOutPointerValid(aPassword);
3183
3184 AutoCaller autoCaller(this);
3185 HRESULT hrc = autoCaller.rc();
3186 if (SUCCEEDED(hrc))
3187 {
3188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3189 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3190 }
3191
3192 return hrc;
3193}
3194
3195STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3196{
3197 /*
3198 * Hash the password first.
3199 */
3200 Utf8Str strPassword(aPassword);
3201 if (!strPassword.isEmpty())
3202 {
3203 if (VBoxIsPasswordHashed(&strPassword))
3204 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3205 VBoxHashPassword(&strPassword);
3206 }
3207
3208 /*
3209 * Do the update.
3210 */
3211 AutoCaller autoCaller(this);
3212 HRESULT hrc = autoCaller.rc();
3213 if (SUCCEEDED(hrc))
3214 {
3215 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3216 hrc = checkStateDependency(MutableStateDep);
3217 if (SUCCEEDED(hrc))
3218 {
3219 setModified(IsModified_MachineData);
3220 mUserData.backup();
3221 mUserData->s.strTeleporterPassword = strPassword;
3222 }
3223 }
3224
3225 return hrc;
3226}
3227
3228STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3229{
3230 CheckComArgOutPointerValid(aState);
3231
3232 AutoCaller autoCaller(this);
3233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3234
3235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3236
3237 *aState = mUserData->s.enmFaultToleranceState;
3238 return S_OK;
3239}
3240
3241STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3242{
3243 AutoCaller autoCaller(this);
3244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3245
3246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3247
3248 /* @todo deal with running state change. */
3249 HRESULT rc = checkStateDependency(MutableStateDep);
3250 if (FAILED(rc)) return rc;
3251
3252 setModified(IsModified_MachineData);
3253 mUserData.backup();
3254 mUserData->s.enmFaultToleranceState = aState;
3255 return S_OK;
3256}
3257
3258STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3259{
3260 CheckComArgOutPointerValid(aAddress);
3261
3262 AutoCaller autoCaller(this);
3263 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3264
3265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3266
3267 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3268 return S_OK;
3269}
3270
3271STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3272{
3273 AutoCaller autoCaller(this);
3274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3275
3276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3277
3278 /* @todo deal with running state change. */
3279 HRESULT rc = checkStateDependency(MutableStateDep);
3280 if (FAILED(rc)) return rc;
3281
3282 setModified(IsModified_MachineData);
3283 mUserData.backup();
3284 mUserData->s.strFaultToleranceAddress = aAddress;
3285 return S_OK;
3286}
3287
3288STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3289{
3290 CheckComArgOutPointerValid(aPort);
3291
3292 AutoCaller autoCaller(this);
3293 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3294
3295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3296
3297 *aPort = mUserData->s.uFaultTolerancePort;
3298 return S_OK;
3299}
3300
3301STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3302{
3303 AutoCaller autoCaller(this);
3304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3305
3306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3307
3308 /* @todo deal with running state change. */
3309 HRESULT rc = checkStateDependency(MutableStateDep);
3310 if (FAILED(rc)) return rc;
3311
3312 setModified(IsModified_MachineData);
3313 mUserData.backup();
3314 mUserData->s.uFaultTolerancePort = aPort;
3315 return S_OK;
3316}
3317
3318STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3319{
3320 CheckComArgOutPointerValid(aPassword);
3321
3322 AutoCaller autoCaller(this);
3323 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3324
3325 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3326
3327 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3328
3329 return S_OK;
3330}
3331
3332STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3333{
3334 AutoCaller autoCaller(this);
3335 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3336
3337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3338
3339 /* @todo deal with running state change. */
3340 HRESULT rc = checkStateDependency(MutableStateDep);
3341 if (FAILED(rc)) return rc;
3342
3343 setModified(IsModified_MachineData);
3344 mUserData.backup();
3345 mUserData->s.strFaultTolerancePassword = aPassword;
3346
3347 return S_OK;
3348}
3349
3350STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3351{
3352 CheckComArgOutPointerValid(aInterval);
3353
3354 AutoCaller autoCaller(this);
3355 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3356
3357 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3358
3359 *aInterval = mUserData->s.uFaultToleranceInterval;
3360 return S_OK;
3361}
3362
3363STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3364{
3365 AutoCaller autoCaller(this);
3366 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3367
3368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3369
3370 /* @todo deal with running state change. */
3371 HRESULT rc = checkStateDependency(MutableStateDep);
3372 if (FAILED(rc)) return rc;
3373
3374 setModified(IsModified_MachineData);
3375 mUserData.backup();
3376 mUserData->s.uFaultToleranceInterval = aInterval;
3377 return S_OK;
3378}
3379
3380STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3381{
3382 CheckComArgOutPointerValid(aEnabled);
3383
3384 AutoCaller autoCaller(this);
3385 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3386
3387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3388
3389 *aEnabled = mUserData->s.fRTCUseUTC;
3390
3391 return S_OK;
3392}
3393
3394STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3395{
3396 AutoCaller autoCaller(this);
3397 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3398
3399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3400
3401 /* Only allow it to be set to true when PoweredOff or Aborted.
3402 (Clearing it is always permitted.) */
3403 if ( aEnabled
3404 && mData->mRegistered
3405 && ( !isSessionMachine()
3406 || ( mData->mMachineState != MachineState_PoweredOff
3407 && mData->mMachineState != MachineState_Teleported
3408 && mData->mMachineState != MachineState_Aborted
3409 )
3410 )
3411 )
3412 return setError(VBOX_E_INVALID_VM_STATE,
3413 tr("The machine is not powered off (state is %s)"),
3414 Global::stringifyMachineState(mData->mMachineState));
3415
3416 setModified(IsModified_MachineData);
3417 mUserData.backup();
3418 mUserData->s.fRTCUseUTC = !!aEnabled;
3419
3420 return S_OK;
3421}
3422
3423STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3424{
3425 CheckComArgOutPointerValid(aEnabled);
3426
3427 AutoCaller autoCaller(this);
3428 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3429
3430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3431
3432 *aEnabled = mHWData->mIOCacheEnabled;
3433
3434 return S_OK;
3435}
3436
3437STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3438{
3439 AutoCaller autoCaller(this);
3440 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3441
3442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3443
3444 HRESULT rc = checkStateDependency(MutableStateDep);
3445 if (FAILED(rc)) return rc;
3446
3447 setModified(IsModified_MachineData);
3448 mHWData.backup();
3449 mHWData->mIOCacheEnabled = aEnabled;
3450
3451 return S_OK;
3452}
3453
3454STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3455{
3456 CheckComArgOutPointerValid(aIOCacheSize);
3457
3458 AutoCaller autoCaller(this);
3459 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3460
3461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3462
3463 *aIOCacheSize = mHWData->mIOCacheSize;
3464
3465 return S_OK;
3466}
3467
3468STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3469{
3470 AutoCaller autoCaller(this);
3471 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3472
3473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3474
3475 HRESULT rc = checkStateDependency(MutableStateDep);
3476 if (FAILED(rc)) return rc;
3477
3478 setModified(IsModified_MachineData);
3479 mHWData.backup();
3480 mHWData->mIOCacheSize = aIOCacheSize;
3481
3482 return S_OK;
3483}
3484
3485
3486/**
3487 * @note Locks objects!
3488 */
3489STDMETHODIMP Machine::LockMachine(ISession *aSession,
3490 LockType_T lockType)
3491{
3492 CheckComArgNotNull(aSession);
3493
3494 AutoCaller autoCaller(this);
3495 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3496
3497 /* check the session state */
3498 SessionState_T state;
3499 HRESULT rc = aSession->COMGETTER(State)(&state);
3500 if (FAILED(rc)) return rc;
3501
3502 if (state != SessionState_Unlocked)
3503 return setError(VBOX_E_INVALID_OBJECT_STATE,
3504 tr("The given session is busy"));
3505
3506 // get the client's IInternalSessionControl interface
3507 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3508 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3509 E_INVALIDARG);
3510
3511 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3512
3513 if (!mData->mRegistered)
3514 return setError(E_UNEXPECTED,
3515 tr("The machine '%s' is not registered"),
3516 mUserData->s.strName.c_str());
3517
3518 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3519
3520 SessionState_T oldState = mData->mSession.mState;
3521 /* Hack: in case the session is closing and there is a progress object
3522 * which allows waiting for the session to be closed, take the opportunity
3523 * and do a limited wait (max. 1 second). This helps a lot when the system
3524 * is busy and thus session closing can take a little while. */
3525 if ( mData->mSession.mState == SessionState_Unlocking
3526 && mData->mSession.mProgress)
3527 {
3528 alock.release();
3529 mData->mSession.mProgress->WaitForCompletion(1000);
3530 alock.acquire();
3531 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3532 }
3533
3534 // try again now
3535 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3536 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3537 )
3538 {
3539 // OK, share the session... we are now dealing with three processes:
3540 // 1) VBoxSVC (where this code runs);
3541 // 2) process C: the caller's client process (who wants a shared session);
3542 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3543
3544 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3545 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3546 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3547 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3548 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3549
3550 /*
3551 * Release the lock before calling the client process. It's safe here
3552 * since the only thing to do after we get the lock again is to add
3553 * the remote control to the list (which doesn't directly influence
3554 * anything).
3555 */
3556 alock.release();
3557
3558 // get the console of the session holding the write lock (this is a remote call)
3559 ComPtr<IConsole> pConsoleW;
3560 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3561 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3562 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3563 if (FAILED(rc))
3564 // the failure may occur w/o any error info (from RPC), so provide one
3565 return setError(VBOX_E_VM_ERROR,
3566 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3567
3568 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3569
3570 // share the session machine and W's console with the caller's session
3571 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3572 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3573 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3574
3575 if (FAILED(rc))
3576 // the failure may occur w/o any error info (from RPC), so provide one
3577 return setError(VBOX_E_VM_ERROR,
3578 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3579 alock.acquire();
3580
3581 // need to revalidate the state after acquiring the lock again
3582 if (mData->mSession.mState != SessionState_Locked)
3583 {
3584 pSessionControl->Uninitialize();
3585 return setError(VBOX_E_INVALID_SESSION_STATE,
3586 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3587 mUserData->s.strName.c_str());
3588 }
3589
3590 // add the caller's session to the list
3591 mData->mSession.mRemoteControls.push_back(pSessionControl);
3592 }
3593 else if ( mData->mSession.mState == SessionState_Locked
3594 || mData->mSession.mState == SessionState_Unlocking
3595 )
3596 {
3597 // sharing not permitted, or machine still unlocking:
3598 return setError(VBOX_E_INVALID_OBJECT_STATE,
3599 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3600 mUserData->s.strName.c_str());
3601 }
3602 else
3603 {
3604 // machine is not locked: then write-lock the machine (create the session machine)
3605
3606 // must not be busy
3607 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3608
3609 // get the caller's session PID
3610 RTPROCESS pid = NIL_RTPROCESS;
3611 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3612 pSessionControl->GetPID((ULONG*)&pid);
3613 Assert(pid != NIL_RTPROCESS);
3614
3615 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3616
3617 if (fLaunchingVMProcess)
3618 {
3619 // this machine is awaiting for a spawning session to be opened:
3620 // then the calling process must be the one that got started by
3621 // LaunchVMProcess()
3622
3623 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3624 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3625
3626 if (mData->mSession.mPID != pid)
3627 return setError(E_ACCESSDENIED,
3628 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3629 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3630 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3631 }
3632
3633 // create the mutable SessionMachine from the current machine
3634 ComObjPtr<SessionMachine> sessionMachine;
3635 sessionMachine.createObject();
3636 rc = sessionMachine->init(this);
3637 AssertComRC(rc);
3638
3639 /* NOTE: doing return from this function after this point but
3640 * before the end is forbidden since it may call SessionMachine::uninit()
3641 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3642 * lock while still holding the Machine lock in alock so that a deadlock
3643 * is possible due to the wrong lock order. */
3644
3645 if (SUCCEEDED(rc))
3646 {
3647 /*
3648 * Set the session state to Spawning to protect against subsequent
3649 * attempts to open a session and to unregister the machine after
3650 * we release the lock.
3651 */
3652 SessionState_T origState = mData->mSession.mState;
3653 mData->mSession.mState = SessionState_Spawning;
3654
3655 /*
3656 * Release the lock before calling the client process -- it will call
3657 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3658 * because the state is Spawning, so that LaunchVMProcess() and
3659 * LockMachine() calls will fail. This method, called before we
3660 * acquire the lock again, will fail because of the wrong PID.
3661 *
3662 * Note that mData->mSession.mRemoteControls accessed outside
3663 * the lock may not be modified when state is Spawning, so it's safe.
3664 */
3665 alock.release();
3666
3667 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3668 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3669 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3670
3671 /* The failure may occur w/o any error info (from RPC), so provide one */
3672 if (FAILED(rc))
3673 setError(VBOX_E_VM_ERROR,
3674 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3675
3676 if ( SUCCEEDED(rc)
3677 && fLaunchingVMProcess
3678 )
3679 {
3680 /* complete the remote session initialization */
3681
3682 /* get the console from the direct session */
3683 ComPtr<IConsole> console;
3684 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3685 ComAssertComRC(rc);
3686
3687 if (SUCCEEDED(rc) && !console)
3688 {
3689 ComAssert(!!console);
3690 rc = E_FAIL;
3691 }
3692
3693 /* assign machine & console to the remote session */
3694 if (SUCCEEDED(rc))
3695 {
3696 /*
3697 * after LaunchVMProcess(), the first and the only
3698 * entry in remoteControls is that remote session
3699 */
3700 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3701 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3702 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3703
3704 /* The failure may occur w/o any error info (from RPC), so provide one */
3705 if (FAILED(rc))
3706 setError(VBOX_E_VM_ERROR,
3707 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3708 }
3709
3710 if (FAILED(rc))
3711 pSessionControl->Uninitialize();
3712 }
3713
3714 /* acquire the lock again */
3715 alock.acquire();
3716
3717 /* Restore the session state */
3718 mData->mSession.mState = origState;
3719 }
3720
3721 // finalize spawning anyway (this is why we don't return on errors above)
3722 if (fLaunchingVMProcess)
3723 {
3724 /* Note that the progress object is finalized later */
3725 /** @todo Consider checking mData->mSession.mProgress for cancellation
3726 * around here. */
3727
3728 /* We don't reset mSession.mPID here because it is necessary for
3729 * SessionMachine::uninit() to reap the child process later. */
3730
3731 if (FAILED(rc))
3732 {
3733 /* Close the remote session, remove the remote control from the list
3734 * and reset session state to Closed (@note keep the code in sync
3735 * with the relevant part in openSession()). */
3736
3737 Assert(mData->mSession.mRemoteControls.size() == 1);
3738 if (mData->mSession.mRemoteControls.size() == 1)
3739 {
3740 ErrorInfoKeeper eik;
3741 mData->mSession.mRemoteControls.front()->Uninitialize();
3742 }
3743
3744 mData->mSession.mRemoteControls.clear();
3745 mData->mSession.mState = SessionState_Unlocked;
3746 }
3747 }
3748 else
3749 {
3750 /* memorize PID of the directly opened session */
3751 if (SUCCEEDED(rc))
3752 mData->mSession.mPID = pid;
3753 }
3754
3755 if (SUCCEEDED(rc))
3756 {
3757 /* memorize the direct session control and cache IUnknown for it */
3758 mData->mSession.mDirectControl = pSessionControl;
3759 mData->mSession.mState = SessionState_Locked;
3760 /* associate the SessionMachine with this Machine */
3761 mData->mSession.mMachine = sessionMachine;
3762
3763 /* request an IUnknown pointer early from the remote party for later
3764 * identity checks (it will be internally cached within mDirectControl
3765 * at least on XPCOM) */
3766 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3767 NOREF(unk);
3768 }
3769
3770 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3771 * would break the lock order */
3772 alock.release();
3773
3774 /* uninitialize the created session machine on failure */
3775 if (FAILED(rc))
3776 sessionMachine->uninit();
3777
3778 }
3779
3780 if (SUCCEEDED(rc))
3781 {
3782 /*
3783 * tell the client watcher thread to update the set of
3784 * machines that have open sessions
3785 */
3786 mParent->updateClientWatcher();
3787
3788 if (oldState != SessionState_Locked)
3789 /* fire an event */
3790 mParent->onSessionStateChange(getId(), SessionState_Locked);
3791 }
3792
3793 return rc;
3794}
3795
3796/**
3797 * @note Locks objects!
3798 */
3799STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3800 IN_BSTR aFrontend,
3801 IN_BSTR aEnvironment,
3802 IProgress **aProgress)
3803{
3804 CheckComArgStr(aFrontend);
3805 Utf8Str strFrontend(aFrontend);
3806 Utf8Str strEnvironment(aEnvironment);
3807 /* "emergencystop" doesn't need the session, so skip the checks/interface
3808 * retrieval. This code doesn't quite fit in here, but introducing a
3809 * special API method would be even more effort, and would require explicit
3810 * support by every API client. It's better to hide the feature a bit. */
3811 if (strFrontend != "emergencystop")
3812 CheckComArgNotNull(aSession);
3813 CheckComArgOutPointerValid(aProgress);
3814
3815 AutoCaller autoCaller(this);
3816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3817
3818 HRESULT rc = S_OK;
3819 if (strFrontend.isEmpty())
3820 {
3821 Bstr bstrFrontend;
3822 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3823 if (FAILED(rc))
3824 return rc;
3825 strFrontend = bstrFrontend;
3826 if (strFrontend.isEmpty())
3827 {
3828 ComPtr<ISystemProperties> systemProperties;
3829 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3830 if (FAILED(rc))
3831 return rc;
3832 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3833 if (FAILED(rc))
3834 return rc;
3835 strFrontend = bstrFrontend;
3836 }
3837 /* paranoia - emergencystop is not a valid default */
3838 if (strFrontend == "emergencystop")
3839 strFrontend = Utf8Str::Empty;
3840 }
3841 /* default frontend: Qt GUI */
3842 if (strFrontend.isEmpty())
3843 strFrontend = "GUI/Qt";
3844
3845 if (strFrontend != "emergencystop")
3846 {
3847 /* check the session state */
3848 SessionState_T state;
3849 rc = aSession->COMGETTER(State)(&state);
3850 if (FAILED(rc))
3851 return rc;
3852
3853 if (state != SessionState_Unlocked)
3854 return setError(VBOX_E_INVALID_OBJECT_STATE,
3855 tr("The given session is busy"));
3856
3857 /* get the IInternalSessionControl interface */
3858 ComPtr<IInternalSessionControl> control(aSession);
3859 ComAssertMsgRet(!control.isNull(),
3860 ("No IInternalSessionControl interface"),
3861 E_INVALIDARG);
3862
3863 /* get the teleporter enable state for the progress object init. */
3864 BOOL fTeleporterEnabled;
3865 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3866 if (FAILED(rc))
3867 return rc;
3868
3869 /* create a progress object */
3870 ComObjPtr<ProgressProxy> progress;
3871 progress.createObject();
3872 rc = progress->init(mParent,
3873 static_cast<IMachine*>(this),
3874 Bstr(tr("Starting VM")).raw(),
3875 TRUE /* aCancelable */,
3876 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3877 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3878 2 /* uFirstOperationWeight */,
3879 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3880
3881 if (SUCCEEDED(rc))
3882 {
3883 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3884 if (SUCCEEDED(rc))
3885 {
3886 progress.queryInterfaceTo(aProgress);
3887
3888 /* signal the client watcher thread */
3889 mParent->updateClientWatcher();
3890
3891 /* fire an event */
3892 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3893 }
3894 }
3895 }
3896 else
3897 {
3898 /* no progress object - either instant success or failure */
3899 *aProgress = NULL;
3900
3901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3902
3903 if (mData->mSession.mState != SessionState_Locked)
3904 return setError(VBOX_E_INVALID_OBJECT_STATE,
3905 tr("The machine '%s' is not locked by a session"),
3906 mUserData->s.strName.c_str());
3907
3908 /* must have a VM process associated - do not kill normal API clients
3909 * with an open session */
3910 if (!Global::IsOnline(mData->mMachineState))
3911 return setError(VBOX_E_INVALID_OBJECT_STATE,
3912 tr("The machine '%s' does not have a VM process"),
3913 mUserData->s.strName.c_str());
3914
3915 /* forcibly terminate the VM process */
3916 if (mData->mSession.mPID != NIL_RTPROCESS)
3917 RTProcTerminate(mData->mSession.mPID);
3918
3919 /* signal the client watcher thread, as most likely the client has
3920 * been terminated */
3921 mParent->updateClientWatcher();
3922 }
3923
3924 return rc;
3925}
3926
3927STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3928{
3929 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3930 return setError(E_INVALIDARG,
3931 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3932 aPosition, SchemaDefs::MaxBootPosition);
3933
3934 if (aDevice == DeviceType_USB)
3935 return setError(E_NOTIMPL,
3936 tr("Booting from USB device is currently not supported"));
3937
3938 AutoCaller autoCaller(this);
3939 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3940
3941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3942
3943 HRESULT rc = checkStateDependency(MutableStateDep);
3944 if (FAILED(rc)) return rc;
3945
3946 setModified(IsModified_MachineData);
3947 mHWData.backup();
3948 mHWData->mBootOrder[aPosition - 1] = aDevice;
3949
3950 return S_OK;
3951}
3952
3953STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3954{
3955 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3956 return setError(E_INVALIDARG,
3957 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3958 aPosition, SchemaDefs::MaxBootPosition);
3959
3960 AutoCaller autoCaller(this);
3961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3962
3963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3964
3965 *aDevice = mHWData->mBootOrder[aPosition - 1];
3966
3967 return S_OK;
3968}
3969
3970STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3971 LONG aControllerPort,
3972 LONG aDevice,
3973 DeviceType_T aType,
3974 IMedium *aMedium)
3975{
3976 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3977 aControllerName, aControllerPort, aDevice, aType, aMedium));
3978
3979 CheckComArgStrNotEmptyOrNull(aControllerName);
3980
3981 AutoCaller autoCaller(this);
3982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3983
3984 // request the host lock first, since might be calling Host methods for getting host drives;
3985 // next, protect the media tree all the while we're in here, as well as our member variables
3986 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3987 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3988
3989 HRESULT rc = checkStateDependency(MutableStateDep);
3990 if (FAILED(rc)) return rc;
3991
3992 /// @todo NEWMEDIA implicit machine registration
3993 if (!mData->mRegistered)
3994 return setError(VBOX_E_INVALID_OBJECT_STATE,
3995 tr("Cannot attach storage devices to an unregistered machine"));
3996
3997 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3998
3999 /* Check for an existing controller. */
4000 ComObjPtr<StorageController> ctl;
4001 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4002 if (FAILED(rc)) return rc;
4003
4004 StorageControllerType_T ctrlType;
4005 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4006 if (FAILED(rc))
4007 return setError(E_FAIL,
4008 tr("Could not get type of controller '%ls'"),
4009 aControllerName);
4010
4011 bool fSilent = false;
4012 Utf8Str strReconfig;
4013
4014 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4015 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4016 if (FAILED(rc))
4017 return rc;
4018 if ( mData->mMachineState == MachineState_Paused
4019 && strReconfig == "1")
4020 fSilent = true;
4021
4022 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4023 bool fHotplug = false;
4024 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4025 fHotplug = true;
4026
4027 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4028 return setError(VBOX_E_INVALID_VM_STATE,
4029 tr("Controller '%ls' does not support hotplugging"),
4030 aControllerName);
4031
4032 // check that the port and device are not out of range
4033 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4034 if (FAILED(rc)) return rc;
4035
4036 /* check if the device slot is already busy */
4037 MediumAttachment *pAttachTemp;
4038 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4039 aControllerName,
4040 aControllerPort,
4041 aDevice)))
4042 {
4043 Medium *pMedium = pAttachTemp->getMedium();
4044 if (pMedium)
4045 {
4046 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4047 return setError(VBOX_E_OBJECT_IN_USE,
4048 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4049 pMedium->getLocationFull().c_str(),
4050 aControllerPort,
4051 aDevice,
4052 aControllerName);
4053 }
4054 else
4055 return setError(VBOX_E_OBJECT_IN_USE,
4056 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4057 aControllerPort, aDevice, aControllerName);
4058 }
4059
4060 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4061 if (aMedium && medium.isNull())
4062 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4063
4064 AutoCaller mediumCaller(medium);
4065 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4066
4067 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4068
4069 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4070 && !medium.isNull()
4071 )
4072 return setError(VBOX_E_OBJECT_IN_USE,
4073 tr("Medium '%s' is already attached to this virtual machine"),
4074 medium->getLocationFull().c_str());
4075
4076 if (!medium.isNull())
4077 {
4078 MediumType_T mtype = medium->getType();
4079 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4080 // For DVDs it's not written to the config file, so needs no global config
4081 // version bump. For floppies it's a new attribute "type", which is ignored
4082 // by older VirtualBox version, so needs no global config version bump either.
4083 // For hard disks this type is not accepted.
4084 if (mtype == MediumType_MultiAttach)
4085 {
4086 // This type is new with VirtualBox 4.0 and therefore requires settings
4087 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4088 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4089 // two reasons: The medium type is a property of the media registry tree, which
4090 // can reside in the global config file (for pre-4.0 media); we would therefore
4091 // possibly need to bump the global config version. We don't want to do that though
4092 // because that might make downgrading to pre-4.0 impossible.
4093 // As a result, we can only use these two new types if the medium is NOT in the
4094 // global registry:
4095 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4096 if ( medium->isInRegistry(uuidGlobalRegistry)
4097 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4098 )
4099 return setError(VBOX_E_INVALID_OBJECT_STATE,
4100 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4101 "to machines that were created with VirtualBox 4.0 or later"),
4102 medium->getLocationFull().c_str());
4103 }
4104 }
4105
4106 bool fIndirect = false;
4107 if (!medium.isNull())
4108 fIndirect = medium->isReadOnly();
4109 bool associate = true;
4110
4111 do
4112 {
4113 if ( aType == DeviceType_HardDisk
4114 && mMediaData.isBackedUp())
4115 {
4116 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4117
4118 /* check if the medium was attached to the VM before we started
4119 * changing attachments in which case the attachment just needs to
4120 * be restored */
4121 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4122 {
4123 AssertReturn(!fIndirect, E_FAIL);
4124
4125 /* see if it's the same bus/channel/device */
4126 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4127 {
4128 /* the simplest case: restore the whole attachment
4129 * and return, nothing else to do */
4130 mMediaData->mAttachments.push_back(pAttachTemp);
4131
4132 /* Reattach the medium to the VM. */
4133 if (fHotplug || fSilent)
4134 {
4135 mediumLock.release();
4136 treeLock.release();
4137 alock.release();
4138
4139 MediumLockList *pMediumLockList(new MediumLockList());
4140
4141 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4142 true /* fMediumLockWrite */,
4143 NULL,
4144 *pMediumLockList);
4145 alock.acquire();
4146 if (FAILED(rc))
4147 delete pMediumLockList;
4148 else
4149 {
4150 mData->mSession.mLockedMedia.Unlock();
4151 alock.release();
4152 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4153 mData->mSession.mLockedMedia.Lock();
4154 alock.acquire();
4155 }
4156 alock.release();
4157
4158 if (SUCCEEDED(rc))
4159 {
4160 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4161 /* Remove lock list in case of error. */
4162 if (FAILED(rc))
4163 {
4164 mData->mSession.mLockedMedia.Unlock();
4165 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4166 mData->mSession.mLockedMedia.Lock();
4167 }
4168 }
4169 }
4170
4171 return S_OK;
4172 }
4173
4174 /* bus/channel/device differ; we need a new attachment object,
4175 * but don't try to associate it again */
4176 associate = false;
4177 break;
4178 }
4179 }
4180
4181 /* go further only if the attachment is to be indirect */
4182 if (!fIndirect)
4183 break;
4184
4185 /* perform the so called smart attachment logic for indirect
4186 * attachments. Note that smart attachment is only applicable to base
4187 * hard disks. */
4188
4189 if (medium->getParent().isNull())
4190 {
4191 /* first, investigate the backup copy of the current hard disk
4192 * attachments to make it possible to re-attach existing diffs to
4193 * another device slot w/o losing their contents */
4194 if (mMediaData.isBackedUp())
4195 {
4196 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4197
4198 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4199 uint32_t foundLevel = 0;
4200
4201 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4202 it != oldAtts.end();
4203 ++it)
4204 {
4205 uint32_t level = 0;
4206 MediumAttachment *pAttach = *it;
4207 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4208 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4209 if (pMedium.isNull())
4210 continue;
4211
4212 if (pMedium->getBase(&level) == medium)
4213 {
4214 /* skip the hard disk if its currently attached (we
4215 * cannot attach the same hard disk twice) */
4216 if (findAttachment(mMediaData->mAttachments,
4217 pMedium))
4218 continue;
4219
4220 /* matched device, channel and bus (i.e. attached to the
4221 * same place) will win and immediately stop the search;
4222 * otherwise the attachment that has the youngest
4223 * descendant of medium will be used
4224 */
4225 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4226 {
4227 /* the simplest case: restore the whole attachment
4228 * and return, nothing else to do */
4229 mMediaData->mAttachments.push_back(*it);
4230
4231 /* Reattach the medium to the VM. */
4232 if (fHotplug || fSilent)
4233 {
4234 mediumLock.release();
4235 treeLock.release();
4236 alock.release();
4237
4238 MediumLockList *pMediumLockList(new MediumLockList());
4239
4240 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4241 true /* fMediumLockWrite */,
4242 NULL,
4243 *pMediumLockList);
4244 alock.acquire();
4245 if (FAILED(rc))
4246 delete pMediumLockList;
4247 else
4248 {
4249 mData->mSession.mLockedMedia.Unlock();
4250 alock.release();
4251 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4252 mData->mSession.mLockedMedia.Lock();
4253 alock.acquire();
4254 }
4255 alock.release();
4256
4257 if (SUCCEEDED(rc))
4258 {
4259 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4260 /* Remove lock list in case of error. */
4261 if (FAILED(rc))
4262 {
4263 mData->mSession.mLockedMedia.Unlock();
4264 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4265 mData->mSession.mLockedMedia.Lock();
4266 }
4267 }
4268 }
4269
4270 return S_OK;
4271 }
4272 else if ( foundIt == oldAtts.end()
4273 || level > foundLevel /* prefer younger */
4274 )
4275 {
4276 foundIt = it;
4277 foundLevel = level;
4278 }
4279 }
4280 }
4281
4282 if (foundIt != oldAtts.end())
4283 {
4284 /* use the previously attached hard disk */
4285 medium = (*foundIt)->getMedium();
4286 mediumCaller.attach(medium);
4287 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4288 mediumLock.attach(medium);
4289 /* not implicit, doesn't require association with this VM */
4290 fIndirect = false;
4291 associate = false;
4292 /* go right to the MediumAttachment creation */
4293 break;
4294 }
4295 }
4296
4297 /* must give up the medium lock and medium tree lock as below we
4298 * go over snapshots, which needs a lock with higher lock order. */
4299 mediumLock.release();
4300 treeLock.release();
4301
4302 /* then, search through snapshots for the best diff in the given
4303 * hard disk's chain to base the new diff on */
4304
4305 ComObjPtr<Medium> base;
4306 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4307 while (snap)
4308 {
4309 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4310
4311 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4312
4313 MediumAttachment *pAttachFound = NULL;
4314 uint32_t foundLevel = 0;
4315
4316 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4317 it != snapAtts.end();
4318 ++it)
4319 {
4320 MediumAttachment *pAttach = *it;
4321 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4322 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4323 if (pMedium.isNull())
4324 continue;
4325
4326 uint32_t level = 0;
4327 if (pMedium->getBase(&level) == medium)
4328 {
4329 /* matched device, channel and bus (i.e. attached to the
4330 * same place) will win and immediately stop the search;
4331 * otherwise the attachment that has the youngest
4332 * descendant of medium will be used
4333 */
4334 if ( pAttach->getDevice() == aDevice
4335 && pAttach->getPort() == aControllerPort
4336 && pAttach->getControllerName() == aControllerName
4337 )
4338 {
4339 pAttachFound = pAttach;
4340 break;
4341 }
4342 else if ( !pAttachFound
4343 || level > foundLevel /* prefer younger */
4344 )
4345 {
4346 pAttachFound = pAttach;
4347 foundLevel = level;
4348 }
4349 }
4350 }
4351
4352 if (pAttachFound)
4353 {
4354 base = pAttachFound->getMedium();
4355 break;
4356 }
4357
4358 snap = snap->getParent();
4359 }
4360
4361 /* re-lock medium tree and the medium, as we need it below */
4362 treeLock.acquire();
4363 mediumLock.acquire();
4364
4365 /* found a suitable diff, use it as a base */
4366 if (!base.isNull())
4367 {
4368 medium = base;
4369 mediumCaller.attach(medium);
4370 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4371 mediumLock.attach(medium);
4372 }
4373 }
4374
4375 Utf8Str strFullSnapshotFolder;
4376 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4377
4378 ComObjPtr<Medium> diff;
4379 diff.createObject();
4380 // store this diff in the same registry as the parent
4381 Guid uuidRegistryParent;
4382 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4383 {
4384 // parent image has no registry: this can happen if we're attaching a new immutable
4385 // image that has not yet been attached (medium then points to the base and we're
4386 // creating the diff image for the immutable, and the parent is not yet registered);
4387 // put the parent in the machine registry then
4388 mediumLock.release();
4389 treeLock.release();
4390 alock.release();
4391 addMediumToRegistry(medium);
4392 alock.acquire();
4393 treeLock.acquire();
4394 mediumLock.acquire();
4395 medium->getFirstRegistryMachineId(uuidRegistryParent);
4396 }
4397 rc = diff->init(mParent,
4398 medium->getPreferredDiffFormat(),
4399 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4400 uuidRegistryParent);
4401 if (FAILED(rc)) return rc;
4402
4403 /* Apply the normal locking logic to the entire chain. */
4404 MediumLockList *pMediumLockList(new MediumLockList());
4405 mediumLock.release();
4406 treeLock.release();
4407 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4408 true /* fMediumLockWrite */,
4409 medium,
4410 *pMediumLockList);
4411 treeLock.acquire();
4412 mediumLock.acquire();
4413 if (SUCCEEDED(rc))
4414 {
4415 mediumLock.release();
4416 treeLock.release();
4417 rc = pMediumLockList->Lock();
4418 treeLock.acquire();
4419 mediumLock.acquire();
4420 if (FAILED(rc))
4421 setError(rc,
4422 tr("Could not lock medium when creating diff '%s'"),
4423 diff->getLocationFull().c_str());
4424 else
4425 {
4426 /* will release the lock before the potentially lengthy
4427 * operation, so protect with the special state */
4428 MachineState_T oldState = mData->mMachineState;
4429 setMachineState(MachineState_SettingUp);
4430
4431 mediumLock.release();
4432 treeLock.release();
4433 alock.release();
4434
4435 rc = medium->createDiffStorage(diff,
4436 MediumVariant_Standard,
4437 pMediumLockList,
4438 NULL /* aProgress */,
4439 true /* aWait */);
4440
4441 alock.acquire();
4442 treeLock.acquire();
4443 mediumLock.acquire();
4444
4445 setMachineState(oldState);
4446 }
4447 }
4448
4449 /* Unlock the media and free the associated memory. */
4450 delete pMediumLockList;
4451
4452 if (FAILED(rc)) return rc;
4453
4454 /* use the created diff for the actual attachment */
4455 medium = diff;
4456 mediumCaller.attach(medium);
4457 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4458 mediumLock.attach(medium);
4459 }
4460 while (0);
4461
4462 ComObjPtr<MediumAttachment> attachment;
4463 attachment.createObject();
4464 rc = attachment->init(this,
4465 medium,
4466 aControllerName,
4467 aControllerPort,
4468 aDevice,
4469 aType,
4470 fIndirect,
4471 false /* fPassthrough */,
4472 false /* fTempEject */,
4473 false /* fNonRotational */,
4474 false /* fDiscard */,
4475 Utf8Str::Empty);
4476 if (FAILED(rc)) return rc;
4477
4478 if (associate && !medium.isNull())
4479 {
4480 // as the last step, associate the medium to the VM
4481 rc = medium->addBackReference(mData->mUuid);
4482 // here we can fail because of Deleting, or being in process of creating a Diff
4483 if (FAILED(rc)) return rc;
4484
4485 mediumLock.release();
4486 treeLock.release();
4487 alock.release();
4488 addMediumToRegistry(medium);
4489 alock.acquire();
4490 treeLock.acquire();
4491 mediumLock.acquire();
4492 }
4493
4494 /* success: finally remember the attachment */
4495 setModified(IsModified_Storage);
4496 mMediaData.backup();
4497 mMediaData->mAttachments.push_back(attachment);
4498
4499 mediumLock.release();
4500 treeLock.release();
4501 alock.release();
4502
4503 if (fHotplug || fSilent)
4504 {
4505 MediumLockList *pMediumLockList(new MediumLockList());
4506
4507 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4508 true /* fMediumLockWrite */,
4509 NULL,
4510 *pMediumLockList);
4511 alock.acquire();
4512 if (FAILED(rc))
4513 delete pMediumLockList;
4514 else
4515 {
4516 mData->mSession.mLockedMedia.Unlock();
4517 alock.release();
4518 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4519 mData->mSession.mLockedMedia.Lock();
4520 alock.acquire();
4521 }
4522 alock.release();
4523
4524 if (SUCCEEDED(rc))
4525 {
4526 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4527 /* Remove lock list in case of error. */
4528 if (FAILED(rc))
4529 {
4530 mData->mSession.mLockedMedia.Unlock();
4531 mData->mSession.mLockedMedia.Remove(attachment);
4532 mData->mSession.mLockedMedia.Lock();
4533 }
4534 }
4535 }
4536
4537 mParent->saveModifiedRegistries();
4538
4539 return rc;
4540}
4541
4542STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4543 LONG aDevice)
4544{
4545 CheckComArgStrNotEmptyOrNull(aControllerName);
4546
4547 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4548 aControllerName, aControllerPort, aDevice));
4549
4550 AutoCaller autoCaller(this);
4551 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4552
4553 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4554
4555 HRESULT rc = checkStateDependency(MutableStateDep);
4556 if (FAILED(rc)) return rc;
4557
4558 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4559
4560 /* Check for an existing controller. */
4561 ComObjPtr<StorageController> ctl;
4562 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4563 if (FAILED(rc)) return rc;
4564
4565 StorageControllerType_T ctrlType;
4566 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4567 if (FAILED(rc))
4568 return setError(E_FAIL,
4569 tr("Could not get type of controller '%ls'"),
4570 aControllerName);
4571
4572 bool fSilent = false;
4573 Utf8Str strReconfig;
4574
4575 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4576 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4577 if (FAILED(rc))
4578 return rc;
4579 if ( mData->mMachineState == MachineState_Paused
4580 && strReconfig == "1")
4581 fSilent = true;
4582
4583 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4584 bool fHotplug = false;
4585 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4586 fHotplug = true;
4587
4588 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4589 return setError(VBOX_E_INVALID_VM_STATE,
4590 tr("Controller '%ls' does not support hotplugging"),
4591 aControllerName);
4592
4593 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4594 aControllerName,
4595 aControllerPort,
4596 aDevice);
4597 if (!pAttach)
4598 return setError(VBOX_E_OBJECT_NOT_FOUND,
4599 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4600 aDevice, aControllerPort, aControllerName);
4601
4602 /*
4603 * The VM has to detach the device before we delete any implicit diffs.
4604 * If this fails we can roll back without loosing data.
4605 */
4606 if (fHotplug || fSilent)
4607 {
4608 alock.release();
4609 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4610 alock.acquire();
4611 }
4612 if (FAILED(rc)) return rc;
4613
4614 /* If we are here everything went well and we can delete the implicit now. */
4615 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4616
4617 alock.release();
4618
4619 mParent->saveModifiedRegistries();
4620
4621 return rc;
4622}
4623
4624STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4625 LONG aDevice, BOOL aPassthrough)
4626{
4627 CheckComArgStrNotEmptyOrNull(aControllerName);
4628
4629 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4630 aControllerName, aControllerPort, aDevice, aPassthrough));
4631
4632 AutoCaller autoCaller(this);
4633 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4634
4635 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4636
4637 HRESULT rc = checkStateDependency(MutableStateDep);
4638 if (FAILED(rc)) return rc;
4639
4640 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4641
4642 if (Global::IsOnlineOrTransient(mData->mMachineState))
4643 return setError(VBOX_E_INVALID_VM_STATE,
4644 tr("Invalid machine state: %s"),
4645 Global::stringifyMachineState(mData->mMachineState));
4646
4647 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4648 aControllerName,
4649 aControllerPort,
4650 aDevice);
4651 if (!pAttach)
4652 return setError(VBOX_E_OBJECT_NOT_FOUND,
4653 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4654 aDevice, aControllerPort, aControllerName);
4655
4656
4657 setModified(IsModified_Storage);
4658 mMediaData.backup();
4659
4660 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4661
4662 if (pAttach->getType() != DeviceType_DVD)
4663 return setError(E_INVALIDARG,
4664 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4665 aDevice, aControllerPort, aControllerName);
4666 pAttach->updatePassthrough(!!aPassthrough);
4667
4668 return S_OK;
4669}
4670
4671STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4672 LONG aDevice, BOOL aTemporaryEject)
4673{
4674 CheckComArgStrNotEmptyOrNull(aControllerName);
4675
4676 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4677 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4678
4679 AutoCaller autoCaller(this);
4680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4681
4682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4683
4684 HRESULT rc = checkStateDependency(MutableStateDep);
4685 if (FAILED(rc)) return rc;
4686
4687 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4688 aControllerName,
4689 aControllerPort,
4690 aDevice);
4691 if (!pAttach)
4692 return setError(VBOX_E_OBJECT_NOT_FOUND,
4693 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4694 aDevice, aControllerPort, aControllerName);
4695
4696
4697 setModified(IsModified_Storage);
4698 mMediaData.backup();
4699
4700 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4701
4702 if (pAttach->getType() != DeviceType_DVD)
4703 return setError(E_INVALIDARG,
4704 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4705 aDevice, aControllerPort, aControllerName);
4706 pAttach->updateTempEject(!!aTemporaryEject);
4707
4708 return S_OK;
4709}
4710
4711STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4712 LONG aDevice, BOOL aNonRotational)
4713{
4714 CheckComArgStrNotEmptyOrNull(aControllerName);
4715
4716 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4717 aControllerName, aControllerPort, aDevice, aNonRotational));
4718
4719 AutoCaller autoCaller(this);
4720 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4721
4722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4723
4724 HRESULT rc = checkStateDependency(MutableStateDep);
4725 if (FAILED(rc)) return rc;
4726
4727 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4728
4729 if (Global::IsOnlineOrTransient(mData->mMachineState))
4730 return setError(VBOX_E_INVALID_VM_STATE,
4731 tr("Invalid machine state: %s"),
4732 Global::stringifyMachineState(mData->mMachineState));
4733
4734 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4735 aControllerName,
4736 aControllerPort,
4737 aDevice);
4738 if (!pAttach)
4739 return setError(VBOX_E_OBJECT_NOT_FOUND,
4740 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4741 aDevice, aControllerPort, aControllerName);
4742
4743
4744 setModified(IsModified_Storage);
4745 mMediaData.backup();
4746
4747 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4748
4749 if (pAttach->getType() != DeviceType_HardDisk)
4750 return setError(E_INVALIDARG,
4751 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"),
4752 aDevice, aControllerPort, aControllerName);
4753 pAttach->updateNonRotational(!!aNonRotational);
4754
4755 return S_OK;
4756}
4757
4758STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4759 LONG aDevice, BOOL aDiscard)
4760{
4761 CheckComArgStrNotEmptyOrNull(aControllerName);
4762
4763 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4764 aControllerName, aControllerPort, aDevice, aDiscard));
4765
4766 AutoCaller autoCaller(this);
4767 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4768
4769 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4770
4771 HRESULT rc = checkStateDependency(MutableStateDep);
4772 if (FAILED(rc)) return rc;
4773
4774 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4775
4776 if (Global::IsOnlineOrTransient(mData->mMachineState))
4777 return setError(VBOX_E_INVALID_VM_STATE,
4778 tr("Invalid machine state: %s"),
4779 Global::stringifyMachineState(mData->mMachineState));
4780
4781 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4782 aControllerName,
4783 aControllerPort,
4784 aDevice);
4785 if (!pAttach)
4786 return setError(VBOX_E_OBJECT_NOT_FOUND,
4787 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4788 aDevice, aControllerPort, aControllerName);
4789
4790
4791 setModified(IsModified_Storage);
4792 mMediaData.backup();
4793
4794 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4795
4796 if (pAttach->getType() != DeviceType_HardDisk)
4797 return setError(E_INVALIDARG,
4798 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"),
4799 aDevice, aControllerPort, aControllerName);
4800 pAttach->updateDiscard(!!aDiscard);
4801
4802 return S_OK;
4803}
4804
4805STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4806 LONG aDevice)
4807{
4808 int rc = S_OK;
4809 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4810 aControllerName, aControllerPort, aDevice));
4811
4812 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4813
4814 return rc;
4815}
4816
4817STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4818 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4819{
4820 CheckComArgStrNotEmptyOrNull(aControllerName);
4821
4822 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4823 aControllerName, aControllerPort, aDevice));
4824
4825 AutoCaller autoCaller(this);
4826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4827
4828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4829
4830 HRESULT rc = checkStateDependency(MutableStateDep);
4831 if (FAILED(rc)) return rc;
4832
4833 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4834
4835 if (Global::IsOnlineOrTransient(mData->mMachineState))
4836 return setError(VBOX_E_INVALID_VM_STATE,
4837 tr("Invalid machine state: %s"),
4838 Global::stringifyMachineState(mData->mMachineState));
4839
4840 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4841 aControllerName,
4842 aControllerPort,
4843 aDevice);
4844 if (!pAttach)
4845 return setError(VBOX_E_OBJECT_NOT_FOUND,
4846 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4847 aDevice, aControllerPort, aControllerName);
4848
4849
4850 setModified(IsModified_Storage);
4851 mMediaData.backup();
4852
4853 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4854 if (aBandwidthGroup && group.isNull())
4855 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4856
4857 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4858
4859 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4860 if (strBandwidthGroupOld.isNotEmpty())
4861 {
4862 /* Get the bandwidth group object and release it - this must not fail. */
4863 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4864 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4865 Assert(SUCCEEDED(rc));
4866
4867 pBandwidthGroupOld->release();
4868 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4869 }
4870
4871 if (!group.isNull())
4872 {
4873 group->reference();
4874 pAttach->updateBandwidthGroup(group->getName());
4875 }
4876
4877 return S_OK;
4878}
4879
4880STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4881 LONG aControllerPort,
4882 LONG aDevice,
4883 DeviceType_T aType)
4884{
4885 HRESULT rc = S_OK;
4886
4887 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4888 aControllerName, aControllerPort, aDevice, aType));
4889
4890 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4891
4892 return rc;
4893}
4894
4895
4896
4897STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4898 LONG aControllerPort,
4899 LONG aDevice,
4900 BOOL aForce)
4901{
4902 int rc = S_OK;
4903 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4904 aControllerName, aControllerPort, aForce));
4905
4906 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4907
4908 return rc;
4909}
4910
4911STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4912 LONG aControllerPort,
4913 LONG aDevice,
4914 IMedium *aMedium,
4915 BOOL aForce)
4916{
4917 int rc = S_OK;
4918 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4919 aControllerName, aControllerPort, aDevice, aForce));
4920
4921 CheckComArgStrNotEmptyOrNull(aControllerName);
4922
4923 AutoCaller autoCaller(this);
4924 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4925
4926 // request the host lock first, since might be calling Host methods for getting host drives;
4927 // next, protect the media tree all the while we're in here, as well as our member variables
4928 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4929 this->lockHandle(),
4930 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4931
4932 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4933 aControllerName,
4934 aControllerPort,
4935 aDevice);
4936 if (pAttach.isNull())
4937 return setError(VBOX_E_OBJECT_NOT_FOUND,
4938 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4939 aDevice, aControllerPort, aControllerName);
4940
4941 /* Remember previously mounted medium. The medium before taking the
4942 * backup is not necessarily the same thing. */
4943 ComObjPtr<Medium> oldmedium;
4944 oldmedium = pAttach->getMedium();
4945
4946 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4947 if (aMedium && pMedium.isNull())
4948 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4949
4950 AutoCaller mediumCaller(pMedium);
4951 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4952
4953 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4954 if (pMedium)
4955 {
4956 DeviceType_T mediumType = pAttach->getType();
4957 switch (mediumType)
4958 {
4959 case DeviceType_DVD:
4960 case DeviceType_Floppy:
4961 break;
4962
4963 default:
4964 return setError(VBOX_E_INVALID_OBJECT_STATE,
4965 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4966 aControllerPort,
4967 aDevice,
4968 aControllerName);
4969 }
4970 }
4971
4972 setModified(IsModified_Storage);
4973 mMediaData.backup();
4974
4975 {
4976 // The backup operation makes the pAttach reference point to the
4977 // old settings. Re-get the correct reference.
4978 pAttach = findAttachment(mMediaData->mAttachments,
4979 aControllerName,
4980 aControllerPort,
4981 aDevice);
4982 if (!oldmedium.isNull())
4983 oldmedium->removeBackReference(mData->mUuid);
4984 if (!pMedium.isNull())
4985 {
4986 pMedium->addBackReference(mData->mUuid);
4987
4988 mediumLock.release();
4989 multiLock.release();
4990 addMediumToRegistry(pMedium);
4991 multiLock.acquire();
4992 mediumLock.acquire();
4993 }
4994
4995 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4996 pAttach->updateMedium(pMedium);
4997 }
4998
4999 setModified(IsModified_Storage);
5000
5001 mediumLock.release();
5002 multiLock.release();
5003 rc = onMediumChange(pAttach, aForce);
5004 multiLock.acquire();
5005 mediumLock.acquire();
5006
5007 /* On error roll back this change only. */
5008 if (FAILED(rc))
5009 {
5010 if (!pMedium.isNull())
5011 pMedium->removeBackReference(mData->mUuid);
5012 pAttach = findAttachment(mMediaData->mAttachments,
5013 aControllerName,
5014 aControllerPort,
5015 aDevice);
5016 /* If the attachment is gone in the meantime, bail out. */
5017 if (pAttach.isNull())
5018 return rc;
5019 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5020 if (!oldmedium.isNull())
5021 oldmedium->addBackReference(mData->mUuid);
5022 pAttach->updateMedium(oldmedium);
5023 }
5024
5025 mediumLock.release();
5026 multiLock.release();
5027
5028 mParent->saveModifiedRegistries();
5029
5030 return rc;
5031}
5032
5033STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5034 LONG aControllerPort,
5035 LONG aDevice,
5036 IMedium **aMedium)
5037{
5038 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5039 aControllerName, aControllerPort, aDevice));
5040
5041 CheckComArgStrNotEmptyOrNull(aControllerName);
5042 CheckComArgOutPointerValid(aMedium);
5043
5044 AutoCaller autoCaller(this);
5045 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5046
5047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5048
5049 *aMedium = NULL;
5050
5051 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5052 aControllerName,
5053 aControllerPort,
5054 aDevice);
5055 if (pAttach.isNull())
5056 return setError(VBOX_E_OBJECT_NOT_FOUND,
5057 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5058 aDevice, aControllerPort, aControllerName);
5059
5060 pAttach->getMedium().queryInterfaceTo(aMedium);
5061
5062 return S_OK;
5063}
5064
5065STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5066{
5067 CheckComArgOutPointerValid(port);
5068 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5069
5070 AutoCaller autoCaller(this);
5071 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5072
5073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5074
5075 mSerialPorts[slot].queryInterfaceTo(port);
5076
5077 return S_OK;
5078}
5079
5080STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5081{
5082 CheckComArgOutPointerValid(port);
5083 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5084
5085 AutoCaller autoCaller(this);
5086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5087
5088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5089
5090 mParallelPorts[slot].queryInterfaceTo(port);
5091
5092 return S_OK;
5093}
5094
5095STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5096{
5097 CheckComArgOutPointerValid(adapter);
5098 /* Do not assert if slot is out of range, just return the advertised
5099 status. testdriver/vbox.py triggers this in logVmInfo. */
5100 if (slot >= mNetworkAdapters.size())
5101 return setError(E_INVALIDARG,
5102 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5103 slot, mNetworkAdapters.size());
5104
5105 AutoCaller autoCaller(this);
5106 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5107
5108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5109
5110 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5111
5112 return S_OK;
5113}
5114
5115STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5116{
5117 CheckComArgOutSafeArrayPointerValid(aKeys);
5118
5119 AutoCaller autoCaller(this);
5120 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5121
5122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5123
5124 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5125 int i = 0;
5126 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5127 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5128 ++it, ++i)
5129 {
5130 const Utf8Str &strKey = it->first;
5131 strKey.cloneTo(&saKeys[i]);
5132 }
5133 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5134
5135 return S_OK;
5136 }
5137
5138 /**
5139 * @note Locks this object for reading.
5140 */
5141STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5142 BSTR *aValue)
5143{
5144 CheckComArgStrNotEmptyOrNull(aKey);
5145 CheckComArgOutPointerValid(aValue);
5146
5147 AutoCaller autoCaller(this);
5148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5149
5150 /* start with nothing found */
5151 Bstr bstrResult("");
5152
5153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5154
5155 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5156 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5157 // found:
5158 bstrResult = it->second; // source is a Utf8Str
5159
5160 /* return the result to caller (may be empty) */
5161 bstrResult.cloneTo(aValue);
5162
5163 return S_OK;
5164}
5165
5166 /**
5167 * @note Locks mParent for writing + this object for writing.
5168 */
5169STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5170{
5171 CheckComArgStrNotEmptyOrNull(aKey);
5172
5173 AutoCaller autoCaller(this);
5174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5175
5176 Utf8Str strKey(aKey);
5177 Utf8Str strValue(aValue);
5178 Utf8Str strOldValue; // empty
5179
5180 // locking note: we only hold the read lock briefly to look up the old value,
5181 // then release it and call the onExtraCanChange callbacks. There is a small
5182 // chance of a race insofar as the callback might be called twice if two callers
5183 // change the same key at the same time, but that's a much better solution
5184 // than the deadlock we had here before. The actual changing of the extradata
5185 // is then performed under the write lock and race-free.
5186
5187 // look up the old value first; if nothing has changed then we need not do anything
5188 {
5189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5190 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5191 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5192 strOldValue = it->second;
5193 }
5194
5195 bool fChanged;
5196 if ((fChanged = (strOldValue != strValue)))
5197 {
5198 // ask for permission from all listeners outside the locks;
5199 // onExtraDataCanChange() only briefly requests the VirtualBox
5200 // lock to copy the list of callbacks to invoke
5201 Bstr error;
5202 Bstr bstrValue(aValue);
5203
5204 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5205 {
5206 const char *sep = error.isEmpty() ? "" : ": ";
5207 CBSTR err = error.raw();
5208 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5209 sep, err));
5210 return setError(E_ACCESSDENIED,
5211 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5212 aKey,
5213 bstrValue.raw(),
5214 sep,
5215 err);
5216 }
5217
5218 // data is changing and change not vetoed: then write it out under the lock
5219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5220
5221 if (isSnapshotMachine())
5222 {
5223 HRESULT rc = checkStateDependency(MutableStateDep);
5224 if (FAILED(rc)) return rc;
5225 }
5226
5227 if (strValue.isEmpty())
5228 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5229 else
5230 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5231 // creates a new key if needed
5232
5233 bool fNeedsGlobalSaveSettings = false;
5234 saveSettings(&fNeedsGlobalSaveSettings);
5235
5236 if (fNeedsGlobalSaveSettings)
5237 {
5238 // save the global settings; for that we should hold only the VirtualBox lock
5239 alock.release();
5240 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5241 mParent->saveSettings();
5242 }
5243 }
5244
5245 // fire notification outside the lock
5246 if (fChanged)
5247 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5248
5249 return S_OK;
5250}
5251
5252STDMETHODIMP Machine::SaveSettings()
5253{
5254 AutoCaller autoCaller(this);
5255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5256
5257 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5258
5259 /* when there was auto-conversion, we want to save the file even if
5260 * the VM is saved */
5261 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5262 if (FAILED(rc)) return rc;
5263
5264 /* the settings file path may never be null */
5265 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5266
5267 /* save all VM data excluding snapshots */
5268 bool fNeedsGlobalSaveSettings = false;
5269 rc = saveSettings(&fNeedsGlobalSaveSettings);
5270 mlock.release();
5271
5272 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5273 {
5274 // save the global settings; for that we should hold only the VirtualBox lock
5275 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5276 rc = mParent->saveSettings();
5277 }
5278
5279 return rc;
5280}
5281
5282STDMETHODIMP Machine::DiscardSettings()
5283{
5284 AutoCaller autoCaller(this);
5285 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5286
5287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5288
5289 HRESULT rc = checkStateDependency(MutableStateDep);
5290 if (FAILED(rc)) return rc;
5291
5292 /*
5293 * during this rollback, the session will be notified if data has
5294 * been actually changed
5295 */
5296 rollback(true /* aNotify */);
5297
5298 return S_OK;
5299}
5300
5301/** @note Locks objects! */
5302STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5303 ComSafeArrayOut(IMedium*, aMedia))
5304{
5305 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5306 AutoLimitedCaller autoCaller(this);
5307 AssertComRCReturnRC(autoCaller.rc());
5308
5309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5310
5311 Guid id(getId());
5312
5313 if (mData->mSession.mState != SessionState_Unlocked)
5314 return setError(VBOX_E_INVALID_OBJECT_STATE,
5315 tr("Cannot unregister the machine '%s' while it is locked"),
5316 mUserData->s.strName.c_str());
5317
5318 // wait for state dependents to drop to zero
5319 ensureNoStateDependencies();
5320
5321 if (!mData->mAccessible)
5322 {
5323 // inaccessible maschines can only be unregistered; uninitialize ourselves
5324 // here because currently there may be no unregistered that are inaccessible
5325 // (this state combination is not supported). Note releasing the caller and
5326 // leaving the lock before calling uninit()
5327 alock.release();
5328 autoCaller.release();
5329
5330 uninit();
5331
5332 mParent->unregisterMachine(this, id);
5333 // calls VirtualBox::saveSettings()
5334
5335 return S_OK;
5336 }
5337
5338 HRESULT rc = S_OK;
5339
5340 // discard saved state
5341 if (mData->mMachineState == MachineState_Saved)
5342 {
5343 // add the saved state file to the list of files the caller should delete
5344 Assert(!mSSData->strStateFilePath.isEmpty());
5345 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5346
5347 mSSData->strStateFilePath.setNull();
5348
5349 // unconditionally set the machine state to powered off, we now
5350 // know no session has locked the machine
5351 mData->mMachineState = MachineState_PoweredOff;
5352 }
5353
5354 size_t cSnapshots = 0;
5355 if (mData->mFirstSnapshot)
5356 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5357 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5358 // fail now before we start detaching media
5359 return setError(VBOX_E_INVALID_OBJECT_STATE,
5360 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5361 mUserData->s.strName.c_str(), cSnapshots);
5362
5363 // This list collects the medium objects from all medium attachments
5364 // which we will detach from the machine and its snapshots, in a specific
5365 // order which allows for closing all media without getting "media in use"
5366 // errors, simply by going through the list from the front to the back:
5367 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5368 // and must be closed before the parent media from the snapshots, or closing the parents
5369 // will fail because they still have children);
5370 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5371 // the root ("first") snapshot of the machine.
5372 MediaList llMedia;
5373
5374 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5375 && mMediaData->mAttachments.size()
5376 )
5377 {
5378 // we have media attachments: detach them all and add the Medium objects to our list
5379 if (cleanupMode != CleanupMode_UnregisterOnly)
5380 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5381 else
5382 return setError(VBOX_E_INVALID_OBJECT_STATE,
5383 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5384 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5385 }
5386
5387 if (cSnapshots)
5388 {
5389 // autoCleanup must be true here, or we would have failed above
5390
5391 // add the media from the medium attachments of the snapshots to llMedia
5392 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5393 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5394 // into the children first
5395
5396 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5397 MachineState_T oldState = mData->mMachineState;
5398 mData->mMachineState = MachineState_DeletingSnapshot;
5399
5400 // make a copy of the first snapshot so the refcount does not drop to 0
5401 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5402 // because of the AutoCaller voodoo)
5403 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5404
5405 // GO!
5406 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5407
5408 mData->mMachineState = oldState;
5409 }
5410
5411 if (FAILED(rc))
5412 {
5413 rollbackMedia();
5414 return rc;
5415 }
5416
5417 // commit all the media changes made above
5418 commitMedia();
5419
5420 mData->mRegistered = false;
5421
5422 // machine lock no longer needed
5423 alock.release();
5424
5425 // return media to caller
5426 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5427 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5428
5429 mParent->unregisterMachine(this, id);
5430 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5431
5432 return S_OK;
5433}
5434
5435struct Machine::DeleteTask
5436{
5437 ComObjPtr<Machine> pMachine;
5438 RTCList<ComPtr<IMedium> > llMediums;
5439 StringsList llFilesToDelete;
5440 ComObjPtr<Progress> pProgress;
5441};
5442
5443STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5444{
5445 LogFlowFuncEnter();
5446
5447 AutoCaller autoCaller(this);
5448 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5449
5450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5451
5452 HRESULT rc = checkStateDependency(MutableStateDep);
5453 if (FAILED(rc)) return rc;
5454
5455 if (mData->mRegistered)
5456 return setError(VBOX_E_INVALID_VM_STATE,
5457 tr("Cannot delete settings of a registered machine"));
5458
5459 DeleteTask *pTask = new DeleteTask;
5460 pTask->pMachine = this;
5461 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5462
5463 // collect files to delete
5464 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5465
5466 for (size_t i = 0; i < sfaMedia.size(); ++i)
5467 {
5468 IMedium *pIMedium(sfaMedia[i]);
5469 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5470 if (pMedium.isNull())
5471 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5472 SafeArray<BSTR> ids;
5473 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5474 if (FAILED(rc)) return rc;
5475 /* At this point the medium should not have any back references
5476 * anymore. If it has it is attached to another VM and *must* not
5477 * deleted. */
5478 if (ids.size() < 1)
5479 pTask->llMediums.append(pMedium);
5480 }
5481 if (mData->pMachineConfigFile->fileExists())
5482 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5483
5484 pTask->pProgress.createObject();
5485 pTask->pProgress->init(getVirtualBox(),
5486 static_cast<IMachine*>(this) /* aInitiator */,
5487 Bstr(tr("Deleting files")).raw(),
5488 true /* fCancellable */,
5489 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5490 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5491
5492 int vrc = RTThreadCreate(NULL,
5493 Machine::deleteThread,
5494 (void*)pTask,
5495 0,
5496 RTTHREADTYPE_MAIN_WORKER,
5497 0,
5498 "MachineDelete");
5499
5500 pTask->pProgress.queryInterfaceTo(aProgress);
5501
5502 if (RT_FAILURE(vrc))
5503 {
5504 delete pTask;
5505 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5506 }
5507
5508 LogFlowFuncLeave();
5509
5510 return S_OK;
5511}
5512
5513/**
5514 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5515 * calls Machine::deleteTaskWorker() on the actual machine object.
5516 * @param Thread
5517 * @param pvUser
5518 * @return
5519 */
5520/*static*/
5521DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5522{
5523 LogFlowFuncEnter();
5524
5525 DeleteTask *pTask = (DeleteTask*)pvUser;
5526 Assert(pTask);
5527 Assert(pTask->pMachine);
5528 Assert(pTask->pProgress);
5529
5530 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5531 pTask->pProgress->notifyComplete(rc);
5532
5533 delete pTask;
5534
5535 LogFlowFuncLeave();
5536
5537 NOREF(Thread);
5538
5539 return VINF_SUCCESS;
5540}
5541
5542/**
5543 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5544 * @param task
5545 * @return
5546 */
5547HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5548{
5549 AutoCaller autoCaller(this);
5550 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5551
5552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5553
5554 HRESULT rc = S_OK;
5555
5556 try
5557 {
5558 ULONG uLogHistoryCount = 3;
5559 ComPtr<ISystemProperties> systemProperties;
5560 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5561 if (FAILED(rc)) throw rc;
5562
5563 if (!systemProperties.isNull())
5564 {
5565 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5566 if (FAILED(rc)) throw rc;
5567 }
5568
5569 MachineState_T oldState = mData->mMachineState;
5570 setMachineState(MachineState_SettingUp);
5571 alock.release();
5572 for (size_t i = 0; i < task.llMediums.size(); ++i)
5573 {
5574 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5575 {
5576 AutoCaller mac(pMedium);
5577 if (FAILED(mac.rc())) throw mac.rc();
5578 Utf8Str strLocation = pMedium->getLocationFull();
5579 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5580 if (FAILED(rc)) throw rc;
5581 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5582 }
5583 ComPtr<IProgress> pProgress2;
5584 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5585 if (FAILED(rc)) throw rc;
5586 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5587 if (FAILED(rc)) throw rc;
5588 /* Check the result of the asynchrony process. */
5589 LONG iRc;
5590 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5591 if (FAILED(rc)) throw rc;
5592 /* If the thread of the progress object has an error, then
5593 * retrieve the error info from there, or it'll be lost. */
5594 if (FAILED(iRc))
5595 throw setError(ProgressErrorInfo(pProgress2));
5596 }
5597 setMachineState(oldState);
5598 alock.acquire();
5599
5600 // delete the files pushed on the task list by Machine::Delete()
5601 // (this includes saved states of the machine and snapshots and
5602 // medium storage files from the IMedium list passed in, and the
5603 // machine XML file)
5604 StringsList::const_iterator it = task.llFilesToDelete.begin();
5605 while (it != task.llFilesToDelete.end())
5606 {
5607 const Utf8Str &strFile = *it;
5608 LogFunc(("Deleting file %s\n", strFile.c_str()));
5609 int vrc = RTFileDelete(strFile.c_str());
5610 if (RT_FAILURE(vrc))
5611 throw setError(VBOX_E_IPRT_ERROR,
5612 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5613
5614 ++it;
5615 if (it == task.llFilesToDelete.end())
5616 {
5617 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5618 if (FAILED(rc)) throw rc;
5619 break;
5620 }
5621
5622 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5623 if (FAILED(rc)) throw rc;
5624 }
5625
5626 /* delete the settings only when the file actually exists */
5627 if (mData->pMachineConfigFile->fileExists())
5628 {
5629 /* Delete any backup or uncommitted XML files. Ignore failures.
5630 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5631 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5632 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5633 RTFileDelete(otherXml.c_str());
5634 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5635 RTFileDelete(otherXml.c_str());
5636
5637 /* delete the Logs folder, nothing important should be left
5638 * there (we don't check for errors because the user might have
5639 * some private files there that we don't want to delete) */
5640 Utf8Str logFolder;
5641 getLogFolder(logFolder);
5642 Assert(logFolder.length());
5643 if (RTDirExists(logFolder.c_str()))
5644 {
5645 /* Delete all VBox.log[.N] files from the Logs folder
5646 * (this must be in sync with the rotation logic in
5647 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5648 * files that may have been created by the GUI. */
5649 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5650 logFolder.c_str(), RTPATH_DELIMITER);
5651 RTFileDelete(log.c_str());
5652 log = Utf8StrFmt("%s%cVBox.png",
5653 logFolder.c_str(), RTPATH_DELIMITER);
5654 RTFileDelete(log.c_str());
5655 for (int i = uLogHistoryCount; i > 0; i--)
5656 {
5657 log = Utf8StrFmt("%s%cVBox.log.%d",
5658 logFolder.c_str(), RTPATH_DELIMITER, i);
5659 RTFileDelete(log.c_str());
5660 log = Utf8StrFmt("%s%cVBox.png.%d",
5661 logFolder.c_str(), RTPATH_DELIMITER, i);
5662 RTFileDelete(log.c_str());
5663 }
5664
5665 RTDirRemove(logFolder.c_str());
5666 }
5667
5668 /* delete the Snapshots folder, nothing important should be left
5669 * there (we don't check for errors because the user might have
5670 * some private files there that we don't want to delete) */
5671 Utf8Str strFullSnapshotFolder;
5672 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5673 Assert(!strFullSnapshotFolder.isEmpty());
5674 if (RTDirExists(strFullSnapshotFolder.c_str()))
5675 RTDirRemove(strFullSnapshotFolder.c_str());
5676
5677 // delete the directory that contains the settings file, but only
5678 // if it matches the VM name
5679 Utf8Str settingsDir;
5680 if (isInOwnDir(&settingsDir))
5681 RTDirRemove(settingsDir.c_str());
5682 }
5683
5684 alock.release();
5685
5686 mParent->saveModifiedRegistries();
5687 }
5688 catch (HRESULT aRC) { rc = aRC; }
5689
5690 return rc;
5691}
5692
5693STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5694{
5695 CheckComArgOutPointerValid(aSnapshot);
5696
5697 AutoCaller autoCaller(this);
5698 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5699
5700 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5701
5702 ComObjPtr<Snapshot> pSnapshot;
5703 HRESULT rc;
5704
5705 if (!aNameOrId || !*aNameOrId)
5706 // null case (caller wants root snapshot): findSnapshotById() handles this
5707 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5708 else
5709 {
5710 Guid uuid(aNameOrId);
5711 if (uuid.isValid())
5712 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5713 else
5714 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5715 }
5716 pSnapshot.queryInterfaceTo(aSnapshot);
5717
5718 return rc;
5719}
5720
5721STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5722{
5723 CheckComArgStrNotEmptyOrNull(aName);
5724 CheckComArgStrNotEmptyOrNull(aHostPath);
5725
5726 AutoCaller autoCaller(this);
5727 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5728
5729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5730
5731 HRESULT rc = checkStateDependency(MutableStateDep);
5732 if (FAILED(rc)) return rc;
5733
5734 Utf8Str strName(aName);
5735
5736 ComObjPtr<SharedFolder> sharedFolder;
5737 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5738 if (SUCCEEDED(rc))
5739 return setError(VBOX_E_OBJECT_IN_USE,
5740 tr("Shared folder named '%s' already exists"),
5741 strName.c_str());
5742
5743 sharedFolder.createObject();
5744 rc = sharedFolder->init(getMachine(),
5745 strName,
5746 aHostPath,
5747 !!aWritable,
5748 !!aAutoMount,
5749 true /* fFailOnError */);
5750 if (FAILED(rc)) return rc;
5751
5752 setModified(IsModified_SharedFolders);
5753 mHWData.backup();
5754 mHWData->mSharedFolders.push_back(sharedFolder);
5755
5756 /* inform the direct session if any */
5757 alock.release();
5758 onSharedFolderChange();
5759
5760 return S_OK;
5761}
5762
5763STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5764{
5765 CheckComArgStrNotEmptyOrNull(aName);
5766
5767 AutoCaller autoCaller(this);
5768 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5769
5770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5771
5772 HRESULT rc = checkStateDependency(MutableStateDep);
5773 if (FAILED(rc)) return rc;
5774
5775 ComObjPtr<SharedFolder> sharedFolder;
5776 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5777 if (FAILED(rc)) return rc;
5778
5779 setModified(IsModified_SharedFolders);
5780 mHWData.backup();
5781 mHWData->mSharedFolders.remove(sharedFolder);
5782
5783 /* inform the direct session if any */
5784 alock.release();
5785 onSharedFolderChange();
5786
5787 return S_OK;
5788}
5789
5790STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5791{
5792 CheckComArgOutPointerValid(aCanShow);
5793
5794 /* start with No */
5795 *aCanShow = FALSE;
5796
5797 AutoCaller autoCaller(this);
5798 AssertComRCReturnRC(autoCaller.rc());
5799
5800 ComPtr<IInternalSessionControl> directControl;
5801 {
5802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5803
5804 if (mData->mSession.mState != SessionState_Locked)
5805 return setError(VBOX_E_INVALID_VM_STATE,
5806 tr("Machine is not locked for session (session state: %s)"),
5807 Global::stringifySessionState(mData->mSession.mState));
5808
5809 directControl = mData->mSession.mDirectControl;
5810 }
5811
5812 /* ignore calls made after #OnSessionEnd() is called */
5813 if (!directControl)
5814 return S_OK;
5815
5816 LONG64 dummy;
5817 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5818}
5819
5820STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5821{
5822 CheckComArgOutPointerValid(aWinId);
5823
5824 AutoCaller autoCaller(this);
5825 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5826
5827 ComPtr<IInternalSessionControl> directControl;
5828 {
5829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5830
5831 if (mData->mSession.mState != SessionState_Locked)
5832 return setError(E_FAIL,
5833 tr("Machine is not locked for session (session state: %s)"),
5834 Global::stringifySessionState(mData->mSession.mState));
5835
5836 directControl = mData->mSession.mDirectControl;
5837 }
5838
5839 /* ignore calls made after #OnSessionEnd() is called */
5840 if (!directControl)
5841 return S_OK;
5842
5843 BOOL dummy;
5844 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5845}
5846
5847#ifdef VBOX_WITH_GUEST_PROPS
5848/**
5849 * Look up a guest property in VBoxSVC's internal structures.
5850 */
5851HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5852 BSTR *aValue,
5853 LONG64 *aTimestamp,
5854 BSTR *aFlags) const
5855{
5856 using namespace guestProp;
5857
5858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5859 Utf8Str strName(aName);
5860 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5861
5862 if (it != mHWData->mGuestProperties.end())
5863 {
5864 char szFlags[MAX_FLAGS_LEN + 1];
5865 it->second.strValue.cloneTo(aValue);
5866 *aTimestamp = it->second.mTimestamp;
5867 writeFlags(it->second.mFlags, szFlags);
5868 Bstr(szFlags).cloneTo(aFlags);
5869 }
5870
5871 return S_OK;
5872}
5873
5874/**
5875 * Query the VM that a guest property belongs to for the property.
5876 * @returns E_ACCESSDENIED if the VM process is not available or not
5877 * currently handling queries and the lookup should then be done in
5878 * VBoxSVC.
5879 */
5880HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5881 BSTR *aValue,
5882 LONG64 *aTimestamp,
5883 BSTR *aFlags) const
5884{
5885 HRESULT rc;
5886 ComPtr<IInternalSessionControl> directControl;
5887 directControl = mData->mSession.mDirectControl;
5888
5889 /* fail if we were called after #OnSessionEnd() is called. This is a
5890 * silly race condition. */
5891
5892 if (!directControl)
5893 rc = E_ACCESSDENIED;
5894 else
5895 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5896 false /* isSetter */,
5897 aValue, aTimestamp, aFlags);
5898 return rc;
5899}
5900#endif // VBOX_WITH_GUEST_PROPS
5901
5902STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5903 BSTR *aValue,
5904 LONG64 *aTimestamp,
5905 BSTR *aFlags)
5906{
5907#ifndef VBOX_WITH_GUEST_PROPS
5908 ReturnComNotImplemented();
5909#else // VBOX_WITH_GUEST_PROPS
5910 CheckComArgStrNotEmptyOrNull(aName);
5911 CheckComArgOutPointerValid(aValue);
5912 CheckComArgOutPointerValid(aTimestamp);
5913 CheckComArgOutPointerValid(aFlags);
5914
5915 AutoCaller autoCaller(this);
5916 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5917
5918 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5919 if (rc == E_ACCESSDENIED)
5920 /* The VM is not running or the service is not (yet) accessible */
5921 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5922 return rc;
5923#endif // VBOX_WITH_GUEST_PROPS
5924}
5925
5926STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5927{
5928 LONG64 dummyTimestamp;
5929 Bstr dummyFlags;
5930 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5931}
5932
5933STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5934{
5935 Bstr dummyValue;
5936 Bstr dummyFlags;
5937 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5938}
5939
5940#ifdef VBOX_WITH_GUEST_PROPS
5941/**
5942 * Set a guest property in VBoxSVC's internal structures.
5943 */
5944HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5945 IN_BSTR aFlags)
5946{
5947 using namespace guestProp;
5948
5949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5950 HRESULT rc = S_OK;
5951
5952 rc = checkStateDependency(MutableStateDep);
5953 if (FAILED(rc)) return rc;
5954
5955 try
5956 {
5957 Utf8Str utf8Name(aName);
5958 Utf8Str utf8Flags(aFlags);
5959 uint32_t fFlags = NILFLAG;
5960 if ( aFlags != NULL
5961 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
5962 return setError(E_INVALIDARG,
5963 tr("Invalid guest property flag values: '%ls'"),
5964 aFlags);
5965
5966 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
5967 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
5968 if (it == mHWData->mGuestProperties.end())
5969 {
5970 if (!fDelete)
5971 {
5972 setModified(IsModified_MachineData);
5973 mHWData.backupEx();
5974
5975 RTTIMESPEC time;
5976 HWData::GuestProperty prop;
5977 prop.strValue = aValue;
5978 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5979 prop.mFlags = fFlags;
5980 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5981 }
5982 }
5983 else
5984 {
5985 if (it->second.mFlags & (RDONLYHOST))
5986 {
5987 rc = setError(E_ACCESSDENIED,
5988 tr("The property '%ls' cannot be changed by the host"),
5989 aName);
5990 }
5991 else
5992 {
5993 setModified(IsModified_MachineData);
5994 mHWData.backupEx();
5995
5996 /* The backupEx() operation invalidates our iterator,
5997 * so get a new one. */
5998 it = mHWData->mGuestProperties.find(utf8Name);
5999 Assert(it != mHWData->mGuestProperties.end());
6000
6001 if (!fDelete)
6002 {
6003 RTTIMESPEC time;
6004 it->second.strValue = aValue;
6005 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6006 it->second.mFlags = fFlags;
6007 }
6008 else
6009 mHWData->mGuestProperties.erase(it);
6010 }
6011 }
6012
6013 if ( SUCCEEDED(rc)
6014 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6015 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6016 RTSTR_MAX,
6017 utf8Name.c_str(),
6018 RTSTR_MAX,
6019 NULL)
6020 )
6021 )
6022 {
6023 alock.release();
6024
6025 mParent->onGuestPropertyChange(mData->mUuid, aName,
6026 aValue ? aValue : Bstr("").raw(),
6027 aFlags ? aFlags : Bstr("").raw());
6028 }
6029 }
6030 catch (std::bad_alloc &)
6031 {
6032 rc = E_OUTOFMEMORY;
6033 }
6034
6035 return rc;
6036}
6037
6038/**
6039 * Set a property on the VM that that property belongs to.
6040 * @returns E_ACCESSDENIED if the VM process is not available or not
6041 * currently handling queries and the setting should then be done in
6042 * VBoxSVC.
6043 */
6044HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6045 IN_BSTR aFlags)
6046{
6047 HRESULT rc;
6048
6049 try
6050 {
6051 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6052
6053 BSTR dummy = NULL; /* will not be changed (setter) */
6054 LONG64 dummy64;
6055 if (!directControl)
6056 rc = E_ACCESSDENIED;
6057 else
6058 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6059 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6060 true /* isSetter */,
6061 &dummy, &dummy64, &dummy);
6062 }
6063 catch (std::bad_alloc &)
6064 {
6065 rc = E_OUTOFMEMORY;
6066 }
6067
6068 return rc;
6069}
6070#endif // VBOX_WITH_GUEST_PROPS
6071
6072STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6073 IN_BSTR aFlags)
6074{
6075#ifndef VBOX_WITH_GUEST_PROPS
6076 ReturnComNotImplemented();
6077#else // VBOX_WITH_GUEST_PROPS
6078 CheckComArgStrNotEmptyOrNull(aName);
6079 CheckComArgMaybeNull(aFlags);
6080 CheckComArgMaybeNull(aValue);
6081
6082 AutoCaller autoCaller(this);
6083 if (FAILED(autoCaller.rc()))
6084 return autoCaller.rc();
6085
6086 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6087 if (rc == E_ACCESSDENIED)
6088 /* The VM is not running or the service is not (yet) accessible */
6089 rc = setGuestPropertyToService(aName, aValue, aFlags);
6090 return rc;
6091#endif // VBOX_WITH_GUEST_PROPS
6092}
6093
6094STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6095{
6096 return SetGuestProperty(aName, aValue, NULL);
6097}
6098
6099STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6100{
6101 return SetGuestProperty(aName, NULL, NULL);
6102}
6103
6104#ifdef VBOX_WITH_GUEST_PROPS
6105/**
6106 * Enumerate the guest properties in VBoxSVC's internal structures.
6107 */
6108HRESULT Machine::enumerateGuestPropertiesInService
6109 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6110 ComSafeArrayOut(BSTR, aValues),
6111 ComSafeArrayOut(LONG64, aTimestamps),
6112 ComSafeArrayOut(BSTR, aFlags))
6113{
6114 using namespace guestProp;
6115
6116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6117 Utf8Str strPatterns(aPatterns);
6118
6119 HWData::GuestPropertyMap propMap;
6120
6121 /*
6122 * Look for matching patterns and build up a list.
6123 */
6124 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6125 while (it != mHWData->mGuestProperties.end())
6126 {
6127 if ( strPatterns.isEmpty()
6128 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6129 RTSTR_MAX,
6130 it->first.c_str(),
6131 RTSTR_MAX,
6132 NULL)
6133 )
6134 {
6135 propMap.insert(*it);
6136 }
6137
6138 it++;
6139 }
6140
6141 alock.release();
6142
6143 /*
6144 * And build up the arrays for returning the property information.
6145 */
6146 size_t cEntries = propMap.size();
6147 SafeArray<BSTR> names(cEntries);
6148 SafeArray<BSTR> values(cEntries);
6149 SafeArray<LONG64> timestamps(cEntries);
6150 SafeArray<BSTR> flags(cEntries);
6151 size_t iProp = 0;
6152
6153 it = propMap.begin();
6154 while (it != propMap.end())
6155 {
6156 char szFlags[MAX_FLAGS_LEN + 1];
6157 it->first.cloneTo(&names[iProp]);
6158 it->second.strValue.cloneTo(&values[iProp]);
6159 timestamps[iProp] = it->second.mTimestamp;
6160 writeFlags(it->second.mFlags, szFlags);
6161 Bstr(szFlags).cloneTo(&flags[iProp++]);
6162 it++;
6163 }
6164 names.detachTo(ComSafeArrayOutArg(aNames));
6165 values.detachTo(ComSafeArrayOutArg(aValues));
6166 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6167 flags.detachTo(ComSafeArrayOutArg(aFlags));
6168 return S_OK;
6169}
6170
6171/**
6172 * Enumerate the properties managed by a VM.
6173 * @returns E_ACCESSDENIED if the VM process is not available or not
6174 * currently handling queries and the setting should then be done in
6175 * VBoxSVC.
6176 */
6177HRESULT Machine::enumerateGuestPropertiesOnVM
6178 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6179 ComSafeArrayOut(BSTR, aValues),
6180 ComSafeArrayOut(LONG64, aTimestamps),
6181 ComSafeArrayOut(BSTR, aFlags))
6182{
6183 HRESULT rc;
6184 ComPtr<IInternalSessionControl> directControl;
6185 directControl = mData->mSession.mDirectControl;
6186
6187 if (!directControl)
6188 rc = E_ACCESSDENIED;
6189 else
6190 rc = directControl->EnumerateGuestProperties
6191 (aPatterns, ComSafeArrayOutArg(aNames),
6192 ComSafeArrayOutArg(aValues),
6193 ComSafeArrayOutArg(aTimestamps),
6194 ComSafeArrayOutArg(aFlags));
6195 return rc;
6196}
6197#endif // VBOX_WITH_GUEST_PROPS
6198
6199STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6200 ComSafeArrayOut(BSTR, aNames),
6201 ComSafeArrayOut(BSTR, aValues),
6202 ComSafeArrayOut(LONG64, aTimestamps),
6203 ComSafeArrayOut(BSTR, aFlags))
6204{
6205#ifndef VBOX_WITH_GUEST_PROPS
6206 ReturnComNotImplemented();
6207#else // VBOX_WITH_GUEST_PROPS
6208 CheckComArgMaybeNull(aPatterns);
6209 CheckComArgOutSafeArrayPointerValid(aNames);
6210 CheckComArgOutSafeArrayPointerValid(aValues);
6211 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6212 CheckComArgOutSafeArrayPointerValid(aFlags);
6213
6214 AutoCaller autoCaller(this);
6215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6216
6217 HRESULT rc = enumerateGuestPropertiesOnVM
6218 (aPatterns, ComSafeArrayOutArg(aNames),
6219 ComSafeArrayOutArg(aValues),
6220 ComSafeArrayOutArg(aTimestamps),
6221 ComSafeArrayOutArg(aFlags));
6222 if (rc == E_ACCESSDENIED)
6223 /* The VM is not running or the service is not (yet) accessible */
6224 rc = enumerateGuestPropertiesInService
6225 (aPatterns, ComSafeArrayOutArg(aNames),
6226 ComSafeArrayOutArg(aValues),
6227 ComSafeArrayOutArg(aTimestamps),
6228 ComSafeArrayOutArg(aFlags));
6229 return rc;
6230#endif // VBOX_WITH_GUEST_PROPS
6231}
6232
6233STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6234 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6235{
6236 MediaData::AttachmentList atts;
6237
6238 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6239 if (FAILED(rc)) return rc;
6240
6241 SafeIfaceArray<IMediumAttachment> attachments(atts);
6242 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6243
6244 return S_OK;
6245}
6246
6247STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6248 LONG aControllerPort,
6249 LONG aDevice,
6250 IMediumAttachment **aAttachment)
6251{
6252 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6253 aControllerName, aControllerPort, aDevice));
6254
6255 CheckComArgStrNotEmptyOrNull(aControllerName);
6256 CheckComArgOutPointerValid(aAttachment);
6257
6258 AutoCaller autoCaller(this);
6259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6260
6261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6262
6263 *aAttachment = NULL;
6264
6265 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6266 aControllerName,
6267 aControllerPort,
6268 aDevice);
6269 if (pAttach.isNull())
6270 return setError(VBOX_E_OBJECT_NOT_FOUND,
6271 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6272 aDevice, aControllerPort, aControllerName);
6273
6274 pAttach.queryInterfaceTo(aAttachment);
6275
6276 return S_OK;
6277}
6278
6279STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6280 StorageBus_T aConnectionType,
6281 IStorageController **controller)
6282{
6283 CheckComArgStrNotEmptyOrNull(aName);
6284
6285 if ( (aConnectionType <= StorageBus_Null)
6286 || (aConnectionType > StorageBus_SAS))
6287 return setError(E_INVALIDARG,
6288 tr("Invalid connection type: %d"),
6289 aConnectionType);
6290
6291 AutoCaller autoCaller(this);
6292 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6293
6294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6295
6296 HRESULT rc = checkStateDependency(MutableStateDep);
6297 if (FAILED(rc)) return rc;
6298
6299 /* try to find one with the name first. */
6300 ComObjPtr<StorageController> ctrl;
6301
6302 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6303 if (SUCCEEDED(rc))
6304 return setError(VBOX_E_OBJECT_IN_USE,
6305 tr("Storage controller named '%ls' already exists"),
6306 aName);
6307
6308 ctrl.createObject();
6309
6310 /* get a new instance number for the storage controller */
6311 ULONG ulInstance = 0;
6312 bool fBootable = true;
6313 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6314 it != mStorageControllers->end();
6315 ++it)
6316 {
6317 if ((*it)->getStorageBus() == aConnectionType)
6318 {
6319 ULONG ulCurInst = (*it)->getInstance();
6320
6321 if (ulCurInst >= ulInstance)
6322 ulInstance = ulCurInst + 1;
6323
6324 /* Only one controller of each type can be marked as bootable. */
6325 if ((*it)->getBootable())
6326 fBootable = false;
6327 }
6328 }
6329
6330 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6331 if (FAILED(rc)) return rc;
6332
6333 setModified(IsModified_Storage);
6334 mStorageControllers.backup();
6335 mStorageControllers->push_back(ctrl);
6336
6337 ctrl.queryInterfaceTo(controller);
6338
6339 /* inform the direct session if any */
6340 alock.release();
6341 onStorageControllerChange();
6342
6343 return S_OK;
6344}
6345
6346STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6347 IStorageController **aStorageController)
6348{
6349 CheckComArgStrNotEmptyOrNull(aName);
6350
6351 AutoCaller autoCaller(this);
6352 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6353
6354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6355
6356 ComObjPtr<StorageController> ctrl;
6357
6358 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6359 if (SUCCEEDED(rc))
6360 ctrl.queryInterfaceTo(aStorageController);
6361
6362 return rc;
6363}
6364
6365STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6366 IStorageController **aStorageController)
6367{
6368 AutoCaller autoCaller(this);
6369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6370
6371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6372
6373 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6374 it != mStorageControllers->end();
6375 ++it)
6376 {
6377 if ((*it)->getInstance() == aInstance)
6378 {
6379 (*it).queryInterfaceTo(aStorageController);
6380 return S_OK;
6381 }
6382 }
6383
6384 return setError(VBOX_E_OBJECT_NOT_FOUND,
6385 tr("Could not find a storage controller with instance number '%lu'"),
6386 aInstance);
6387}
6388
6389STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6390{
6391 AutoCaller autoCaller(this);
6392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6393
6394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6395
6396 HRESULT rc = checkStateDependency(MutableStateDep);
6397 if (FAILED(rc)) return rc;
6398
6399 ComObjPtr<StorageController> ctrl;
6400
6401 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6402 if (SUCCEEDED(rc))
6403 {
6404 /* Ensure that only one controller of each type is marked as bootable. */
6405 if (fBootable == TRUE)
6406 {
6407 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6408 it != mStorageControllers->end();
6409 ++it)
6410 {
6411 ComObjPtr<StorageController> aCtrl = (*it);
6412
6413 if ( (aCtrl->getName() != Utf8Str(aName))
6414 && aCtrl->getBootable() == TRUE
6415 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6416 && aCtrl->getControllerType() == ctrl->getControllerType())
6417 {
6418 aCtrl->setBootable(FALSE);
6419 break;
6420 }
6421 }
6422 }
6423
6424 if (SUCCEEDED(rc))
6425 {
6426 ctrl->setBootable(fBootable);
6427 setModified(IsModified_Storage);
6428 }
6429 }
6430
6431 if (SUCCEEDED(rc))
6432 {
6433 /* inform the direct session if any */
6434 alock.release();
6435 onStorageControllerChange();
6436 }
6437
6438 return rc;
6439}
6440
6441STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6442{
6443 CheckComArgStrNotEmptyOrNull(aName);
6444
6445 AutoCaller autoCaller(this);
6446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6447
6448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6449
6450 HRESULT rc = checkStateDependency(MutableStateDep);
6451 if (FAILED(rc)) return rc;
6452
6453 ComObjPtr<StorageController> ctrl;
6454 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6455 if (FAILED(rc)) return rc;
6456
6457 {
6458 /* find all attached devices to the appropriate storage controller and detach them all */
6459 // make a temporary list because detachDevice invalidates iterators into
6460 // mMediaData->mAttachments
6461 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6462
6463 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6464 it != llAttachments2.end();
6465 ++it)
6466 {
6467 MediumAttachment *pAttachTemp = *it;
6468
6469 AutoCaller localAutoCaller(pAttachTemp);
6470 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6471
6472 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6473
6474 if (pAttachTemp->getControllerName() == aName)
6475 {
6476 rc = detachDevice(pAttachTemp, alock, NULL);
6477 if (FAILED(rc)) return rc;
6478 }
6479 }
6480 }
6481
6482 /* We can remove it now. */
6483 setModified(IsModified_Storage);
6484 mStorageControllers.backup();
6485
6486 ctrl->unshare();
6487
6488 mStorageControllers->remove(ctrl);
6489
6490 /* inform the direct session if any */
6491 alock.release();
6492 onStorageControllerChange();
6493
6494 return S_OK;
6495}
6496
6497STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6498 ULONG *puOriginX,
6499 ULONG *puOriginY,
6500 ULONG *puWidth,
6501 ULONG *puHeight,
6502 BOOL *pfEnabled)
6503{
6504 LogFlowThisFunc(("\n"));
6505
6506 CheckComArgNotNull(puOriginX);
6507 CheckComArgNotNull(puOriginY);
6508 CheckComArgNotNull(puWidth);
6509 CheckComArgNotNull(puHeight);
6510 CheckComArgNotNull(pfEnabled);
6511
6512 uint32_t u32OriginX= 0;
6513 uint32_t u32OriginY= 0;
6514 uint32_t u32Width = 0;
6515 uint32_t u32Height = 0;
6516 uint16_t u16Flags = 0;
6517
6518 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6519 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6520 if (RT_FAILURE(vrc))
6521 {
6522#ifdef RT_OS_WINDOWS
6523 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6524 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6525 * So just assign fEnable to TRUE again.
6526 * The right fix would be to change GUI API wrappers to make sure that parameters
6527 * are changed only if API succeeds.
6528 */
6529 *pfEnabled = TRUE;
6530#endif
6531 return setError(VBOX_E_IPRT_ERROR,
6532 tr("Saved guest size is not available (%Rrc)"),
6533 vrc);
6534 }
6535
6536 *puOriginX = u32OriginX;
6537 *puOriginY = u32OriginY;
6538 *puWidth = u32Width;
6539 *puHeight = u32Height;
6540 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6541
6542 return S_OK;
6543}
6544
6545STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6546{
6547 LogFlowThisFunc(("\n"));
6548
6549 CheckComArgNotNull(aSize);
6550 CheckComArgNotNull(aWidth);
6551 CheckComArgNotNull(aHeight);
6552
6553 if (aScreenId != 0)
6554 return E_NOTIMPL;
6555
6556 AutoCaller autoCaller(this);
6557 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6558
6559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6560
6561 uint8_t *pu8Data = NULL;
6562 uint32_t cbData = 0;
6563 uint32_t u32Width = 0;
6564 uint32_t u32Height = 0;
6565
6566 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6567
6568 if (RT_FAILURE(vrc))
6569 return setError(VBOX_E_IPRT_ERROR,
6570 tr("Saved screenshot data is not available (%Rrc)"),
6571 vrc);
6572
6573 *aSize = cbData;
6574 *aWidth = u32Width;
6575 *aHeight = u32Height;
6576
6577 freeSavedDisplayScreenshot(pu8Data);
6578
6579 return S_OK;
6580}
6581
6582STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6583{
6584 LogFlowThisFunc(("\n"));
6585
6586 CheckComArgNotNull(aWidth);
6587 CheckComArgNotNull(aHeight);
6588 CheckComArgOutSafeArrayPointerValid(aData);
6589
6590 if (aScreenId != 0)
6591 return E_NOTIMPL;
6592
6593 AutoCaller autoCaller(this);
6594 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6595
6596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6597
6598 uint8_t *pu8Data = NULL;
6599 uint32_t cbData = 0;
6600 uint32_t u32Width = 0;
6601 uint32_t u32Height = 0;
6602
6603 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6604
6605 if (RT_FAILURE(vrc))
6606 return setError(VBOX_E_IPRT_ERROR,
6607 tr("Saved screenshot data is not available (%Rrc)"),
6608 vrc);
6609
6610 *aWidth = u32Width;
6611 *aHeight = u32Height;
6612
6613 com::SafeArray<BYTE> bitmap(cbData);
6614 /* Convert pixels to format expected by the API caller. */
6615 if (aBGR)
6616 {
6617 /* [0] B, [1] G, [2] R, [3] A. */
6618 for (unsigned i = 0; i < cbData; i += 4)
6619 {
6620 bitmap[i] = pu8Data[i];
6621 bitmap[i + 1] = pu8Data[i + 1];
6622 bitmap[i + 2] = pu8Data[i + 2];
6623 bitmap[i + 3] = 0xff;
6624 }
6625 }
6626 else
6627 {
6628 /* [0] R, [1] G, [2] B, [3] A. */
6629 for (unsigned i = 0; i < cbData; i += 4)
6630 {
6631 bitmap[i] = pu8Data[i + 2];
6632 bitmap[i + 1] = pu8Data[i + 1];
6633 bitmap[i + 2] = pu8Data[i];
6634 bitmap[i + 3] = 0xff;
6635 }
6636 }
6637 bitmap.detachTo(ComSafeArrayOutArg(aData));
6638
6639 freeSavedDisplayScreenshot(pu8Data);
6640
6641 return S_OK;
6642}
6643
6644
6645STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6646{
6647 LogFlowThisFunc(("\n"));
6648
6649 CheckComArgNotNull(aWidth);
6650 CheckComArgNotNull(aHeight);
6651 CheckComArgOutSafeArrayPointerValid(aData);
6652
6653 if (aScreenId != 0)
6654 return E_NOTIMPL;
6655
6656 AutoCaller autoCaller(this);
6657 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6658
6659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6660
6661 uint8_t *pu8Data = NULL;
6662 uint32_t cbData = 0;
6663 uint32_t u32Width = 0;
6664 uint32_t u32Height = 0;
6665
6666 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6667
6668 if (RT_FAILURE(vrc))
6669 return setError(VBOX_E_IPRT_ERROR,
6670 tr("Saved screenshot data is not available (%Rrc)"),
6671 vrc);
6672
6673 *aWidth = u32Width;
6674 *aHeight = u32Height;
6675
6676 HRESULT rc = S_OK;
6677 uint8_t *pu8PNG = NULL;
6678 uint32_t cbPNG = 0;
6679 uint32_t cxPNG = 0;
6680 uint32_t cyPNG = 0;
6681
6682 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6683
6684 if (RT_SUCCESS(vrc))
6685 {
6686 com::SafeArray<BYTE> screenData(cbPNG);
6687 screenData.initFrom(pu8PNG, cbPNG);
6688 if (pu8PNG)
6689 RTMemFree(pu8PNG);
6690 screenData.detachTo(ComSafeArrayOutArg(aData));
6691 }
6692 else
6693 {
6694 if (pu8PNG)
6695 RTMemFree(pu8PNG);
6696 return setError(VBOX_E_IPRT_ERROR,
6697 tr("Could not convert screenshot to PNG (%Rrc)"),
6698 vrc);
6699 }
6700
6701 freeSavedDisplayScreenshot(pu8Data);
6702
6703 return rc;
6704}
6705
6706STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6707{
6708 LogFlowThisFunc(("\n"));
6709
6710 CheckComArgNotNull(aSize);
6711 CheckComArgNotNull(aWidth);
6712 CheckComArgNotNull(aHeight);
6713
6714 if (aScreenId != 0)
6715 return E_NOTIMPL;
6716
6717 AutoCaller autoCaller(this);
6718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6719
6720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6721
6722 uint8_t *pu8Data = NULL;
6723 uint32_t cbData = 0;
6724 uint32_t u32Width = 0;
6725 uint32_t u32Height = 0;
6726
6727 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6728
6729 if (RT_FAILURE(vrc))
6730 return setError(VBOX_E_IPRT_ERROR,
6731 tr("Saved screenshot data is not available (%Rrc)"),
6732 vrc);
6733
6734 *aSize = cbData;
6735 *aWidth = u32Width;
6736 *aHeight = u32Height;
6737
6738 freeSavedDisplayScreenshot(pu8Data);
6739
6740 return S_OK;
6741}
6742
6743STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6744{
6745 LogFlowThisFunc(("\n"));
6746
6747 CheckComArgNotNull(aWidth);
6748 CheckComArgNotNull(aHeight);
6749 CheckComArgOutSafeArrayPointerValid(aData);
6750
6751 if (aScreenId != 0)
6752 return E_NOTIMPL;
6753
6754 AutoCaller autoCaller(this);
6755 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6756
6757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6758
6759 uint8_t *pu8Data = NULL;
6760 uint32_t cbData = 0;
6761 uint32_t u32Width = 0;
6762 uint32_t u32Height = 0;
6763
6764 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6765
6766 if (RT_FAILURE(vrc))
6767 return setError(VBOX_E_IPRT_ERROR,
6768 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6769 vrc);
6770
6771 *aWidth = u32Width;
6772 *aHeight = u32Height;
6773
6774 com::SafeArray<BYTE> png(cbData);
6775 png.initFrom(pu8Data, cbData);
6776 png.detachTo(ComSafeArrayOutArg(aData));
6777
6778 freeSavedDisplayScreenshot(pu8Data);
6779
6780 return S_OK;
6781}
6782
6783STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6784{
6785 HRESULT rc = S_OK;
6786 LogFlowThisFunc(("\n"));
6787
6788 AutoCaller autoCaller(this);
6789 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6790
6791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6792
6793 if (!mHWData->mCPUHotPlugEnabled)
6794 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6795
6796 if (aCpu >= mHWData->mCPUCount)
6797 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6798
6799 if (mHWData->mCPUAttached[aCpu])
6800 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6801
6802 alock.release();
6803 rc = onCPUChange(aCpu, false);
6804 alock.acquire();
6805 if (FAILED(rc)) return rc;
6806
6807 setModified(IsModified_MachineData);
6808 mHWData.backup();
6809 mHWData->mCPUAttached[aCpu] = true;
6810
6811 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6812 if (Global::IsOnline(mData->mMachineState))
6813 saveSettings(NULL);
6814
6815 return S_OK;
6816}
6817
6818STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6819{
6820 HRESULT rc = S_OK;
6821 LogFlowThisFunc(("\n"));
6822
6823 AutoCaller autoCaller(this);
6824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6825
6826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6827
6828 if (!mHWData->mCPUHotPlugEnabled)
6829 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6830
6831 if (aCpu >= SchemaDefs::MaxCPUCount)
6832 return setError(E_INVALIDARG,
6833 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6834 SchemaDefs::MaxCPUCount);
6835
6836 if (!mHWData->mCPUAttached[aCpu])
6837 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6838
6839 /* CPU 0 can't be detached */
6840 if (aCpu == 0)
6841 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6842
6843 alock.release();
6844 rc = onCPUChange(aCpu, true);
6845 alock.acquire();
6846 if (FAILED(rc)) return rc;
6847
6848 setModified(IsModified_MachineData);
6849 mHWData.backup();
6850 mHWData->mCPUAttached[aCpu] = false;
6851
6852 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6853 if (Global::IsOnline(mData->mMachineState))
6854 saveSettings(NULL);
6855
6856 return S_OK;
6857}
6858
6859STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6860{
6861 LogFlowThisFunc(("\n"));
6862
6863 CheckComArgNotNull(aCpuAttached);
6864
6865 *aCpuAttached = false;
6866
6867 AutoCaller autoCaller(this);
6868 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6869
6870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6871
6872 /* If hotplug is enabled the CPU is always enabled. */
6873 if (!mHWData->mCPUHotPlugEnabled)
6874 {
6875 if (aCpu < mHWData->mCPUCount)
6876 *aCpuAttached = true;
6877 }
6878 else
6879 {
6880 if (aCpu < SchemaDefs::MaxCPUCount)
6881 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6882 }
6883
6884 return S_OK;
6885}
6886
6887STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6888{
6889 CheckComArgOutPointerValid(aName);
6890
6891 AutoCaller autoCaller(this);
6892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6893
6894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6895
6896 Utf8Str log = queryLogFilename(aIdx);
6897 if (!RTFileExists(log.c_str()))
6898 log.setNull();
6899 log.cloneTo(aName);
6900
6901 return S_OK;
6902}
6903
6904STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6905{
6906 LogFlowThisFunc(("\n"));
6907 CheckComArgOutSafeArrayPointerValid(aData);
6908 if (aSize < 0)
6909 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6910
6911 AutoCaller autoCaller(this);
6912 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6913
6914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6915
6916 HRESULT rc = S_OK;
6917 Utf8Str log = queryLogFilename(aIdx);
6918
6919 /* do not unnecessarily hold the lock while doing something which does
6920 * not need the lock and potentially takes a long time. */
6921 alock.release();
6922
6923 /* Limit the chunk size to 32K for now, as that gives better performance
6924 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6925 * One byte expands to approx. 25 bytes of breathtaking XML. */
6926 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6927 com::SafeArray<BYTE> logData(cbData);
6928
6929 RTFILE LogFile;
6930 int vrc = RTFileOpen(&LogFile, log.c_str(),
6931 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6932 if (RT_SUCCESS(vrc))
6933 {
6934 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6935 if (RT_SUCCESS(vrc))
6936 logData.resize(cbData);
6937 else
6938 rc = setError(VBOX_E_IPRT_ERROR,
6939 tr("Could not read log file '%s' (%Rrc)"),
6940 log.c_str(), vrc);
6941 RTFileClose(LogFile);
6942 }
6943 else
6944 rc = setError(VBOX_E_IPRT_ERROR,
6945 tr("Could not open log file '%s' (%Rrc)"),
6946 log.c_str(), vrc);
6947
6948 if (FAILED(rc))
6949 logData.resize(0);
6950 logData.detachTo(ComSafeArrayOutArg(aData));
6951
6952 return rc;
6953}
6954
6955
6956/**
6957 * Currently this method doesn't attach device to the running VM,
6958 * just makes sure it's plugged on next VM start.
6959 */
6960STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6961{
6962 AutoCaller autoCaller(this);
6963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6964
6965 // lock scope
6966 {
6967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6968
6969 HRESULT rc = checkStateDependency(MutableStateDep);
6970 if (FAILED(rc)) return rc;
6971
6972 ChipsetType_T aChipset = ChipsetType_PIIX3;
6973 COMGETTER(ChipsetType)(&aChipset);
6974
6975 if (aChipset != ChipsetType_ICH9)
6976 {
6977 return setError(E_INVALIDARG,
6978 tr("Host PCI attachment only supported with ICH9 chipset"));
6979 }
6980
6981 // check if device with this host PCI address already attached
6982 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6983 it != mHWData->mPCIDeviceAssignments.end();
6984 ++it)
6985 {
6986 LONG iHostAddress = -1;
6987 ComPtr<PCIDeviceAttachment> pAttach;
6988 pAttach = *it;
6989 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6990 if (iHostAddress == hostAddress)
6991 return setError(E_INVALIDARG,
6992 tr("Device with host PCI address already attached to this VM"));
6993 }
6994
6995 ComObjPtr<PCIDeviceAttachment> pda;
6996 char name[32];
6997
6998 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6999 Bstr bname(name);
7000 pda.createObject();
7001 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7002 setModified(IsModified_MachineData);
7003 mHWData.backup();
7004 mHWData->mPCIDeviceAssignments.push_back(pda);
7005 }
7006
7007 return S_OK;
7008}
7009
7010/**
7011 * Currently this method doesn't detach device from the running VM,
7012 * just makes sure it's not plugged on next VM start.
7013 */
7014STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7015{
7016 AutoCaller autoCaller(this);
7017 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7018
7019 ComObjPtr<PCIDeviceAttachment> pAttach;
7020 bool fRemoved = false;
7021 HRESULT rc;
7022
7023 // lock scope
7024 {
7025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7026
7027 rc = checkStateDependency(MutableStateDep);
7028 if (FAILED(rc)) return rc;
7029
7030 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7031 it != mHWData->mPCIDeviceAssignments.end();
7032 ++it)
7033 {
7034 LONG iHostAddress = -1;
7035 pAttach = *it;
7036 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7037 if (iHostAddress != -1 && iHostAddress == hostAddress)
7038 {
7039 setModified(IsModified_MachineData);
7040 mHWData.backup();
7041 mHWData->mPCIDeviceAssignments.remove(pAttach);
7042 fRemoved = true;
7043 break;
7044 }
7045 }
7046 }
7047
7048
7049 /* Fire event outside of the lock */
7050 if (fRemoved)
7051 {
7052 Assert(!pAttach.isNull());
7053 ComPtr<IEventSource> es;
7054 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7055 Assert(SUCCEEDED(rc));
7056 Bstr mid;
7057 rc = this->COMGETTER(Id)(mid.asOutParam());
7058 Assert(SUCCEEDED(rc));
7059 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7060 }
7061
7062 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7063 tr("No host PCI device %08x attached"),
7064 hostAddress
7065 );
7066}
7067
7068STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7069{
7070 CheckComArgOutSafeArrayPointerValid(aAssignments);
7071
7072 AutoCaller autoCaller(this);
7073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7074
7075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7076
7077 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7078 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7079
7080 return S_OK;
7081}
7082
7083STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7084{
7085 CheckComArgOutPointerValid(aBandwidthControl);
7086
7087 AutoCaller autoCaller(this);
7088 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7089
7090 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7091
7092 return S_OK;
7093}
7094
7095STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7096{
7097 CheckComArgOutPointerValid(pfEnabled);
7098 AutoCaller autoCaller(this);
7099 HRESULT hrc = autoCaller.rc();
7100 if (SUCCEEDED(hrc))
7101 {
7102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7103 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7104 }
7105 return hrc;
7106}
7107
7108STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7109{
7110 AutoCaller autoCaller(this);
7111 HRESULT hrc = autoCaller.rc();
7112 if (SUCCEEDED(hrc))
7113 {
7114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7115 hrc = checkStateDependency(MutableStateDep);
7116 if (SUCCEEDED(hrc))
7117 {
7118 hrc = mHWData.backupEx();
7119 if (SUCCEEDED(hrc))
7120 {
7121 setModified(IsModified_MachineData);
7122 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7123 }
7124 }
7125 }
7126 return hrc;
7127}
7128
7129STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7130{
7131 CheckComArgOutPointerValid(pbstrConfig);
7132 AutoCaller autoCaller(this);
7133 HRESULT hrc = autoCaller.rc();
7134 if (SUCCEEDED(hrc))
7135 {
7136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7137 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7138 }
7139 return hrc;
7140}
7141
7142STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7143{
7144 CheckComArgStr(bstrConfig);
7145 AutoCaller autoCaller(this);
7146 HRESULT hrc = autoCaller.rc();
7147 if (SUCCEEDED(hrc))
7148 {
7149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7150 hrc = checkStateDependency(MutableStateDep);
7151 if (SUCCEEDED(hrc))
7152 {
7153 hrc = mHWData.backupEx();
7154 if (SUCCEEDED(hrc))
7155 {
7156 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7157 if (SUCCEEDED(hrc))
7158 setModified(IsModified_MachineData);
7159 }
7160 }
7161 }
7162 return hrc;
7163
7164}
7165
7166STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7167{
7168 CheckComArgOutPointerValid(pfAllow);
7169 AutoCaller autoCaller(this);
7170 HRESULT hrc = autoCaller.rc();
7171 if (SUCCEEDED(hrc))
7172 {
7173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7174 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7175 }
7176 return hrc;
7177}
7178
7179STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7180{
7181 AutoCaller autoCaller(this);
7182 HRESULT hrc = autoCaller.rc();
7183 if (SUCCEEDED(hrc))
7184 {
7185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7186 hrc = checkStateDependency(MutableStateDep);
7187 if (SUCCEEDED(hrc))
7188 {
7189 hrc = mHWData.backupEx();
7190 if (SUCCEEDED(hrc))
7191 {
7192 setModified(IsModified_MachineData);
7193 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7194 }
7195 }
7196 }
7197 return hrc;
7198}
7199
7200STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7201{
7202 CheckComArgOutPointerValid(pfEnabled);
7203 AutoCaller autoCaller(this);
7204 HRESULT hrc = autoCaller.rc();
7205 if (SUCCEEDED(hrc))
7206 {
7207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7208 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7209 }
7210 return hrc;
7211}
7212
7213STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7214{
7215 AutoCaller autoCaller(this);
7216 HRESULT hrc = autoCaller.rc();
7217 if (SUCCEEDED(hrc))
7218 {
7219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7220 hrc = checkStateDependency(MutableStateDep);
7221 if ( SUCCEEDED(hrc)
7222 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7223 {
7224 AutostartDb *autostartDb = mParent->getAutostartDb();
7225 int vrc;
7226
7227 if (fEnabled)
7228 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7229 else
7230 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7231
7232 if (RT_SUCCESS(vrc))
7233 {
7234 hrc = mHWData.backupEx();
7235 if (SUCCEEDED(hrc))
7236 {
7237 setModified(IsModified_MachineData);
7238 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7239 }
7240 }
7241 else if (vrc == VERR_NOT_SUPPORTED)
7242 hrc = setError(VBOX_E_NOT_SUPPORTED,
7243 tr("The VM autostart feature is not supported on this platform"));
7244 else if (vrc == VERR_PATH_NOT_FOUND)
7245 hrc = setError(E_FAIL,
7246 tr("The path to the autostart database is not set"));
7247 else
7248 hrc = setError(E_UNEXPECTED,
7249 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7250 fEnabled ? "Adding" : "Removing",
7251 mUserData->s.strName.c_str(), vrc);
7252 }
7253 }
7254 return hrc;
7255}
7256
7257STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7258{
7259 CheckComArgOutPointerValid(puDelay);
7260 AutoCaller autoCaller(this);
7261 HRESULT hrc = autoCaller.rc();
7262 if (SUCCEEDED(hrc))
7263 {
7264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7265 *puDelay = mHWData->mAutostart.uAutostartDelay;
7266 }
7267 return hrc;
7268}
7269
7270STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7271{
7272 AutoCaller autoCaller(this);
7273 HRESULT hrc = autoCaller.rc();
7274 if (SUCCEEDED(hrc))
7275 {
7276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7277 hrc = checkStateDependency(MutableStateDep);
7278 if (SUCCEEDED(hrc))
7279 {
7280 hrc = mHWData.backupEx();
7281 if (SUCCEEDED(hrc))
7282 {
7283 setModified(IsModified_MachineData);
7284 mHWData->mAutostart.uAutostartDelay = uDelay;
7285 }
7286 }
7287 }
7288 return hrc;
7289}
7290
7291STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7292{
7293 CheckComArgOutPointerValid(penmAutostopType);
7294 AutoCaller autoCaller(this);
7295 HRESULT hrc = autoCaller.rc();
7296 if (SUCCEEDED(hrc))
7297 {
7298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7299 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7300 }
7301 return hrc;
7302}
7303
7304STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7305{
7306 AutoCaller autoCaller(this);
7307 HRESULT hrc = autoCaller.rc();
7308 if (SUCCEEDED(hrc))
7309 {
7310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7311 hrc = checkStateDependency(MutableStateDep);
7312 if ( SUCCEEDED(hrc)
7313 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7314 {
7315 AutostartDb *autostartDb = mParent->getAutostartDb();
7316 int vrc;
7317
7318 if (enmAutostopType != AutostopType_Disabled)
7319 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7320 else
7321 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7322
7323 if (RT_SUCCESS(vrc))
7324 {
7325 hrc = mHWData.backupEx();
7326 if (SUCCEEDED(hrc))
7327 {
7328 setModified(IsModified_MachineData);
7329 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7330 }
7331 }
7332 else if (vrc == VERR_NOT_SUPPORTED)
7333 hrc = setError(VBOX_E_NOT_SUPPORTED,
7334 tr("The VM autostop feature is not supported on this platform"));
7335 else if (vrc == VERR_PATH_NOT_FOUND)
7336 hrc = setError(E_FAIL,
7337 tr("The path to the autostart database is not set"));
7338 else
7339 hrc = setError(E_UNEXPECTED,
7340 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7341 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7342 mUserData->s.strName.c_str(), vrc);
7343 }
7344 }
7345 return hrc;
7346}
7347
7348STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7349{
7350 CheckComArgOutPointerValid(aDefaultFrontend);
7351 AutoCaller autoCaller(this);
7352 HRESULT hrc = autoCaller.rc();
7353 if (SUCCEEDED(hrc))
7354 {
7355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7356 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7357 }
7358 return hrc;
7359}
7360
7361STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7362{
7363 CheckComArgStr(aDefaultFrontend);
7364 AutoCaller autoCaller(this);
7365 HRESULT hrc = autoCaller.rc();
7366 if (SUCCEEDED(hrc))
7367 {
7368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7369 hrc = checkStateDependency(MutableOrSavedStateDep);
7370 if (SUCCEEDED(hrc))
7371 {
7372 hrc = mHWData.backupEx();
7373 if (SUCCEEDED(hrc))
7374 {
7375 setModified(IsModified_MachineData);
7376 mHWData->mDefaultFrontend = aDefaultFrontend;
7377 }
7378 }
7379 }
7380 return hrc;
7381}
7382
7383STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7384{
7385 CheckComArgSafeArrayNotNull(aIcon);
7386 CheckComArgOutSafeArrayPointerValid(aIcon);
7387 AutoCaller autoCaller(this);
7388 HRESULT hrc = autoCaller.rc();
7389 if (SUCCEEDED(hrc))
7390 {
7391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7392 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7393 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7394 icon.detachTo(ComSafeArrayOutArg(aIcon));
7395 }
7396 return hrc;
7397}
7398
7399STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7400{
7401 CheckComArgSafeArrayNotNull(aIcon);
7402 AutoCaller autoCaller(this);
7403 HRESULT hrc = autoCaller.rc();
7404 if (SUCCEEDED(hrc))
7405 {
7406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7407 hrc = checkStateDependency(MutableOrSavedStateDep);
7408 if (SUCCEEDED(hrc))
7409 {
7410 setModified(IsModified_MachineData);
7411 mUserData.backup();
7412 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7413 mUserData->mIcon.clear();
7414 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7415 }
7416 }
7417 return hrc;
7418}
7419
7420STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7421{
7422 LogFlowFuncEnter();
7423
7424 CheckComArgNotNull(pTarget);
7425 CheckComArgOutPointerValid(pProgress);
7426
7427 /* Convert the options. */
7428 RTCList<CloneOptions_T> optList;
7429 if (options != NULL)
7430 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7431
7432 if (optList.contains(CloneOptions_Link))
7433 {
7434 if (!isSnapshotMachine())
7435 return setError(E_INVALIDARG,
7436 tr("Linked clone can only be created from a snapshot"));
7437 if (mode != CloneMode_MachineState)
7438 return setError(E_INVALIDARG,
7439 tr("Linked clone can only be created for a single machine state"));
7440 }
7441 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7442
7443 AutoCaller autoCaller(this);
7444 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7445
7446
7447 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7448
7449 HRESULT rc = pWorker->start(pProgress);
7450
7451 LogFlowFuncLeave();
7452
7453 return rc;
7454}
7455
7456// public methods for internal purposes
7457/////////////////////////////////////////////////////////////////////////////
7458
7459/**
7460 * Adds the given IsModified_* flag to the dirty flags of the machine.
7461 * This must be called either during loadSettings or under the machine write lock.
7462 * @param fl
7463 */
7464void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7465{
7466 mData->flModifications |= fl;
7467 if (fAllowStateModification && isStateModificationAllowed())
7468 mData->mCurrentStateModified = true;
7469}
7470
7471/**
7472 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7473 * care of the write locking.
7474 *
7475 * @param fModifications The flag to add.
7476 */
7477void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7478{
7479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7480 setModified(fModification, fAllowStateModification);
7481}
7482
7483/**
7484 * Saves the registry entry of this machine to the given configuration node.
7485 *
7486 * @param aEntryNode Node to save the registry entry to.
7487 *
7488 * @note locks this object for reading.
7489 */
7490HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7491{
7492 AutoLimitedCaller autoCaller(this);
7493 AssertComRCReturnRC(autoCaller.rc());
7494
7495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7496
7497 data.uuid = mData->mUuid;
7498 data.strSettingsFile = mData->m_strConfigFile;
7499
7500 return S_OK;
7501}
7502
7503/**
7504 * Calculates the absolute path of the given path taking the directory of the
7505 * machine settings file as the current directory.
7506 *
7507 * @param aPath Path to calculate the absolute path for.
7508 * @param aResult Where to put the result (used only on success, can be the
7509 * same Utf8Str instance as passed in @a aPath).
7510 * @return IPRT result.
7511 *
7512 * @note Locks this object for reading.
7513 */
7514int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7515{
7516 AutoCaller autoCaller(this);
7517 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7518
7519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7520
7521 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7522
7523 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7524
7525 strSettingsDir.stripFilename();
7526 char folder[RTPATH_MAX];
7527 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7528 if (RT_SUCCESS(vrc))
7529 aResult = folder;
7530
7531 return vrc;
7532}
7533
7534/**
7535 * Copies strSource to strTarget, making it relative to the machine folder
7536 * if it is a subdirectory thereof, or simply copying it otherwise.
7537 *
7538 * @param strSource Path to evaluate and copy.
7539 * @param strTarget Buffer to receive target path.
7540 *
7541 * @note Locks this object for reading.
7542 */
7543void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7544 Utf8Str &strTarget)
7545{
7546 AutoCaller autoCaller(this);
7547 AssertComRCReturn(autoCaller.rc(), (void)0);
7548
7549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7550
7551 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7552 // use strTarget as a temporary buffer to hold the machine settings dir
7553 strTarget = mData->m_strConfigFileFull;
7554 strTarget.stripFilename();
7555 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7556 {
7557 // is relative: then append what's left
7558 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7559 // for empty paths (only possible for subdirs) use "." to avoid
7560 // triggering default settings for not present config attributes.
7561 if (strTarget.isEmpty())
7562 strTarget = ".";
7563 }
7564 else
7565 // is not relative: then overwrite
7566 strTarget = strSource;
7567}
7568
7569/**
7570 * Returns the full path to the machine's log folder in the
7571 * \a aLogFolder argument.
7572 */
7573void Machine::getLogFolder(Utf8Str &aLogFolder)
7574{
7575 AutoCaller autoCaller(this);
7576 AssertComRCReturnVoid(autoCaller.rc());
7577
7578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7579
7580 char szTmp[RTPATH_MAX];
7581 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7582 if (RT_SUCCESS(vrc))
7583 {
7584 if (szTmp[0] && !mUserData.isNull())
7585 {
7586 char szTmp2[RTPATH_MAX];
7587 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7588 if (RT_SUCCESS(vrc))
7589 aLogFolder = BstrFmt("%s%c%s",
7590 szTmp2,
7591 RTPATH_DELIMITER,
7592 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7593 }
7594 else
7595 vrc = VERR_PATH_IS_RELATIVE;
7596 }
7597
7598 if (RT_FAILURE(vrc))
7599 {
7600 // fallback if VBOX_USER_LOGHOME is not set or invalid
7601 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7602 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7603 aLogFolder.append(RTPATH_DELIMITER);
7604 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7605 }
7606}
7607
7608/**
7609 * Returns the full path to the machine's log file for an given index.
7610 */
7611Utf8Str Machine::queryLogFilename(ULONG idx)
7612{
7613 Utf8Str logFolder;
7614 getLogFolder(logFolder);
7615 Assert(logFolder.length());
7616 Utf8Str log;
7617 if (idx == 0)
7618 log = Utf8StrFmt("%s%cVBox.log",
7619 logFolder.c_str(), RTPATH_DELIMITER);
7620 else
7621 log = Utf8StrFmt("%s%cVBox.log.%d",
7622 logFolder.c_str(), RTPATH_DELIMITER, idx);
7623 return log;
7624}
7625
7626/**
7627 * Composes a unique saved state filename based on the current system time. The filename is
7628 * granular to the second so this will work so long as no more than one snapshot is taken on
7629 * a machine per second.
7630 *
7631 * Before version 4.1, we used this formula for saved state files:
7632 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7633 * which no longer works because saved state files can now be shared between the saved state of the
7634 * "saved" machine and an online snapshot, and the following would cause problems:
7635 * 1) save machine
7636 * 2) create online snapshot from that machine state --> reusing saved state file
7637 * 3) save machine again --> filename would be reused, breaking the online snapshot
7638 *
7639 * So instead we now use a timestamp.
7640 *
7641 * @param str
7642 */
7643void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7644{
7645 AutoCaller autoCaller(this);
7646 AssertComRCReturnVoid(autoCaller.rc());
7647
7648 {
7649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7650 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7651 }
7652
7653 RTTIMESPEC ts;
7654 RTTimeNow(&ts);
7655 RTTIME time;
7656 RTTimeExplode(&time, &ts);
7657
7658 strStateFilePath += RTPATH_DELIMITER;
7659 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7660 time.i32Year, time.u8Month, time.u8MonthDay,
7661 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7662}
7663
7664/**
7665 * @note Locks this object for writing, calls the client process
7666 * (inside the lock).
7667 */
7668HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7669 const Utf8Str &strFrontend,
7670 const Utf8Str &strEnvironment,
7671 ProgressProxy *aProgress)
7672{
7673 LogFlowThisFuncEnter();
7674
7675 AssertReturn(aControl, E_FAIL);
7676 AssertReturn(aProgress, E_FAIL);
7677 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7678
7679 AutoCaller autoCaller(this);
7680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7681
7682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7683
7684 if (!mData->mRegistered)
7685 return setError(E_UNEXPECTED,
7686 tr("The machine '%s' is not registered"),
7687 mUserData->s.strName.c_str());
7688
7689 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7690
7691 if ( mData->mSession.mState == SessionState_Locked
7692 || mData->mSession.mState == SessionState_Spawning
7693 || mData->mSession.mState == SessionState_Unlocking)
7694 return setError(VBOX_E_INVALID_OBJECT_STATE,
7695 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7696 mUserData->s.strName.c_str());
7697
7698 /* may not be busy */
7699 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7700
7701 /* get the path to the executable */
7702 char szPath[RTPATH_MAX];
7703 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7704 size_t sz = strlen(szPath);
7705 szPath[sz++] = RTPATH_DELIMITER;
7706 szPath[sz] = 0;
7707 char *cmd = szPath + sz;
7708 sz = RTPATH_MAX - sz;
7709
7710 int vrc = VINF_SUCCESS;
7711 RTPROCESS pid = NIL_RTPROCESS;
7712
7713 RTENV env = RTENV_DEFAULT;
7714
7715 if (!strEnvironment.isEmpty())
7716 {
7717 char *newEnvStr = NULL;
7718
7719 do
7720 {
7721 /* clone the current environment */
7722 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7723 AssertRCBreakStmt(vrc2, vrc = vrc2);
7724
7725 newEnvStr = RTStrDup(strEnvironment.c_str());
7726 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7727
7728 /* put new variables to the environment
7729 * (ignore empty variable names here since RTEnv API
7730 * intentionally doesn't do that) */
7731 char *var = newEnvStr;
7732 for (char *p = newEnvStr; *p; ++p)
7733 {
7734 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7735 {
7736 *p = '\0';
7737 if (*var)
7738 {
7739 char *val = strchr(var, '=');
7740 if (val)
7741 {
7742 *val++ = '\0';
7743 vrc2 = RTEnvSetEx(env, var, val);
7744 }
7745 else
7746 vrc2 = RTEnvUnsetEx(env, var);
7747 if (RT_FAILURE(vrc2))
7748 break;
7749 }
7750 var = p + 1;
7751 }
7752 }
7753 if (RT_SUCCESS(vrc2) && *var)
7754 vrc2 = RTEnvPutEx(env, var);
7755
7756 AssertRCBreakStmt(vrc2, vrc = vrc2);
7757 }
7758 while (0);
7759
7760 if (newEnvStr != NULL)
7761 RTStrFree(newEnvStr);
7762 }
7763
7764#ifdef VBOX_WITH_QTGUI
7765 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7766 {
7767# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7768 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7769# else
7770 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7771# endif
7772 Assert(sz >= sizeof(VirtualBox_exe));
7773 strcpy(cmd, VirtualBox_exe);
7774
7775 Utf8Str idStr = mData->mUuid.toString();
7776 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7777 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7778 }
7779#else /* !VBOX_WITH_QTGUI */
7780 if (0)
7781 ;
7782#endif /* VBOX_WITH_QTGUI */
7783
7784 else
7785
7786#ifdef VBOX_WITH_VBOXSDL
7787 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7788 {
7789 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7790 Assert(sz >= sizeof(VBoxSDL_exe));
7791 strcpy(cmd, VBoxSDL_exe);
7792
7793 Utf8Str idStr = mData->mUuid.toString();
7794 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7795 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7796 }
7797#else /* !VBOX_WITH_VBOXSDL */
7798 if (0)
7799 ;
7800#endif /* !VBOX_WITH_VBOXSDL */
7801
7802 else
7803
7804#ifdef VBOX_WITH_HEADLESS
7805 if ( strFrontend == "headless"
7806 || strFrontend == "capture"
7807 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7808 )
7809 {
7810 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7811 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7812 * and a VM works even if the server has not been installed.
7813 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7814 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7815 * differently in 4.0 and 3.x.
7816 */
7817 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7818 Assert(sz >= sizeof(VBoxHeadless_exe));
7819 strcpy(cmd, VBoxHeadless_exe);
7820
7821 Utf8Str idStr = mData->mUuid.toString();
7822 /* Leave space for "--capture" arg. */
7823 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7824 "--startvm", idStr.c_str(),
7825 "--vrde", "config",
7826 0, /* For "--capture". */
7827 0 };
7828 if (strFrontend == "capture")
7829 {
7830 unsigned pos = RT_ELEMENTS(args) - 2;
7831 args[pos] = "--capture";
7832 }
7833 vrc = RTProcCreate(szPath, args, env,
7834#ifdef RT_OS_WINDOWS
7835 RTPROC_FLAGS_NO_WINDOW
7836#else
7837 0
7838#endif
7839 , &pid);
7840 }
7841#else /* !VBOX_WITH_HEADLESS */
7842 if (0)
7843 ;
7844#endif /* !VBOX_WITH_HEADLESS */
7845 else
7846 {
7847 RTEnvDestroy(env);
7848 return setError(E_INVALIDARG,
7849 tr("Invalid frontend name: '%s'"),
7850 strFrontend.c_str());
7851 }
7852
7853 RTEnvDestroy(env);
7854
7855 if (RT_FAILURE(vrc))
7856 return setError(VBOX_E_IPRT_ERROR,
7857 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7858 mUserData->s.strName.c_str(), vrc);
7859
7860 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7861
7862 /*
7863 * Note that we don't release the lock here before calling the client,
7864 * because it doesn't need to call us back if called with a NULL argument.
7865 * Releasing the lock here is dangerous because we didn't prepare the
7866 * launch data yet, but the client we've just started may happen to be
7867 * too fast and call openSession() that will fail (because of PID, etc.),
7868 * so that the Machine will never get out of the Spawning session state.
7869 */
7870
7871 /* inform the session that it will be a remote one */
7872 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7873 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7874 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7875
7876 if (FAILED(rc))
7877 {
7878 /* restore the session state */
7879 mData->mSession.mState = SessionState_Unlocked;
7880 /* The failure may occur w/o any error info (from RPC), so provide one */
7881 return setError(VBOX_E_VM_ERROR,
7882 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7883 }
7884
7885 /* attach launch data to the machine */
7886 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7887 mData->mSession.mRemoteControls.push_back(aControl);
7888 mData->mSession.mProgress = aProgress;
7889 mData->mSession.mPID = pid;
7890 mData->mSession.mState = SessionState_Spawning;
7891 mData->mSession.mType = strFrontend;
7892
7893 LogFlowThisFuncLeave();
7894 return S_OK;
7895}
7896
7897/**
7898 * Returns @c true if the given machine has an open direct session and returns
7899 * the session machine instance and additional session data (on some platforms)
7900 * if so.
7901 *
7902 * Note that when the method returns @c false, the arguments remain unchanged.
7903 *
7904 * @param aMachine Session machine object.
7905 * @param aControl Direct session control object (optional).
7906 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7907 *
7908 * @note locks this object for reading.
7909 */
7910#if defined(RT_OS_WINDOWS)
7911bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7912 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7913 HANDLE *aIPCSem /*= NULL*/,
7914 bool aAllowClosing /*= false*/)
7915#elif defined(RT_OS_OS2)
7916bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7917 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7918 HMTX *aIPCSem /*= NULL*/,
7919 bool aAllowClosing /*= false*/)
7920#else
7921bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7922 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7923 bool aAllowClosing /*= false*/)
7924#endif
7925{
7926 AutoLimitedCaller autoCaller(this);
7927 AssertComRCReturn(autoCaller.rc(), false);
7928
7929 /* just return false for inaccessible machines */
7930 if (autoCaller.state() != Ready)
7931 return false;
7932
7933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7934
7935 if ( mData->mSession.mState == SessionState_Locked
7936 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7937 )
7938 {
7939 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7940
7941 aMachine = mData->mSession.mMachine;
7942
7943 if (aControl != NULL)
7944 *aControl = mData->mSession.mDirectControl;
7945
7946#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7947 /* Additional session data */
7948 if (aIPCSem != NULL)
7949 *aIPCSem = aMachine->mIPCSem;
7950#endif
7951 return true;
7952 }
7953
7954 return false;
7955}
7956
7957/**
7958 * Returns @c true if the given machine has an spawning direct session and
7959 * returns and additional session data (on some platforms) if so.
7960 *
7961 * Note that when the method returns @c false, the arguments remain unchanged.
7962 *
7963 * @param aPID PID of the spawned direct session process.
7964 *
7965 * @note locks this object for reading.
7966 */
7967#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7968bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7969#else
7970bool Machine::isSessionSpawning()
7971#endif
7972{
7973 AutoLimitedCaller autoCaller(this);
7974 AssertComRCReturn(autoCaller.rc(), false);
7975
7976 /* just return false for inaccessible machines */
7977 if (autoCaller.state() != Ready)
7978 return false;
7979
7980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7981
7982 if (mData->mSession.mState == SessionState_Spawning)
7983 {
7984#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7985 /* Additional session data */
7986 if (aPID != NULL)
7987 {
7988 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7989 *aPID = mData->mSession.mPID;
7990 }
7991#endif
7992 return true;
7993 }
7994
7995 return false;
7996}
7997
7998/**
7999 * Called from the client watcher thread to check for unexpected client process
8000 * death during Session_Spawning state (e.g. before it successfully opened a
8001 * direct session).
8002 *
8003 * On Win32 and on OS/2, this method is called only when we've got the
8004 * direct client's process termination notification, so it always returns @c
8005 * true.
8006 *
8007 * On other platforms, this method returns @c true if the client process is
8008 * terminated and @c false if it's still alive.
8009 *
8010 * @note Locks this object for writing.
8011 */
8012bool Machine::checkForSpawnFailure()
8013{
8014 AutoCaller autoCaller(this);
8015 if (!autoCaller.isOk())
8016 {
8017 /* nothing to do */
8018 LogFlowThisFunc(("Already uninitialized!\n"));
8019 return true;
8020 }
8021
8022 /* VirtualBox::addProcessToReap() needs a write lock */
8023 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
8024
8025 if (mData->mSession.mState != SessionState_Spawning)
8026 {
8027 /* nothing to do */
8028 LogFlowThisFunc(("Not spawning any more!\n"));
8029 return true;
8030 }
8031
8032 HRESULT rc = S_OK;
8033
8034#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8035
8036 /* the process was already unexpectedly terminated, we just need to set an
8037 * error and finalize session spawning */
8038 rc = setError(E_FAIL,
8039 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
8040 getName().c_str());
8041#else
8042
8043 /* PID not yet initialized, skip check. */
8044 if (mData->mSession.mPID == NIL_RTPROCESS)
8045 return false;
8046
8047 RTPROCSTATUS status;
8048 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
8049 &status);
8050
8051 if (vrc != VERR_PROCESS_RUNNING)
8052 {
8053 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8054 rc = setError(E_FAIL,
8055 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8056 getName().c_str(), status.iStatus);
8057 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8058 rc = setError(E_FAIL,
8059 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8060 getName().c_str(), status.iStatus);
8061 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8062 rc = setError(E_FAIL,
8063 tr("The virtual machine '%s' has terminated abnormally"),
8064 getName().c_str(), status.iStatus);
8065 else
8066 rc = setError(E_FAIL,
8067 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8068 getName().c_str(), rc);
8069 }
8070
8071#endif
8072
8073 if (FAILED(rc))
8074 {
8075 /* Close the remote session, remove the remote control from the list
8076 * and reset session state to Closed (@note keep the code in sync with
8077 * the relevant part in checkForSpawnFailure()). */
8078
8079 Assert(mData->mSession.mRemoteControls.size() == 1);
8080 if (mData->mSession.mRemoteControls.size() == 1)
8081 {
8082 ErrorInfoKeeper eik;
8083 mData->mSession.mRemoteControls.front()->Uninitialize();
8084 }
8085
8086 mData->mSession.mRemoteControls.clear();
8087 mData->mSession.mState = SessionState_Unlocked;
8088
8089 /* finalize the progress after setting the state */
8090 if (!mData->mSession.mProgress.isNull())
8091 {
8092 mData->mSession.mProgress->notifyComplete(rc);
8093 mData->mSession.mProgress.setNull();
8094 }
8095
8096 mParent->addProcessToReap(mData->mSession.mPID);
8097 mData->mSession.mPID = NIL_RTPROCESS;
8098
8099 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8100 return true;
8101 }
8102
8103 return false;
8104}
8105
8106/**
8107 * Checks whether the machine can be registered. If so, commits and saves
8108 * all settings.
8109 *
8110 * @note Must be called from mParent's write lock. Locks this object and
8111 * children for writing.
8112 */
8113HRESULT Machine::prepareRegister()
8114{
8115 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8116
8117 AutoLimitedCaller autoCaller(this);
8118 AssertComRCReturnRC(autoCaller.rc());
8119
8120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8121
8122 /* wait for state dependents to drop to zero */
8123 ensureNoStateDependencies();
8124
8125 if (!mData->mAccessible)
8126 return setError(VBOX_E_INVALID_OBJECT_STATE,
8127 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8128 mUserData->s.strName.c_str(),
8129 mData->mUuid.toString().c_str());
8130
8131 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8132
8133 if (mData->mRegistered)
8134 return setError(VBOX_E_INVALID_OBJECT_STATE,
8135 tr("The machine '%s' with UUID {%s} is already registered"),
8136 mUserData->s.strName.c_str(),
8137 mData->mUuid.toString().c_str());
8138
8139 HRESULT rc = S_OK;
8140
8141 // Ensure the settings are saved. If we are going to be registered and
8142 // no config file exists yet, create it by calling saveSettings() too.
8143 if ( (mData->flModifications)
8144 || (!mData->pMachineConfigFile->fileExists())
8145 )
8146 {
8147 rc = saveSettings(NULL);
8148 // no need to check whether VirtualBox.xml needs saving too since
8149 // we can't have a machine XML file rename pending
8150 if (FAILED(rc)) return rc;
8151 }
8152
8153 /* more config checking goes here */
8154
8155 if (SUCCEEDED(rc))
8156 {
8157 /* we may have had implicit modifications we want to fix on success */
8158 commit();
8159
8160 mData->mRegistered = true;
8161 }
8162 else
8163 {
8164 /* we may have had implicit modifications we want to cancel on failure*/
8165 rollback(false /* aNotify */);
8166 }
8167
8168 return rc;
8169}
8170
8171/**
8172 * Increases the number of objects dependent on the machine state or on the
8173 * registered state. Guarantees that these two states will not change at least
8174 * until #releaseStateDependency() is called.
8175 *
8176 * Depending on the @a aDepType value, additional state checks may be made.
8177 * These checks will set extended error info on failure. See
8178 * #checkStateDependency() for more info.
8179 *
8180 * If this method returns a failure, the dependency is not added and the caller
8181 * is not allowed to rely on any particular machine state or registration state
8182 * value and may return the failed result code to the upper level.
8183 *
8184 * @param aDepType Dependency type to add.
8185 * @param aState Current machine state (NULL if not interested).
8186 * @param aRegistered Current registered state (NULL if not interested).
8187 *
8188 * @note Locks this object for writing.
8189 */
8190HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8191 MachineState_T *aState /* = NULL */,
8192 BOOL *aRegistered /* = NULL */)
8193{
8194 AutoCaller autoCaller(this);
8195 AssertComRCReturnRC(autoCaller.rc());
8196
8197 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8198
8199 HRESULT rc = checkStateDependency(aDepType);
8200 if (FAILED(rc)) return rc;
8201
8202 {
8203 if (mData->mMachineStateChangePending != 0)
8204 {
8205 /* ensureNoStateDependencies() is waiting for state dependencies to
8206 * drop to zero so don't add more. It may make sense to wait a bit
8207 * and retry before reporting an error (since the pending state
8208 * transition should be really quick) but let's just assert for
8209 * now to see if it ever happens on practice. */
8210
8211 AssertFailed();
8212
8213 return setError(E_ACCESSDENIED,
8214 tr("Machine state change is in progress. Please retry the operation later."));
8215 }
8216
8217 ++mData->mMachineStateDeps;
8218 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8219 }
8220
8221 if (aState)
8222 *aState = mData->mMachineState;
8223 if (aRegistered)
8224 *aRegistered = mData->mRegistered;
8225
8226 return S_OK;
8227}
8228
8229/**
8230 * Decreases the number of objects dependent on the machine state.
8231 * Must always complete the #addStateDependency() call after the state
8232 * dependency is no more necessary.
8233 */
8234void Machine::releaseStateDependency()
8235{
8236 AutoCaller autoCaller(this);
8237 AssertComRCReturnVoid(autoCaller.rc());
8238
8239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8240
8241 /* releaseStateDependency() w/o addStateDependency()? */
8242 AssertReturnVoid(mData->mMachineStateDeps != 0);
8243 -- mData->mMachineStateDeps;
8244
8245 if (mData->mMachineStateDeps == 0)
8246 {
8247 /* inform ensureNoStateDependencies() that there are no more deps */
8248 if (mData->mMachineStateChangePending != 0)
8249 {
8250 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8251 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8252 }
8253 }
8254}
8255
8256Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8257{
8258 /* start with nothing found */
8259 Utf8Str strResult("");
8260
8261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8262
8263 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8264 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8265 // found:
8266 strResult = it->second; // source is a Utf8Str
8267
8268 return strResult;
8269}
8270
8271// protected methods
8272/////////////////////////////////////////////////////////////////////////////
8273
8274/**
8275 * Performs machine state checks based on the @a aDepType value. If a check
8276 * fails, this method will set extended error info, otherwise it will return
8277 * S_OK. It is supposed, that on failure, the caller will immediately return
8278 * the return value of this method to the upper level.
8279 *
8280 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8281 *
8282 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8283 * current state of this machine object allows to change settings of the
8284 * machine (i.e. the machine is not registered, or registered but not running
8285 * and not saved). It is useful to call this method from Machine setters
8286 * before performing any change.
8287 *
8288 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8289 * as for MutableStateDep except that if the machine is saved, S_OK is also
8290 * returned. This is useful in setters which allow changing machine
8291 * properties when it is in the saved state.
8292 *
8293 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8294 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8295 * Aborted).
8296 *
8297 * @param aDepType Dependency type to check.
8298 *
8299 * @note Non Machine based classes should use #addStateDependency() and
8300 * #releaseStateDependency() methods or the smart AutoStateDependency
8301 * template.
8302 *
8303 * @note This method must be called from under this object's read or write
8304 * lock.
8305 */
8306HRESULT Machine::checkStateDependency(StateDependency aDepType)
8307{
8308 switch (aDepType)
8309 {
8310 case AnyStateDep:
8311 {
8312 break;
8313 }
8314 case MutableStateDep:
8315 {
8316 if ( mData->mRegistered
8317 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8318 || ( mData->mMachineState != MachineState_Paused
8319 && mData->mMachineState != MachineState_Running
8320 && mData->mMachineState != MachineState_Aborted
8321 && mData->mMachineState != MachineState_Teleported
8322 && mData->mMachineState != MachineState_PoweredOff
8323 )
8324 )
8325 )
8326 return setError(VBOX_E_INVALID_VM_STATE,
8327 tr("The machine is not mutable (state is %s)"),
8328 Global::stringifyMachineState(mData->mMachineState));
8329 break;
8330 }
8331 case MutableOrSavedStateDep:
8332 {
8333 if ( mData->mRegistered
8334 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8335 || ( mData->mMachineState != MachineState_Paused
8336 && mData->mMachineState != MachineState_Running
8337 && mData->mMachineState != MachineState_Aborted
8338 && mData->mMachineState != MachineState_Teleported
8339 && mData->mMachineState != MachineState_Saved
8340 && mData->mMachineState != MachineState_PoweredOff
8341 )
8342 )
8343 )
8344 return setError(VBOX_E_INVALID_VM_STATE,
8345 tr("The machine is not mutable (state is %s)"),
8346 Global::stringifyMachineState(mData->mMachineState));
8347 break;
8348 }
8349 case OfflineStateDep:
8350 {
8351 if ( mData->mRegistered
8352 && ( !isSessionMachine()
8353 || ( mData->mMachineState != MachineState_PoweredOff
8354 && mData->mMachineState != MachineState_Saved
8355 && mData->mMachineState != MachineState_Aborted
8356 && mData->mMachineState != MachineState_Teleported
8357 )
8358 )
8359 )
8360 return setError(VBOX_E_INVALID_VM_STATE,
8361 tr("The machine is not offline (state is %s)"),
8362 Global::stringifyMachineState(mData->mMachineState));
8363 break;
8364 }
8365 }
8366
8367 return S_OK;
8368}
8369
8370/**
8371 * Helper to initialize all associated child objects and allocate data
8372 * structures.
8373 *
8374 * This method must be called as a part of the object's initialization procedure
8375 * (usually done in the #init() method).
8376 *
8377 * @note Must be called only from #init() or from #registeredInit().
8378 */
8379HRESULT Machine::initDataAndChildObjects()
8380{
8381 AutoCaller autoCaller(this);
8382 AssertComRCReturnRC(autoCaller.rc());
8383 AssertComRCReturn(autoCaller.state() == InInit ||
8384 autoCaller.state() == Limited, E_FAIL);
8385
8386 AssertReturn(!mData->mAccessible, E_FAIL);
8387
8388 /* allocate data structures */
8389 mSSData.allocate();
8390 mUserData.allocate();
8391 mHWData.allocate();
8392 mMediaData.allocate();
8393 mStorageControllers.allocate();
8394
8395 /* initialize mOSTypeId */
8396 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8397
8398 /* create associated BIOS settings object */
8399 unconst(mBIOSSettings).createObject();
8400 mBIOSSettings->init(this);
8401
8402 /* create an associated VRDE object (default is disabled) */
8403 unconst(mVRDEServer).createObject();
8404 mVRDEServer->init(this);
8405
8406 /* create associated serial port objects */
8407 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8408 {
8409 unconst(mSerialPorts[slot]).createObject();
8410 mSerialPorts[slot]->init(this, slot);
8411 }
8412
8413 /* create associated parallel port objects */
8414 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8415 {
8416 unconst(mParallelPorts[slot]).createObject();
8417 mParallelPorts[slot]->init(this, slot);
8418 }
8419
8420 /* create the audio adapter object (always present, default is disabled) */
8421 unconst(mAudioAdapter).createObject();
8422 mAudioAdapter->init(this);
8423
8424 /* create the USB controller object (always present, default is disabled) */
8425 unconst(mUSBController).createObject();
8426 mUSBController->init(this);
8427
8428 /* create associated network adapter objects */
8429 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8430 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8431 {
8432 unconst(mNetworkAdapters[slot]).createObject();
8433 mNetworkAdapters[slot]->init(this, slot);
8434 }
8435
8436 /* create the bandwidth control */
8437 unconst(mBandwidthControl).createObject();
8438 mBandwidthControl->init(this);
8439
8440 return S_OK;
8441}
8442
8443/**
8444 * Helper to uninitialize all associated child objects and to free all data
8445 * structures.
8446 *
8447 * This method must be called as a part of the object's uninitialization
8448 * procedure (usually done in the #uninit() method).
8449 *
8450 * @note Must be called only from #uninit() or from #registeredInit().
8451 */
8452void Machine::uninitDataAndChildObjects()
8453{
8454 AutoCaller autoCaller(this);
8455 AssertComRCReturnVoid(autoCaller.rc());
8456 AssertComRCReturnVoid( autoCaller.state() == InUninit
8457 || autoCaller.state() == Limited);
8458
8459 /* tell all our other child objects we've been uninitialized */
8460 if (mBandwidthControl)
8461 {
8462 mBandwidthControl->uninit();
8463 unconst(mBandwidthControl).setNull();
8464 }
8465
8466 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8467 {
8468 if (mNetworkAdapters[slot])
8469 {
8470 mNetworkAdapters[slot]->uninit();
8471 unconst(mNetworkAdapters[slot]).setNull();
8472 }
8473 }
8474
8475 if (mUSBController)
8476 {
8477 mUSBController->uninit();
8478 unconst(mUSBController).setNull();
8479 }
8480
8481 if (mAudioAdapter)
8482 {
8483 mAudioAdapter->uninit();
8484 unconst(mAudioAdapter).setNull();
8485 }
8486
8487 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8488 {
8489 if (mParallelPorts[slot])
8490 {
8491 mParallelPorts[slot]->uninit();
8492 unconst(mParallelPorts[slot]).setNull();
8493 }
8494 }
8495
8496 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8497 {
8498 if (mSerialPorts[slot])
8499 {
8500 mSerialPorts[slot]->uninit();
8501 unconst(mSerialPorts[slot]).setNull();
8502 }
8503 }
8504
8505 if (mVRDEServer)
8506 {
8507 mVRDEServer->uninit();
8508 unconst(mVRDEServer).setNull();
8509 }
8510
8511 if (mBIOSSettings)
8512 {
8513 mBIOSSettings->uninit();
8514 unconst(mBIOSSettings).setNull();
8515 }
8516
8517 /* Deassociate media (only when a real Machine or a SnapshotMachine
8518 * instance is uninitialized; SessionMachine instances refer to real
8519 * Machine media). This is necessary for a clean re-initialization of
8520 * the VM after successfully re-checking the accessibility state. Note
8521 * that in case of normal Machine or SnapshotMachine uninitialization (as
8522 * a result of unregistering or deleting the snapshot), outdated media
8523 * attachments will already be uninitialized and deleted, so this
8524 * code will not affect them. */
8525 if ( !!mMediaData
8526 && (!isSessionMachine())
8527 )
8528 {
8529 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8530 it != mMediaData->mAttachments.end();
8531 ++it)
8532 {
8533 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8534 if (pMedium.isNull())
8535 continue;
8536 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8537 AssertComRC(rc);
8538 }
8539 }
8540
8541 if (!isSessionMachine() && !isSnapshotMachine())
8542 {
8543 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8544 if (mData->mFirstSnapshot)
8545 {
8546 // snapshots tree is protected by machine write lock; strictly
8547 // this isn't necessary here since we're deleting the entire
8548 // machine, but otherwise we assert in Snapshot::uninit()
8549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8550 mData->mFirstSnapshot->uninit();
8551 mData->mFirstSnapshot.setNull();
8552 }
8553
8554 mData->mCurrentSnapshot.setNull();
8555 }
8556
8557 /* free data structures (the essential mData structure is not freed here
8558 * since it may be still in use) */
8559 mMediaData.free();
8560 mStorageControllers.free();
8561 mHWData.free();
8562 mUserData.free();
8563 mSSData.free();
8564}
8565
8566/**
8567 * Returns a pointer to the Machine object for this machine that acts like a
8568 * parent for complex machine data objects such as shared folders, etc.
8569 *
8570 * For primary Machine objects and for SnapshotMachine objects, returns this
8571 * object's pointer itself. For SessionMachine objects, returns the peer
8572 * (primary) machine pointer.
8573 */
8574Machine* Machine::getMachine()
8575{
8576 if (isSessionMachine())
8577 return (Machine*)mPeer;
8578 return this;
8579}
8580
8581/**
8582 * Makes sure that there are no machine state dependents. If necessary, waits
8583 * for the number of dependents to drop to zero.
8584 *
8585 * Make sure this method is called from under this object's write lock to
8586 * guarantee that no new dependents may be added when this method returns
8587 * control to the caller.
8588 *
8589 * @note Locks this object for writing. The lock will be released while waiting
8590 * (if necessary).
8591 *
8592 * @warning To be used only in methods that change the machine state!
8593 */
8594void Machine::ensureNoStateDependencies()
8595{
8596 AssertReturnVoid(isWriteLockOnCurrentThread());
8597
8598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8599
8600 /* Wait for all state dependents if necessary */
8601 if (mData->mMachineStateDeps != 0)
8602 {
8603 /* lazy semaphore creation */
8604 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8605 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8606
8607 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8608 mData->mMachineStateDeps));
8609
8610 ++mData->mMachineStateChangePending;
8611
8612 /* reset the semaphore before waiting, the last dependent will signal
8613 * it */
8614 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8615
8616 alock.release();
8617
8618 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8619
8620 alock.acquire();
8621
8622 -- mData->mMachineStateChangePending;
8623 }
8624}
8625
8626/**
8627 * Changes the machine state and informs callbacks.
8628 *
8629 * This method is not intended to fail so it either returns S_OK or asserts (and
8630 * returns a failure).
8631 *
8632 * @note Locks this object for writing.
8633 */
8634HRESULT Machine::setMachineState(MachineState_T aMachineState)
8635{
8636 LogFlowThisFuncEnter();
8637 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8638
8639 AutoCaller autoCaller(this);
8640 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8641
8642 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8643
8644 /* wait for state dependents to drop to zero */
8645 ensureNoStateDependencies();
8646
8647 if (mData->mMachineState != aMachineState)
8648 {
8649 mData->mMachineState = aMachineState;
8650
8651 RTTimeNow(&mData->mLastStateChange);
8652
8653 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8654 }
8655
8656 LogFlowThisFuncLeave();
8657 return S_OK;
8658}
8659
8660/**
8661 * Searches for a shared folder with the given logical name
8662 * in the collection of shared folders.
8663 *
8664 * @param aName logical name of the shared folder
8665 * @param aSharedFolder where to return the found object
8666 * @param aSetError whether to set the error info if the folder is
8667 * not found
8668 * @return
8669 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8670 *
8671 * @note
8672 * must be called from under the object's lock!
8673 */
8674HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8675 ComObjPtr<SharedFolder> &aSharedFolder,
8676 bool aSetError /* = false */)
8677{
8678 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8679 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8680 it != mHWData->mSharedFolders.end();
8681 ++it)
8682 {
8683 SharedFolder *pSF = *it;
8684 AutoCaller autoCaller(pSF);
8685 if (pSF->getName() == aName)
8686 {
8687 aSharedFolder = pSF;
8688 rc = S_OK;
8689 break;
8690 }
8691 }
8692
8693 if (aSetError && FAILED(rc))
8694 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8695
8696 return rc;
8697}
8698
8699/**
8700 * Initializes all machine instance data from the given settings structures
8701 * from XML. The exception is the machine UUID which needs special handling
8702 * depending on the caller's use case, so the caller needs to set that herself.
8703 *
8704 * This gets called in several contexts during machine initialization:
8705 *
8706 * -- When machine XML exists on disk already and needs to be loaded into memory,
8707 * for example, from registeredInit() to load all registered machines on
8708 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8709 * attached to the machine should be part of some media registry already.
8710 *
8711 * -- During OVF import, when a machine config has been constructed from an
8712 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8713 * ensure that the media listed as attachments in the config (which have
8714 * been imported from the OVF) receive the correct registry ID.
8715 *
8716 * -- During VM cloning.
8717 *
8718 * @param config Machine settings from XML.
8719 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8720 * @return
8721 */
8722HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8723 const Guid *puuidRegistry)
8724{
8725 // copy name, description, OS type, teleporter, UTC etc.
8726 #define DECODE_STR_MAX _1M
8727 mUserData->s = config.machineUserData;
8728
8729 // Decode the Icon overide data from config userdata and set onto Machine.
8730 const char* pszStr = config.machineUserData.ovIcon.c_str();
8731 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8732 if (cbOut > DECODE_STR_MAX)
8733 return setError(E_FAIL,
8734 tr("Icon Data too long.'%d' > '%d'"),
8735 cbOut,
8736 DECODE_STR_MAX);
8737 com::SafeArray<BYTE> iconByte(cbOut);
8738 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8739 if (FAILED(rc))
8740 return setError(E_FAIL,
8741 tr("Failure to Decode Icon Data. '%s' (%d)"),
8742 pszStr,
8743 rc);
8744 COMSETTER(Icon)(ComSafeArrayAsInParam(iconByte));
8745
8746 // look up the object by Id to check it is valid
8747 ComPtr<IGuestOSType> guestOSType;
8748 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8749 guestOSType.asOutParam());
8750 if (FAILED(rc)) return rc;
8751
8752 // stateFile (optional)
8753 if (config.strStateFile.isEmpty())
8754 mSSData->strStateFilePath.setNull();
8755 else
8756 {
8757 Utf8Str stateFilePathFull(config.strStateFile);
8758 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8759 if (RT_FAILURE(vrc))
8760 return setError(E_FAIL,
8761 tr("Invalid saved state file path '%s' (%Rrc)"),
8762 config.strStateFile.c_str(),
8763 vrc);
8764 mSSData->strStateFilePath = stateFilePathFull;
8765 }
8766
8767 // snapshot folder needs special processing so set it again
8768 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8769 if (FAILED(rc)) return rc;
8770
8771 /* Copy the extra data items (Not in any case config is already the same as
8772 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8773 * make sure the extra data map is copied). */
8774 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8775
8776 /* currentStateModified (optional, default is true) */
8777 mData->mCurrentStateModified = config.fCurrentStateModified;
8778
8779 mData->mLastStateChange = config.timeLastStateChange;
8780
8781 /*
8782 * note: all mUserData members must be assigned prior this point because
8783 * we need to commit changes in order to let mUserData be shared by all
8784 * snapshot machine instances.
8785 */
8786 mUserData.commitCopy();
8787
8788 // machine registry, if present (must be loaded before snapshots)
8789 if (config.canHaveOwnMediaRegistry())
8790 {
8791 // determine machine folder
8792 Utf8Str strMachineFolder = getSettingsFileFull();
8793 strMachineFolder.stripFilename();
8794 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8795 config.mediaRegistry,
8796 strMachineFolder);
8797 if (FAILED(rc)) return rc;
8798 }
8799
8800 /* Snapshot node (optional) */
8801 size_t cRootSnapshots;
8802 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8803 {
8804 // there must be only one root snapshot
8805 Assert(cRootSnapshots == 1);
8806
8807 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8808
8809 rc = loadSnapshot(snap,
8810 config.uuidCurrentSnapshot,
8811 NULL); // no parent == first snapshot
8812 if (FAILED(rc)) return rc;
8813 }
8814
8815 // hardware data
8816 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8817 if (FAILED(rc)) return rc;
8818
8819 // load storage controllers
8820 rc = loadStorageControllers(config.storageMachine,
8821 puuidRegistry,
8822 NULL /* puuidSnapshot */);
8823 if (FAILED(rc)) return rc;
8824
8825 /*
8826 * NOTE: the assignment below must be the last thing to do,
8827 * otherwise it will be not possible to change the settings
8828 * somewhere in the code above because all setters will be
8829 * blocked by checkStateDependency(MutableStateDep).
8830 */
8831
8832 /* set the machine state to Aborted or Saved when appropriate */
8833 if (config.fAborted)
8834 {
8835 mSSData->strStateFilePath.setNull();
8836
8837 /* no need to use setMachineState() during init() */
8838 mData->mMachineState = MachineState_Aborted;
8839 }
8840 else if (!mSSData->strStateFilePath.isEmpty())
8841 {
8842 /* no need to use setMachineState() during init() */
8843 mData->mMachineState = MachineState_Saved;
8844 }
8845
8846 // after loading settings, we are no longer different from the XML on disk
8847 mData->flModifications = 0;
8848
8849 return S_OK;
8850}
8851
8852/**
8853 * Recursively loads all snapshots starting from the given.
8854 *
8855 * @param aNode <Snapshot> node.
8856 * @param aCurSnapshotId Current snapshot ID from the settings file.
8857 * @param aParentSnapshot Parent snapshot.
8858 */
8859HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8860 const Guid &aCurSnapshotId,
8861 Snapshot *aParentSnapshot)
8862{
8863 AssertReturn(!isSnapshotMachine(), E_FAIL);
8864 AssertReturn(!isSessionMachine(), E_FAIL);
8865
8866 HRESULT rc = S_OK;
8867
8868 Utf8Str strStateFile;
8869 if (!data.strStateFile.isEmpty())
8870 {
8871 /* optional */
8872 strStateFile = data.strStateFile;
8873 int vrc = calculateFullPath(strStateFile, strStateFile);
8874 if (RT_FAILURE(vrc))
8875 return setError(E_FAIL,
8876 tr("Invalid saved state file path '%s' (%Rrc)"),
8877 strStateFile.c_str(),
8878 vrc);
8879 }
8880
8881 /* create a snapshot machine object */
8882 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8883 pSnapshotMachine.createObject();
8884 rc = pSnapshotMachine->initFromSettings(this,
8885 data.hardware,
8886 &data.debugging,
8887 &data.autostart,
8888 data.storage,
8889 data.uuid.ref(),
8890 strStateFile);
8891 if (FAILED(rc)) return rc;
8892
8893 /* create a snapshot object */
8894 ComObjPtr<Snapshot> pSnapshot;
8895 pSnapshot.createObject();
8896 /* initialize the snapshot */
8897 rc = pSnapshot->init(mParent, // VirtualBox object
8898 data.uuid,
8899 data.strName,
8900 data.strDescription,
8901 data.timestamp,
8902 pSnapshotMachine,
8903 aParentSnapshot);
8904 if (FAILED(rc)) return rc;
8905
8906 /* memorize the first snapshot if necessary */
8907 if (!mData->mFirstSnapshot)
8908 mData->mFirstSnapshot = pSnapshot;
8909
8910 /* memorize the current snapshot when appropriate */
8911 if ( !mData->mCurrentSnapshot
8912 && pSnapshot->getId() == aCurSnapshotId
8913 )
8914 mData->mCurrentSnapshot = pSnapshot;
8915
8916 // now create the children
8917 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8918 it != data.llChildSnapshots.end();
8919 ++it)
8920 {
8921 const settings::Snapshot &childData = *it;
8922 // recurse
8923 rc = loadSnapshot(childData,
8924 aCurSnapshotId,
8925 pSnapshot); // parent = the one we created above
8926 if (FAILED(rc)) return rc;
8927 }
8928
8929 return rc;
8930}
8931
8932/**
8933 * Loads settings into mHWData.
8934 *
8935 * @param data Reference to the hardware settings.
8936 * @param pDbg Pointer to the debugging settings.
8937 * @param pAutostart Pointer to the autostart settings.
8938 */
8939HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8940 const settings::Autostart *pAutostart)
8941{
8942 AssertReturn(!isSessionMachine(), E_FAIL);
8943
8944 HRESULT rc = S_OK;
8945
8946 try
8947 {
8948 /* The hardware version attribute (optional). */
8949 mHWData->mHWVersion = data.strVersion;
8950 mHWData->mHardwareUUID = data.uuid;
8951
8952 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8953 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8954 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8955 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8956 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8957 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8958 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8959 mHWData->mPAEEnabled = data.fPAE;
8960 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8961 mHWData->mLongMode = data.enmLongMode;
8962 mHWData->mCPUCount = data.cCPUs;
8963 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8964 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8965
8966 // cpu
8967 if (mHWData->mCPUHotPlugEnabled)
8968 {
8969 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8970 it != data.llCpus.end();
8971 ++it)
8972 {
8973 const settings::Cpu &cpu = *it;
8974
8975 mHWData->mCPUAttached[cpu.ulId] = true;
8976 }
8977 }
8978
8979 // cpuid leafs
8980 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8981 it != data.llCpuIdLeafs.end();
8982 ++it)
8983 {
8984 const settings::CpuIdLeaf &leaf = *it;
8985
8986 switch (leaf.ulId)
8987 {
8988 case 0x0:
8989 case 0x1:
8990 case 0x2:
8991 case 0x3:
8992 case 0x4:
8993 case 0x5:
8994 case 0x6:
8995 case 0x7:
8996 case 0x8:
8997 case 0x9:
8998 case 0xA:
8999 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9000 break;
9001
9002 case 0x80000000:
9003 case 0x80000001:
9004 case 0x80000002:
9005 case 0x80000003:
9006 case 0x80000004:
9007 case 0x80000005:
9008 case 0x80000006:
9009 case 0x80000007:
9010 case 0x80000008:
9011 case 0x80000009:
9012 case 0x8000000A:
9013 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9014 break;
9015
9016 default:
9017 /* just ignore */
9018 break;
9019 }
9020 }
9021
9022 mHWData->mMemorySize = data.ulMemorySizeMB;
9023 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9024
9025 // boot order
9026 for (size_t i = 0;
9027 i < RT_ELEMENTS(mHWData->mBootOrder);
9028 i++)
9029 {
9030 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9031 if (it == data.mapBootOrder.end())
9032 mHWData->mBootOrder[i] = DeviceType_Null;
9033 else
9034 mHWData->mBootOrder[i] = it->second;
9035 }
9036
9037 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9038 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9039 mHWData->mMonitorCount = data.cMonitors;
9040 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9041 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9042 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9043 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9044 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9045 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9046 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9047 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9048 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9049 mHWData->mVideoCaptureFps = data.ulVideoCaptureFps;
9050 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
9051 mHWData->mFirmwareType = data.firmwareType;
9052 mHWData->mPointingHIDType = data.pointingHIDType;
9053 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9054 mHWData->mChipsetType = data.chipsetType;
9055 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9056 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9057 mHWData->mHPETEnabled = data.fHPETEnabled;
9058
9059 /* VRDEServer */
9060 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9061 if (FAILED(rc)) return rc;
9062
9063 /* BIOS */
9064 rc = mBIOSSettings->loadSettings(data.biosSettings);
9065 if (FAILED(rc)) return rc;
9066
9067 // Bandwidth control (must come before network adapters)
9068 rc = mBandwidthControl->loadSettings(data.ioSettings);
9069 if (FAILED(rc)) return rc;
9070
9071 /* USB Controller */
9072 rc = mUSBController->loadSettings(data.usbController);
9073 if (FAILED(rc)) return rc;
9074
9075 // network adapters
9076 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9077 uint32_t oldCount = mNetworkAdapters.size();
9078 if (newCount > oldCount)
9079 {
9080 mNetworkAdapters.resize(newCount);
9081 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9082 {
9083 unconst(mNetworkAdapters[slot]).createObject();
9084 mNetworkAdapters[slot]->init(this, slot);
9085 }
9086 }
9087 else if (newCount < oldCount)
9088 mNetworkAdapters.resize(newCount);
9089 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9090 it != data.llNetworkAdapters.end();
9091 ++it)
9092 {
9093 const settings::NetworkAdapter &nic = *it;
9094
9095 /* slot unicity is guaranteed by XML Schema */
9096 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9097 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9098 if (FAILED(rc)) return rc;
9099 }
9100
9101 // serial ports
9102 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9103 it != data.llSerialPorts.end();
9104 ++it)
9105 {
9106 const settings::SerialPort &s = *it;
9107
9108 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9109 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9110 if (FAILED(rc)) return rc;
9111 }
9112
9113 // parallel ports (optional)
9114 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9115 it != data.llParallelPorts.end();
9116 ++it)
9117 {
9118 const settings::ParallelPort &p = *it;
9119
9120 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9121 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9122 if (FAILED(rc)) return rc;
9123 }
9124
9125 /* AudioAdapter */
9126 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9127 if (FAILED(rc)) return rc;
9128
9129 /* Shared folders */
9130 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9131 it != data.llSharedFolders.end();
9132 ++it)
9133 {
9134 const settings::SharedFolder &sf = *it;
9135
9136 ComObjPtr<SharedFolder> sharedFolder;
9137 /* Check for double entries. Not allowed! */
9138 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9139 if (SUCCEEDED(rc))
9140 return setError(VBOX_E_OBJECT_IN_USE,
9141 tr("Shared folder named '%s' already exists"),
9142 sf.strName.c_str());
9143
9144 /* Create the new shared folder. Don't break on error. This will be
9145 * reported when the machine starts. */
9146 sharedFolder.createObject();
9147 rc = sharedFolder->init(getMachine(),
9148 sf.strName,
9149 sf.strHostPath,
9150 RT_BOOL(sf.fWritable),
9151 RT_BOOL(sf.fAutoMount),
9152 false /* fFailOnError */);
9153 if (FAILED(rc)) return rc;
9154 mHWData->mSharedFolders.push_back(sharedFolder);
9155 }
9156
9157 // Clipboard
9158 mHWData->mClipboardMode = data.clipboardMode;
9159
9160 // drag'n'drop
9161 mHWData->mDragAndDropMode = data.dragAndDropMode;
9162
9163 // guest settings
9164 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9165
9166 // IO settings
9167 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9168 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9169
9170 // Host PCI devices
9171 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9172 it != data.pciAttachments.end();
9173 ++it)
9174 {
9175 const settings::HostPCIDeviceAttachment &hpda = *it;
9176 ComObjPtr<PCIDeviceAttachment> pda;
9177
9178 pda.createObject();
9179 pda->loadSettings(this, hpda);
9180 mHWData->mPCIDeviceAssignments.push_back(pda);
9181 }
9182
9183 /*
9184 * (The following isn't really real hardware, but it lives in HWData
9185 * for reasons of convenience.)
9186 */
9187
9188#ifdef VBOX_WITH_GUEST_PROPS
9189 /* Guest properties (optional) */
9190 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9191 it != data.llGuestProperties.end();
9192 ++it)
9193 {
9194 const settings::GuestProperty &prop = *it;
9195 uint32_t fFlags = guestProp::NILFLAG;
9196 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9197 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9198 mHWData->mGuestProperties[prop.strName] = property;
9199 }
9200
9201 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9202#endif /* VBOX_WITH_GUEST_PROPS defined */
9203
9204 rc = loadDebugging(pDbg);
9205 if (FAILED(rc))
9206 return rc;
9207
9208 mHWData->mAutostart = *pAutostart;
9209
9210 /* default frontend */
9211 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9212 }
9213 catch(std::bad_alloc &)
9214 {
9215 return E_OUTOFMEMORY;
9216 }
9217
9218 AssertComRC(rc);
9219 return rc;
9220}
9221
9222/**
9223 * Called from Machine::loadHardware() to load the debugging settings of the
9224 * machine.
9225 *
9226 * @param pDbg Pointer to the settings.
9227 */
9228HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9229{
9230 mHWData->mDebugging = *pDbg;
9231 /* no more processing currently required, this will probably change. */
9232 return S_OK;
9233}
9234
9235/**
9236 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9237 *
9238 * @param data
9239 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9240 * @param puuidSnapshot
9241 * @return
9242 */
9243HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9244 const Guid *puuidRegistry,
9245 const Guid *puuidSnapshot)
9246{
9247 AssertReturn(!isSessionMachine(), E_FAIL);
9248
9249 HRESULT rc = S_OK;
9250
9251 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9252 it != data.llStorageControllers.end();
9253 ++it)
9254 {
9255 const settings::StorageController &ctlData = *it;
9256
9257 ComObjPtr<StorageController> pCtl;
9258 /* Try to find one with the name first. */
9259 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9260 if (SUCCEEDED(rc))
9261 return setError(VBOX_E_OBJECT_IN_USE,
9262 tr("Storage controller named '%s' already exists"),
9263 ctlData.strName.c_str());
9264
9265 pCtl.createObject();
9266 rc = pCtl->init(this,
9267 ctlData.strName,
9268 ctlData.storageBus,
9269 ctlData.ulInstance,
9270 ctlData.fBootable);
9271 if (FAILED(rc)) return rc;
9272
9273 mStorageControllers->push_back(pCtl);
9274
9275 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9276 if (FAILED(rc)) return rc;
9277
9278 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9279 if (FAILED(rc)) return rc;
9280
9281 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9282 if (FAILED(rc)) return rc;
9283
9284 /* Set IDE emulation settings (only for AHCI controller). */
9285 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9286 {
9287 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9288 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9289 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9290 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9291 )
9292 return rc;
9293 }
9294
9295 /* Load the attached devices now. */
9296 rc = loadStorageDevices(pCtl,
9297 ctlData,
9298 puuidRegistry,
9299 puuidSnapshot);
9300 if (FAILED(rc)) return rc;
9301 }
9302
9303 return S_OK;
9304}
9305
9306/**
9307 * Called from loadStorageControllers for a controller's devices.
9308 *
9309 * @param aStorageController
9310 * @param data
9311 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9312 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9313 * @return
9314 */
9315HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9316 const settings::StorageController &data,
9317 const Guid *puuidRegistry,
9318 const Guid *puuidSnapshot)
9319{
9320 HRESULT rc = S_OK;
9321
9322 /* paranoia: detect duplicate attachments */
9323 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9324 it != data.llAttachedDevices.end();
9325 ++it)
9326 {
9327 const settings::AttachedDevice &ad = *it;
9328
9329 for (settings::AttachedDevicesList::const_iterator it2 = it;
9330 it2 != data.llAttachedDevices.end();
9331 ++it2)
9332 {
9333 if (it == it2)
9334 continue;
9335
9336 const settings::AttachedDevice &ad2 = *it2;
9337
9338 if ( ad.lPort == ad2.lPort
9339 && ad.lDevice == ad2.lDevice)
9340 {
9341 return setError(E_FAIL,
9342 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9343 aStorageController->getName().c_str(),
9344 ad.lPort,
9345 ad.lDevice,
9346 mUserData->s.strName.c_str());
9347 }
9348 }
9349 }
9350
9351 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9352 it != data.llAttachedDevices.end();
9353 ++it)
9354 {
9355 const settings::AttachedDevice &dev = *it;
9356 ComObjPtr<Medium> medium;
9357
9358 switch (dev.deviceType)
9359 {
9360 case DeviceType_Floppy:
9361 case DeviceType_DVD:
9362 if (dev.strHostDriveSrc.isNotEmpty())
9363 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9364 else
9365 rc = mParent->findRemoveableMedium(dev.deviceType,
9366 dev.uuid,
9367 false /* fRefresh */,
9368 false /* aSetError */,
9369 medium);
9370 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9371 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9372 rc = S_OK;
9373 break;
9374
9375 case DeviceType_HardDisk:
9376 {
9377 /* find a hard disk by UUID */
9378 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9379 if (FAILED(rc))
9380 {
9381 if (isSnapshotMachine())
9382 {
9383 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9384 // so the user knows that the bad disk is in a snapshot somewhere
9385 com::ErrorInfo info;
9386 return setError(E_FAIL,
9387 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9388 puuidSnapshot->raw(),
9389 info.getText().raw());
9390 }
9391 else
9392 return rc;
9393 }
9394
9395 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9396
9397 if (medium->getType() == MediumType_Immutable)
9398 {
9399 if (isSnapshotMachine())
9400 return setError(E_FAIL,
9401 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9402 "of the virtual machine '%s' ('%s')"),
9403 medium->getLocationFull().c_str(),
9404 dev.uuid.raw(),
9405 puuidSnapshot->raw(),
9406 mUserData->s.strName.c_str(),
9407 mData->m_strConfigFileFull.c_str());
9408
9409 return setError(E_FAIL,
9410 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9411 medium->getLocationFull().c_str(),
9412 dev.uuid.raw(),
9413 mUserData->s.strName.c_str(),
9414 mData->m_strConfigFileFull.c_str());
9415 }
9416
9417 if (medium->getType() == MediumType_MultiAttach)
9418 {
9419 if (isSnapshotMachine())
9420 return setError(E_FAIL,
9421 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9422 "of the virtual machine '%s' ('%s')"),
9423 medium->getLocationFull().c_str(),
9424 dev.uuid.raw(),
9425 puuidSnapshot->raw(),
9426 mUserData->s.strName.c_str(),
9427 mData->m_strConfigFileFull.c_str());
9428
9429 return setError(E_FAIL,
9430 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9431 medium->getLocationFull().c_str(),
9432 dev.uuid.raw(),
9433 mUserData->s.strName.c_str(),
9434 mData->m_strConfigFileFull.c_str());
9435 }
9436
9437 if ( !isSnapshotMachine()
9438 && medium->getChildren().size() != 0
9439 )
9440 return setError(E_FAIL,
9441 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9442 "because it has %d differencing child hard disks"),
9443 medium->getLocationFull().c_str(),
9444 dev.uuid.raw(),
9445 mUserData->s.strName.c_str(),
9446 mData->m_strConfigFileFull.c_str(),
9447 medium->getChildren().size());
9448
9449 if (findAttachment(mMediaData->mAttachments,
9450 medium))
9451 return setError(E_FAIL,
9452 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9453 medium->getLocationFull().c_str(),
9454 dev.uuid.raw(),
9455 mUserData->s.strName.c_str(),
9456 mData->m_strConfigFileFull.c_str());
9457
9458 break;
9459 }
9460
9461 default:
9462 return setError(E_FAIL,
9463 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9464 medium->getLocationFull().c_str(),
9465 mUserData->s.strName.c_str(),
9466 mData->m_strConfigFileFull.c_str());
9467 }
9468
9469 if (FAILED(rc))
9470 break;
9471
9472 /* Bandwidth groups are loaded at this point. */
9473 ComObjPtr<BandwidthGroup> pBwGroup;
9474
9475 if (!dev.strBwGroup.isEmpty())
9476 {
9477 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9478 if (FAILED(rc))
9479 return setError(E_FAIL,
9480 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9481 medium->getLocationFull().c_str(),
9482 dev.strBwGroup.c_str(),
9483 mUserData->s.strName.c_str(),
9484 mData->m_strConfigFileFull.c_str());
9485 pBwGroup->reference();
9486 }
9487
9488 const Bstr controllerName = aStorageController->getName();
9489 ComObjPtr<MediumAttachment> pAttachment;
9490 pAttachment.createObject();
9491 rc = pAttachment->init(this,
9492 medium,
9493 controllerName,
9494 dev.lPort,
9495 dev.lDevice,
9496 dev.deviceType,
9497 false,
9498 dev.fPassThrough,
9499 dev.fTempEject,
9500 dev.fNonRotational,
9501 dev.fDiscard,
9502 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9503 if (FAILED(rc)) break;
9504
9505 /* associate the medium with this machine and snapshot */
9506 if (!medium.isNull())
9507 {
9508 AutoCaller medCaller(medium);
9509 if (FAILED(medCaller.rc())) return medCaller.rc();
9510 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9511
9512 if (isSnapshotMachine())
9513 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9514 else
9515 rc = medium->addBackReference(mData->mUuid);
9516 /* If the medium->addBackReference fails it sets an appropriate
9517 * error message, so no need to do any guesswork here. */
9518
9519 if (puuidRegistry)
9520 // caller wants registry ID to be set on all attached media (OVF import case)
9521 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9522 }
9523
9524 if (FAILED(rc))
9525 break;
9526
9527 /* back up mMediaData to let registeredInit() properly rollback on failure
9528 * (= limited accessibility) */
9529 setModified(IsModified_Storage);
9530 mMediaData.backup();
9531 mMediaData->mAttachments.push_back(pAttachment);
9532 }
9533
9534 return rc;
9535}
9536
9537/**
9538 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9539 *
9540 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9541 * @param aSnapshot where to return the found snapshot
9542 * @param aSetError true to set extended error info on failure
9543 */
9544HRESULT Machine::findSnapshotById(const Guid &aId,
9545 ComObjPtr<Snapshot> &aSnapshot,
9546 bool aSetError /* = false */)
9547{
9548 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9549
9550 if (!mData->mFirstSnapshot)
9551 {
9552 if (aSetError)
9553 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9554 return E_FAIL;
9555 }
9556
9557 if (aId.isZero())
9558 aSnapshot = mData->mFirstSnapshot;
9559 else
9560 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9561
9562 if (!aSnapshot)
9563 {
9564 if (aSetError)
9565 return setError(E_FAIL,
9566 tr("Could not find a snapshot with UUID {%s}"),
9567 aId.toString().c_str());
9568 return E_FAIL;
9569 }
9570
9571 return S_OK;
9572}
9573
9574/**
9575 * Returns the snapshot with the given name or fails of no such snapshot.
9576 *
9577 * @param aName snapshot name to find
9578 * @param aSnapshot where to return the found snapshot
9579 * @param aSetError true to set extended error info on failure
9580 */
9581HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9582 ComObjPtr<Snapshot> &aSnapshot,
9583 bool aSetError /* = false */)
9584{
9585 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9586
9587 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9588
9589 if (!mData->mFirstSnapshot)
9590 {
9591 if (aSetError)
9592 return setError(VBOX_E_OBJECT_NOT_FOUND,
9593 tr("This machine does not have any snapshots"));
9594 return VBOX_E_OBJECT_NOT_FOUND;
9595 }
9596
9597 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9598
9599 if (!aSnapshot)
9600 {
9601 if (aSetError)
9602 return setError(VBOX_E_OBJECT_NOT_FOUND,
9603 tr("Could not find a snapshot named '%s'"), strName.c_str());
9604 return VBOX_E_OBJECT_NOT_FOUND;
9605 }
9606
9607 return S_OK;
9608}
9609
9610/**
9611 * Returns a storage controller object with the given name.
9612 *
9613 * @param aName storage controller name to find
9614 * @param aStorageController where to return the found storage controller
9615 * @param aSetError true to set extended error info on failure
9616 */
9617HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9618 ComObjPtr<StorageController> &aStorageController,
9619 bool aSetError /* = false */)
9620{
9621 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9622
9623 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9624 it != mStorageControllers->end();
9625 ++it)
9626 {
9627 if ((*it)->getName() == aName)
9628 {
9629 aStorageController = (*it);
9630 return S_OK;
9631 }
9632 }
9633
9634 if (aSetError)
9635 return setError(VBOX_E_OBJECT_NOT_FOUND,
9636 tr("Could not find a storage controller named '%s'"),
9637 aName.c_str());
9638 return VBOX_E_OBJECT_NOT_FOUND;
9639}
9640
9641HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9642 MediaData::AttachmentList &atts)
9643{
9644 AutoCaller autoCaller(this);
9645 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9646
9647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9648
9649 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9650 it != mMediaData->mAttachments.end();
9651 ++it)
9652 {
9653 const ComObjPtr<MediumAttachment> &pAtt = *it;
9654
9655 // should never happen, but deal with NULL pointers in the list.
9656 AssertStmt(!pAtt.isNull(), continue);
9657
9658 // getControllerName() needs caller+read lock
9659 AutoCaller autoAttCaller(pAtt);
9660 if (FAILED(autoAttCaller.rc()))
9661 {
9662 atts.clear();
9663 return autoAttCaller.rc();
9664 }
9665 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9666
9667 if (pAtt->getControllerName() == aName)
9668 atts.push_back(pAtt);
9669 }
9670
9671 return S_OK;
9672}
9673
9674/**
9675 * Helper for #saveSettings. Cares about renaming the settings directory and
9676 * file if the machine name was changed and about creating a new settings file
9677 * if this is a new machine.
9678 *
9679 * @note Must be never called directly but only from #saveSettings().
9680 */
9681HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9682{
9683 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9684
9685 HRESULT rc = S_OK;
9686
9687 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9688
9689 /// @todo need to handle primary group change, too
9690
9691 /* attempt to rename the settings file if machine name is changed */
9692 if ( mUserData->s.fNameSync
9693 && mUserData.isBackedUp()
9694 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9695 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9696 )
9697 {
9698 bool dirRenamed = false;
9699 bool fileRenamed = false;
9700
9701 Utf8Str configFile, newConfigFile;
9702 Utf8Str configFilePrev, newConfigFilePrev;
9703 Utf8Str configDir, newConfigDir;
9704
9705 do
9706 {
9707 int vrc = VINF_SUCCESS;
9708
9709 Utf8Str name = mUserData.backedUpData()->s.strName;
9710 Utf8Str newName = mUserData->s.strName;
9711 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9712 if (group == "/")
9713 group.setNull();
9714 Utf8Str newGroup = mUserData->s.llGroups.front();
9715 if (newGroup == "/")
9716 newGroup.setNull();
9717
9718 configFile = mData->m_strConfigFileFull;
9719
9720 /* first, rename the directory if it matches the group and machine name */
9721 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9722 group.c_str(), RTPATH_DELIMITER, name.c_str());
9723 /** @todo hack, make somehow use of ComposeMachineFilename */
9724 if (mUserData->s.fDirectoryIncludesUUID)
9725 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9726 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9727 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9728 /** @todo hack, make somehow use of ComposeMachineFilename */
9729 if (mUserData->s.fDirectoryIncludesUUID)
9730 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9731 configDir = configFile;
9732 configDir.stripFilename();
9733 newConfigDir = configDir;
9734 if ( configDir.length() >= groupPlusName.length()
9735 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9736 {
9737 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9738 Utf8Str newConfigBaseDir(newConfigDir);
9739 newConfigDir.append(newGroupPlusName);
9740 /* consistency: use \ if appropriate on the platform */
9741 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9742 /* new dir and old dir cannot be equal here because of 'if'
9743 * above and because name != newName */
9744 Assert(configDir != newConfigDir);
9745 if (!fSettingsFileIsNew)
9746 {
9747 /* perform real rename only if the machine is not new */
9748 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9749 if ( vrc == VERR_FILE_NOT_FOUND
9750 || vrc == VERR_PATH_NOT_FOUND)
9751 {
9752 /* create the parent directory, then retry renaming */
9753 Utf8Str parent(newConfigDir);
9754 parent.stripFilename();
9755 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9756 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9757 }
9758 if (RT_FAILURE(vrc))
9759 {
9760 rc = setError(E_FAIL,
9761 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9762 configDir.c_str(),
9763 newConfigDir.c_str(),
9764 vrc);
9765 break;
9766 }
9767 /* delete subdirectories which are no longer needed */
9768 Utf8Str dir(configDir);
9769 dir.stripFilename();
9770 while (dir != newConfigBaseDir && dir != ".")
9771 {
9772 vrc = RTDirRemove(dir.c_str());
9773 if (RT_FAILURE(vrc))
9774 break;
9775 dir.stripFilename();
9776 }
9777 dirRenamed = true;
9778 }
9779 }
9780
9781 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9782 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9783
9784 /* then try to rename the settings file itself */
9785 if (newConfigFile != configFile)
9786 {
9787 /* get the path to old settings file in renamed directory */
9788 configFile = Utf8StrFmt("%s%c%s",
9789 newConfigDir.c_str(),
9790 RTPATH_DELIMITER,
9791 RTPathFilename(configFile.c_str()));
9792 if (!fSettingsFileIsNew)
9793 {
9794 /* perform real rename only if the machine is not new */
9795 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9796 if (RT_FAILURE(vrc))
9797 {
9798 rc = setError(E_FAIL,
9799 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9800 configFile.c_str(),
9801 newConfigFile.c_str(),
9802 vrc);
9803 break;
9804 }
9805 fileRenamed = true;
9806 configFilePrev = configFile;
9807 configFilePrev += "-prev";
9808 newConfigFilePrev = newConfigFile;
9809 newConfigFilePrev += "-prev";
9810 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9811 }
9812 }
9813
9814 // update m_strConfigFileFull amd mConfigFile
9815 mData->m_strConfigFileFull = newConfigFile;
9816 // compute the relative path too
9817 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9818
9819 // store the old and new so that VirtualBox::saveSettings() can update
9820 // the media registry
9821 if ( mData->mRegistered
9822 && configDir != newConfigDir)
9823 {
9824 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9825
9826 if (pfNeedsGlobalSaveSettings)
9827 *pfNeedsGlobalSaveSettings = true;
9828 }
9829
9830 // in the saved state file path, replace the old directory with the new directory
9831 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9832 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9833
9834 // and do the same thing for the saved state file paths of all the online snapshots
9835 if (mData->mFirstSnapshot)
9836 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9837 newConfigDir.c_str());
9838 }
9839 while (0);
9840
9841 if (FAILED(rc))
9842 {
9843 /* silently try to rename everything back */
9844 if (fileRenamed)
9845 {
9846 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9847 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9848 }
9849 if (dirRenamed)
9850 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9851 }
9852
9853 if (FAILED(rc)) return rc;
9854 }
9855
9856 if (fSettingsFileIsNew)
9857 {
9858 /* create a virgin config file */
9859 int vrc = VINF_SUCCESS;
9860
9861 /* ensure the settings directory exists */
9862 Utf8Str path(mData->m_strConfigFileFull);
9863 path.stripFilename();
9864 if (!RTDirExists(path.c_str()))
9865 {
9866 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9867 if (RT_FAILURE(vrc))
9868 {
9869 return setError(E_FAIL,
9870 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9871 path.c_str(),
9872 vrc);
9873 }
9874 }
9875
9876 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9877 path = Utf8Str(mData->m_strConfigFileFull);
9878 RTFILE f = NIL_RTFILE;
9879 vrc = RTFileOpen(&f, path.c_str(),
9880 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9881 if (RT_FAILURE(vrc))
9882 return setError(E_FAIL,
9883 tr("Could not create the settings file '%s' (%Rrc)"),
9884 path.c_str(),
9885 vrc);
9886 RTFileClose(f);
9887 }
9888
9889 return rc;
9890}
9891
9892/**
9893 * Saves and commits machine data, user data and hardware data.
9894 *
9895 * Note that on failure, the data remains uncommitted.
9896 *
9897 * @a aFlags may combine the following flags:
9898 *
9899 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9900 * Used when saving settings after an operation that makes them 100%
9901 * correspond to the settings from the current snapshot.
9902 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9903 * #isReallyModified() returns false. This is necessary for cases when we
9904 * change machine data directly, not through the backup()/commit() mechanism.
9905 * - SaveS_Force: settings will be saved without doing a deep compare of the
9906 * settings structures. This is used when this is called because snapshots
9907 * have changed to avoid the overhead of the deep compare.
9908 *
9909 * @note Must be called from under this object's write lock. Locks children for
9910 * writing.
9911 *
9912 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9913 * initialized to false and that will be set to true by this function if
9914 * the caller must invoke VirtualBox::saveSettings() because the global
9915 * settings have changed. This will happen if a machine rename has been
9916 * saved and the global machine and media registries will therefore need
9917 * updating.
9918 */
9919HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9920 int aFlags /*= 0*/)
9921{
9922 LogFlowThisFuncEnter();
9923
9924 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9925
9926 /* make sure child objects are unable to modify the settings while we are
9927 * saving them */
9928 ensureNoStateDependencies();
9929
9930 AssertReturn(!isSnapshotMachine(),
9931 E_FAIL);
9932
9933 HRESULT rc = S_OK;
9934 bool fNeedsWrite = false;
9935
9936 /* First, prepare to save settings. It will care about renaming the
9937 * settings directory and file if the machine name was changed and about
9938 * creating a new settings file if this is a new machine. */
9939 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9940 if (FAILED(rc)) return rc;
9941
9942 // keep a pointer to the current settings structures
9943 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9944 settings::MachineConfigFile *pNewConfig = NULL;
9945
9946 try
9947 {
9948 // make a fresh one to have everyone write stuff into
9949 pNewConfig = new settings::MachineConfigFile(NULL);
9950 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9951
9952 // now go and copy all the settings data from COM to the settings structures
9953 // (this calles saveSettings() on all the COM objects in the machine)
9954 copyMachineDataToSettings(*pNewConfig);
9955
9956 if (aFlags & SaveS_ResetCurStateModified)
9957 {
9958 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9959 mData->mCurrentStateModified = FALSE;
9960 fNeedsWrite = true; // always, no need to compare
9961 }
9962 else if (aFlags & SaveS_Force)
9963 {
9964 fNeedsWrite = true; // always, no need to compare
9965 }
9966 else
9967 {
9968 if (!mData->mCurrentStateModified)
9969 {
9970 // do a deep compare of the settings that we just saved with the settings
9971 // previously stored in the config file; this invokes MachineConfigFile::operator==
9972 // which does a deep compare of all the settings, which is expensive but less expensive
9973 // than writing out XML in vain
9974 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9975
9976 // could still be modified if any settings changed
9977 mData->mCurrentStateModified = fAnySettingsChanged;
9978
9979 fNeedsWrite = fAnySettingsChanged;
9980 }
9981 else
9982 fNeedsWrite = true;
9983 }
9984
9985 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9986
9987 if (fNeedsWrite)
9988 // now spit it all out!
9989 pNewConfig->write(mData->m_strConfigFileFull);
9990
9991 mData->pMachineConfigFile = pNewConfig;
9992 delete pOldConfig;
9993 commit();
9994
9995 // after saving settings, we are no longer different from the XML on disk
9996 mData->flModifications = 0;
9997 }
9998 catch (HRESULT err)
9999 {
10000 // we assume that error info is set by the thrower
10001 rc = err;
10002
10003 // restore old config
10004 delete pNewConfig;
10005 mData->pMachineConfigFile = pOldConfig;
10006 }
10007 catch (...)
10008 {
10009 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10010 }
10011
10012 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10013 {
10014 /* Fire the data change event, even on failure (since we've already
10015 * committed all data). This is done only for SessionMachines because
10016 * mutable Machine instances are always not registered (i.e. private
10017 * to the client process that creates them) and thus don't need to
10018 * inform callbacks. */
10019 if (isSessionMachine())
10020 mParent->onMachineDataChange(mData->mUuid);
10021 }
10022
10023 LogFlowThisFunc(("rc=%08X\n", rc));
10024 LogFlowThisFuncLeave();
10025 return rc;
10026}
10027
10028/**
10029 * Implementation for saving the machine settings into the given
10030 * settings::MachineConfigFile instance. This copies machine extradata
10031 * from the previous machine config file in the instance data, if any.
10032 *
10033 * This gets called from two locations:
10034 *
10035 * -- Machine::saveSettings(), during the regular XML writing;
10036 *
10037 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10038 * exported to OVF and we write the VirtualBox proprietary XML
10039 * into a <vbox:Machine> tag.
10040 *
10041 * This routine fills all the fields in there, including snapshots, *except*
10042 * for the following:
10043 *
10044 * -- fCurrentStateModified. There is some special logic associated with that.
10045 *
10046 * The caller can then call MachineConfigFile::write() or do something else
10047 * with it.
10048 *
10049 * Caller must hold the machine lock!
10050 *
10051 * This throws XML errors and HRESULT, so the caller must have a catch block!
10052 */
10053void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10054{
10055 // deep copy extradata
10056 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10057
10058 config.uuid = mData->mUuid;
10059
10060 // copy name, description, OS type, teleport, UTC etc.
10061 config.machineUserData = mUserData->s;
10062
10063 // Encode the Icon Override data from Machine and store on config userdata.
10064 com::SafeArray<BYTE> iconByte;
10065 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10066 ssize_t cbData = iconByte.size();
10067 if (cbData > 0)
10068 {
10069 ssize_t cchOut = RTBase64EncodedLength(cbData);
10070 Utf8Str strIconData;
10071 strIconData.reserve(cchOut+1);
10072 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10073 strIconData.mutableRaw(), strIconData.capacity(),
10074 NULL);
10075 if (RT_FAILURE(vrc))
10076 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10077 strIconData.jolt();
10078 config.machineUserData.ovIcon = strIconData;
10079 }
10080 else
10081 config.machineUserData.ovIcon.setNull();
10082
10083 if ( mData->mMachineState == MachineState_Saved
10084 || mData->mMachineState == MachineState_Restoring
10085 // when deleting a snapshot we may or may not have a saved state in the current state,
10086 // so let's not assert here please
10087 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10088 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10089 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10090 && (!mSSData->strStateFilePath.isEmpty())
10091 )
10092 )
10093 {
10094 Assert(!mSSData->strStateFilePath.isEmpty());
10095 /* try to make the file name relative to the settings file dir */
10096 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10097 }
10098 else
10099 {
10100 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10101 config.strStateFile.setNull();
10102 }
10103
10104 if (mData->mCurrentSnapshot)
10105 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10106 else
10107 config.uuidCurrentSnapshot.clear();
10108
10109 config.timeLastStateChange = mData->mLastStateChange;
10110 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10111 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10112
10113 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10114 if (FAILED(rc)) throw rc;
10115
10116 rc = saveStorageControllers(config.storageMachine);
10117 if (FAILED(rc)) throw rc;
10118
10119 // save machine's media registry if this is VirtualBox 4.0 or later
10120 if (config.canHaveOwnMediaRegistry())
10121 {
10122 // determine machine folder
10123 Utf8Str strMachineFolder = getSettingsFileFull();
10124 strMachineFolder.stripFilename();
10125 mParent->saveMediaRegistry(config.mediaRegistry,
10126 getId(), // only media with registry ID == machine UUID
10127 strMachineFolder);
10128 // this throws HRESULT
10129 }
10130
10131 // save snapshots
10132 rc = saveAllSnapshots(config);
10133 if (FAILED(rc)) throw rc;
10134}
10135
10136/**
10137 * Saves all snapshots of the machine into the given machine config file. Called
10138 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10139 * @param config
10140 * @return
10141 */
10142HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10143{
10144 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10145
10146 HRESULT rc = S_OK;
10147
10148 try
10149 {
10150 config.llFirstSnapshot.clear();
10151
10152 if (mData->mFirstSnapshot)
10153 {
10154 settings::Snapshot snapNew;
10155 config.llFirstSnapshot.push_back(snapNew);
10156
10157 // get reference to the fresh copy of the snapshot on the list and
10158 // work on that copy directly to avoid excessive copying later
10159 settings::Snapshot &snap = config.llFirstSnapshot.front();
10160
10161 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10162 if (FAILED(rc)) throw rc;
10163 }
10164
10165// if (mType == IsSessionMachine)
10166// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10167
10168 }
10169 catch (HRESULT err)
10170 {
10171 /* we assume that error info is set by the thrower */
10172 rc = err;
10173 }
10174 catch (...)
10175 {
10176 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10177 }
10178
10179 return rc;
10180}
10181
10182/**
10183 * Saves the VM hardware configuration. It is assumed that the
10184 * given node is empty.
10185 *
10186 * @param data Reference to the settings object for the hardware config.
10187 * @param pDbg Pointer to the settings object for the debugging config
10188 * which happens to live in mHWData.
10189 * @param pAutostart Pointer to the settings object for the autostart config
10190 * which happens to live in mHWData.
10191 */
10192HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10193 settings::Autostart *pAutostart)
10194{
10195 HRESULT rc = S_OK;
10196
10197 try
10198 {
10199 /* The hardware version attribute (optional).
10200 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10201 if ( mHWData->mHWVersion == "1"
10202 && mSSData->strStateFilePath.isEmpty()
10203 )
10204 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. */
10205
10206 data.strVersion = mHWData->mHWVersion;
10207 data.uuid = mHWData->mHardwareUUID;
10208
10209 // CPU
10210 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10211 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10212 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10213 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10214 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10215 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10216 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10217 data.fPAE = !!mHWData->mPAEEnabled;
10218 data.enmLongMode = mHWData->mLongMode;
10219 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10220
10221 /* Standard and Extended CPUID leafs. */
10222 data.llCpuIdLeafs.clear();
10223 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10224 {
10225 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10226 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10227 }
10228 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10229 {
10230 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10231 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10232 }
10233
10234 data.cCPUs = mHWData->mCPUCount;
10235 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10236 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10237
10238 data.llCpus.clear();
10239 if (data.fCpuHotPlug)
10240 {
10241 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10242 {
10243 if (mHWData->mCPUAttached[idx])
10244 {
10245 settings::Cpu cpu;
10246 cpu.ulId = idx;
10247 data.llCpus.push_back(cpu);
10248 }
10249 }
10250 }
10251
10252 // memory
10253 data.ulMemorySizeMB = mHWData->mMemorySize;
10254 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10255
10256 // firmware
10257 data.firmwareType = mHWData->mFirmwareType;
10258
10259 // HID
10260 data.pointingHIDType = mHWData->mPointingHIDType;
10261 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10262
10263 // chipset
10264 data.chipsetType = mHWData->mChipsetType;
10265
10266 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10267 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10268
10269 // HPET
10270 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10271
10272 // boot order
10273 data.mapBootOrder.clear();
10274 for (size_t i = 0;
10275 i < RT_ELEMENTS(mHWData->mBootOrder);
10276 ++i)
10277 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10278
10279 // display
10280 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10281 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10282 data.cMonitors = mHWData->mMonitorCount;
10283 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10284 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10285 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10286 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10287 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10288 data.ulVideoCaptureFps = mHWData->mVideoCaptureFps;
10289 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10290 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10291 {
10292 if (mHWData->maVideoCaptureScreens[i])
10293 ASMBitSet(&data.u64VideoCaptureScreens, i);
10294 else
10295 ASMBitClear(&data.u64VideoCaptureScreens, i);
10296 }
10297 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
10298
10299 /* VRDEServer settings (optional) */
10300 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10301 if (FAILED(rc)) throw rc;
10302
10303 /* BIOS (required) */
10304 rc = mBIOSSettings->saveSettings(data.biosSettings);
10305 if (FAILED(rc)) throw rc;
10306
10307 /* USB Controller (required) */
10308 rc = mUSBController->saveSettings(data.usbController);
10309 if (FAILED(rc)) throw rc;
10310
10311 /* Network adapters (required) */
10312 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10313 data.llNetworkAdapters.clear();
10314 /* Write out only the nominal number of network adapters for this
10315 * chipset type. Since Machine::commit() hasn't been called there
10316 * may be extra NIC settings in the vector. */
10317 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10318 {
10319 settings::NetworkAdapter nic;
10320 nic.ulSlot = slot;
10321 /* paranoia check... must not be NULL, but must not crash either. */
10322 if (mNetworkAdapters[slot])
10323 {
10324 rc = mNetworkAdapters[slot]->saveSettings(nic);
10325 if (FAILED(rc)) throw rc;
10326
10327 data.llNetworkAdapters.push_back(nic);
10328 }
10329 }
10330
10331 /* Serial ports */
10332 data.llSerialPorts.clear();
10333 for (ULONG slot = 0;
10334 slot < RT_ELEMENTS(mSerialPorts);
10335 ++slot)
10336 {
10337 settings::SerialPort s;
10338 s.ulSlot = slot;
10339 rc = mSerialPorts[slot]->saveSettings(s);
10340 if (FAILED(rc)) return rc;
10341
10342 data.llSerialPorts.push_back(s);
10343 }
10344
10345 /* Parallel ports */
10346 data.llParallelPorts.clear();
10347 for (ULONG slot = 0;
10348 slot < RT_ELEMENTS(mParallelPorts);
10349 ++slot)
10350 {
10351 settings::ParallelPort p;
10352 p.ulSlot = slot;
10353 rc = mParallelPorts[slot]->saveSettings(p);
10354 if (FAILED(rc)) return rc;
10355
10356 data.llParallelPorts.push_back(p);
10357 }
10358
10359 /* Audio adapter */
10360 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10361 if (FAILED(rc)) return rc;
10362
10363 /* Shared folders */
10364 data.llSharedFolders.clear();
10365 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10366 it != mHWData->mSharedFolders.end();
10367 ++it)
10368 {
10369 SharedFolder *pSF = *it;
10370 AutoCaller sfCaller(pSF);
10371 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10372 settings::SharedFolder sf;
10373 sf.strName = pSF->getName();
10374 sf.strHostPath = pSF->getHostPath();
10375 sf.fWritable = !!pSF->isWritable();
10376 sf.fAutoMount = !!pSF->isAutoMounted();
10377
10378 data.llSharedFolders.push_back(sf);
10379 }
10380
10381 // clipboard
10382 data.clipboardMode = mHWData->mClipboardMode;
10383
10384 // drag'n'drop
10385 data.dragAndDropMode = mHWData->mDragAndDropMode;
10386
10387 /* Guest */
10388 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10389
10390 // IO settings
10391 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10392 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10393
10394 /* BandwidthControl (required) */
10395 rc = mBandwidthControl->saveSettings(data.ioSettings);
10396 if (FAILED(rc)) throw rc;
10397
10398 /* Host PCI devices */
10399 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10400 it != mHWData->mPCIDeviceAssignments.end();
10401 ++it)
10402 {
10403 ComObjPtr<PCIDeviceAttachment> pda = *it;
10404 settings::HostPCIDeviceAttachment hpda;
10405
10406 rc = pda->saveSettings(hpda);
10407 if (FAILED(rc)) throw rc;
10408
10409 data.pciAttachments.push_back(hpda);
10410 }
10411
10412
10413 // guest properties
10414 data.llGuestProperties.clear();
10415#ifdef VBOX_WITH_GUEST_PROPS
10416 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10417 it != mHWData->mGuestProperties.end();
10418 ++it)
10419 {
10420 HWData::GuestProperty property = it->second;
10421
10422 /* Remove transient guest properties at shutdown unless we
10423 * are saving state */
10424 if ( ( mData->mMachineState == MachineState_PoweredOff
10425 || mData->mMachineState == MachineState_Aborted
10426 || mData->mMachineState == MachineState_Teleported)
10427 && ( property.mFlags & guestProp::TRANSIENT
10428 || property.mFlags & guestProp::TRANSRESET))
10429 continue;
10430 settings::GuestProperty prop;
10431 prop.strName = it->first;
10432 prop.strValue = property.strValue;
10433 prop.timestamp = property.mTimestamp;
10434 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10435 guestProp::writeFlags(property.mFlags, szFlags);
10436 prop.strFlags = szFlags;
10437
10438 data.llGuestProperties.push_back(prop);
10439 }
10440
10441 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10442 /* I presume this doesn't require a backup(). */
10443 mData->mGuestPropertiesModified = FALSE;
10444#endif /* VBOX_WITH_GUEST_PROPS defined */
10445
10446 *pDbg = mHWData->mDebugging;
10447 *pAutostart = mHWData->mAutostart;
10448
10449 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10450 }
10451 catch(std::bad_alloc &)
10452 {
10453 return E_OUTOFMEMORY;
10454 }
10455
10456 AssertComRC(rc);
10457 return rc;
10458}
10459
10460/**
10461 * Saves the storage controller configuration.
10462 *
10463 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10464 */
10465HRESULT Machine::saveStorageControllers(settings::Storage &data)
10466{
10467 data.llStorageControllers.clear();
10468
10469 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10470 it != mStorageControllers->end();
10471 ++it)
10472 {
10473 HRESULT rc;
10474 ComObjPtr<StorageController> pCtl = *it;
10475
10476 settings::StorageController ctl;
10477 ctl.strName = pCtl->getName();
10478 ctl.controllerType = pCtl->getControllerType();
10479 ctl.storageBus = pCtl->getStorageBus();
10480 ctl.ulInstance = pCtl->getInstance();
10481 ctl.fBootable = pCtl->getBootable();
10482
10483 /* Save the port count. */
10484 ULONG portCount;
10485 rc = pCtl->COMGETTER(PortCount)(&portCount);
10486 ComAssertComRCRet(rc, rc);
10487 ctl.ulPortCount = portCount;
10488
10489 /* Save fUseHostIOCache */
10490 BOOL fUseHostIOCache;
10491 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10492 ComAssertComRCRet(rc, rc);
10493 ctl.fUseHostIOCache = !!fUseHostIOCache;
10494
10495 /* Save IDE emulation settings. */
10496 if (ctl.controllerType == StorageControllerType_IntelAhci)
10497 {
10498 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10499 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10500 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10501 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10502 )
10503 ComAssertComRCRet(rc, rc);
10504 }
10505
10506 /* save the devices now. */
10507 rc = saveStorageDevices(pCtl, ctl);
10508 ComAssertComRCRet(rc, rc);
10509
10510 data.llStorageControllers.push_back(ctl);
10511 }
10512
10513 return S_OK;
10514}
10515
10516/**
10517 * Saves the hard disk configuration.
10518 */
10519HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10520 settings::StorageController &data)
10521{
10522 MediaData::AttachmentList atts;
10523
10524 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10525 if (FAILED(rc)) return rc;
10526
10527 data.llAttachedDevices.clear();
10528 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10529 it != atts.end();
10530 ++it)
10531 {
10532 settings::AttachedDevice dev;
10533
10534 MediumAttachment *pAttach = *it;
10535 Medium *pMedium = pAttach->getMedium();
10536
10537 dev.deviceType = pAttach->getType();
10538 dev.lPort = pAttach->getPort();
10539 dev.lDevice = pAttach->getDevice();
10540 if (pMedium)
10541 {
10542 if (pMedium->isHostDrive())
10543 dev.strHostDriveSrc = pMedium->getLocationFull();
10544 else
10545 dev.uuid = pMedium->getId();
10546 dev.fPassThrough = pAttach->getPassthrough();
10547 dev.fTempEject = pAttach->getTempEject();
10548 dev.fNonRotational = pAttach->getNonRotational();
10549 dev.fDiscard = pAttach->getDiscard();
10550 }
10551
10552 dev.strBwGroup = pAttach->getBandwidthGroup();
10553
10554 data.llAttachedDevices.push_back(dev);
10555 }
10556
10557 return S_OK;
10558}
10559
10560/**
10561 * Saves machine state settings as defined by aFlags
10562 * (SaveSTS_* values).
10563 *
10564 * @param aFlags Combination of SaveSTS_* flags.
10565 *
10566 * @note Locks objects for writing.
10567 */
10568HRESULT Machine::saveStateSettings(int aFlags)
10569{
10570 if (aFlags == 0)
10571 return S_OK;
10572
10573 AutoCaller autoCaller(this);
10574 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10575
10576 /* This object's write lock is also necessary to serialize file access
10577 * (prevent concurrent reads and writes) */
10578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10579
10580 HRESULT rc = S_OK;
10581
10582 Assert(mData->pMachineConfigFile);
10583
10584 try
10585 {
10586 if (aFlags & SaveSTS_CurStateModified)
10587 mData->pMachineConfigFile->fCurrentStateModified = true;
10588
10589 if (aFlags & SaveSTS_StateFilePath)
10590 {
10591 if (!mSSData->strStateFilePath.isEmpty())
10592 /* try to make the file name relative to the settings file dir */
10593 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10594 else
10595 mData->pMachineConfigFile->strStateFile.setNull();
10596 }
10597
10598 if (aFlags & SaveSTS_StateTimeStamp)
10599 {
10600 Assert( mData->mMachineState != MachineState_Aborted
10601 || mSSData->strStateFilePath.isEmpty());
10602
10603 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10604
10605 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10606//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10607 }
10608
10609 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10610 }
10611 catch (...)
10612 {
10613 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10614 }
10615
10616 return rc;
10617}
10618
10619/**
10620 * Ensures that the given medium is added to a media registry. If this machine
10621 * was created with 4.0 or later, then the machine registry is used. Otherwise
10622 * the global VirtualBox media registry is used.
10623 *
10624 * Caller must NOT hold machine lock, media tree or any medium locks!
10625 *
10626 * @param pMedium
10627 */
10628void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10629{
10630 /* Paranoia checks: do not hold machine or media tree locks. */
10631 AssertReturnVoid(!isWriteLockOnCurrentThread());
10632 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10633
10634 ComObjPtr<Medium> pBase;
10635 {
10636 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10637 pBase = pMedium->getBase();
10638 }
10639
10640 /* Paranoia checks: do not hold medium locks. */
10641 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10642 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10643
10644 // decide which medium registry to use now that the medium is attached:
10645 Guid uuid;
10646 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10647 // machine XML is VirtualBox 4.0 or higher:
10648 uuid = getId(); // machine UUID
10649 else
10650 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10651
10652 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10653 mParent->markRegistryModified(uuid);
10654
10655 /* For more complex hard disk structures it can happen that the base
10656 * medium isn't yet associated with any medium registry. Do that now. */
10657 if (pMedium != pBase)
10658 {
10659 if (pBase->addRegistry(uuid, true /* fRecurse */))
10660 mParent->markRegistryModified(uuid);
10661 }
10662}
10663
10664/**
10665 * Creates differencing hard disks for all normal hard disks attached to this
10666 * machine and a new set of attachments to refer to created disks.
10667 *
10668 * Used when taking a snapshot or when deleting the current state. Gets called
10669 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10670 *
10671 * This method assumes that mMediaData contains the original hard disk attachments
10672 * it needs to create diffs for. On success, these attachments will be replaced
10673 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10674 * called to delete created diffs which will also rollback mMediaData and restore
10675 * whatever was backed up before calling this method.
10676 *
10677 * Attachments with non-normal hard disks are left as is.
10678 *
10679 * If @a aOnline is @c false then the original hard disks that require implicit
10680 * diffs will be locked for reading. Otherwise it is assumed that they are
10681 * already locked for writing (when the VM was started). Note that in the latter
10682 * case it is responsibility of the caller to lock the newly created diffs for
10683 * writing if this method succeeds.
10684 *
10685 * @param aProgress Progress object to run (must contain at least as
10686 * many operations left as the number of hard disks
10687 * attached).
10688 * @param aOnline Whether the VM was online prior to this operation.
10689 *
10690 * @note The progress object is not marked as completed, neither on success nor
10691 * on failure. This is a responsibility of the caller.
10692 *
10693 * @note Locks this object and the media tree for writing.
10694 */
10695HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10696 ULONG aWeight,
10697 bool aOnline)
10698{
10699 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10700
10701 AutoCaller autoCaller(this);
10702 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10703
10704 AutoMultiWriteLock2 alock(this->lockHandle(),
10705 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10706
10707 /* must be in a protective state because we release the lock below */
10708 AssertReturn( mData->mMachineState == MachineState_Saving
10709 || mData->mMachineState == MachineState_LiveSnapshotting
10710 || mData->mMachineState == MachineState_RestoringSnapshot
10711 || mData->mMachineState == MachineState_DeletingSnapshot
10712 , E_FAIL);
10713
10714 HRESULT rc = S_OK;
10715
10716 // use appropriate locked media map (online or offline)
10717 MediumLockListMap lockedMediaOffline;
10718 MediumLockListMap *lockedMediaMap;
10719 if (aOnline)
10720 lockedMediaMap = &mData->mSession.mLockedMedia;
10721 else
10722 lockedMediaMap = &lockedMediaOffline;
10723
10724 try
10725 {
10726 if (!aOnline)
10727 {
10728 /* lock all attached hard disks early to detect "in use"
10729 * situations before creating actual diffs */
10730 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10731 it != mMediaData->mAttachments.end();
10732 ++it)
10733 {
10734 MediumAttachment* pAtt = *it;
10735 if (pAtt->getType() == DeviceType_HardDisk)
10736 {
10737 Medium* pMedium = pAtt->getMedium();
10738 Assert(pMedium);
10739
10740 MediumLockList *pMediumLockList(new MediumLockList());
10741 alock.release();
10742 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10743 false /* fMediumLockWrite */,
10744 NULL,
10745 *pMediumLockList);
10746 alock.acquire();
10747 if (FAILED(rc))
10748 {
10749 delete pMediumLockList;
10750 throw rc;
10751 }
10752 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10753 if (FAILED(rc))
10754 {
10755 throw setError(rc,
10756 tr("Collecting locking information for all attached media failed"));
10757 }
10758 }
10759 }
10760
10761 /* Now lock all media. If this fails, nothing is locked. */
10762 alock.release();
10763 rc = lockedMediaMap->Lock();
10764 alock.acquire();
10765 if (FAILED(rc))
10766 {
10767 throw setError(rc,
10768 tr("Locking of attached media failed"));
10769 }
10770 }
10771
10772 /* remember the current list (note that we don't use backup() since
10773 * mMediaData may be already backed up) */
10774 MediaData::AttachmentList atts = mMediaData->mAttachments;
10775
10776 /* start from scratch */
10777 mMediaData->mAttachments.clear();
10778
10779 /* go through remembered attachments and create diffs for normal hard
10780 * disks and attach them */
10781 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10782 it != atts.end();
10783 ++it)
10784 {
10785 MediumAttachment* pAtt = *it;
10786
10787 DeviceType_T devType = pAtt->getType();
10788 Medium* pMedium = pAtt->getMedium();
10789
10790 if ( devType != DeviceType_HardDisk
10791 || pMedium == NULL
10792 || pMedium->getType() != MediumType_Normal)
10793 {
10794 /* copy the attachment as is */
10795
10796 /** @todo the progress object created in Console::TakeSnaphot
10797 * only expects operations for hard disks. Later other
10798 * device types need to show up in the progress as well. */
10799 if (devType == DeviceType_HardDisk)
10800 {
10801 if (pMedium == NULL)
10802 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10803 aWeight); // weight
10804 else
10805 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10806 pMedium->getBase()->getName().c_str()).raw(),
10807 aWeight); // weight
10808 }
10809
10810 mMediaData->mAttachments.push_back(pAtt);
10811 continue;
10812 }
10813
10814 /* need a diff */
10815 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10816 pMedium->getBase()->getName().c_str()).raw(),
10817 aWeight); // weight
10818
10819 Utf8Str strFullSnapshotFolder;
10820 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10821
10822 ComObjPtr<Medium> diff;
10823 diff.createObject();
10824 // store the diff in the same registry as the parent
10825 // (this cannot fail here because we can't create implicit diffs for
10826 // unregistered images)
10827 Guid uuidRegistryParent;
10828 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10829 Assert(fInRegistry); NOREF(fInRegistry);
10830 rc = diff->init(mParent,
10831 pMedium->getPreferredDiffFormat(),
10832 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10833 uuidRegistryParent);
10834 if (FAILED(rc)) throw rc;
10835
10836 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10837 * the push_back? Looks like we're going to release medium with the
10838 * wrong kind of lock (general issue with if we fail anywhere at all)
10839 * and an orphaned VDI in the snapshots folder. */
10840
10841 /* update the appropriate lock list */
10842 MediumLockList *pMediumLockList;
10843 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10844 AssertComRCThrowRC(rc);
10845 if (aOnline)
10846 {
10847 alock.release();
10848 /* The currently attached medium will be read-only, change
10849 * the lock type to read. */
10850 rc = pMediumLockList->Update(pMedium, false);
10851 alock.acquire();
10852 AssertComRCThrowRC(rc);
10853 }
10854
10855 /* release the locks before the potentially lengthy operation */
10856 alock.release();
10857 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10858 pMediumLockList,
10859 NULL /* aProgress */,
10860 true /* aWait */);
10861 alock.acquire();
10862 if (FAILED(rc)) throw rc;
10863
10864 /* actual lock list update is done in Medium::commitMedia */
10865
10866 rc = diff->addBackReference(mData->mUuid);
10867 AssertComRCThrowRC(rc);
10868
10869 /* add a new attachment */
10870 ComObjPtr<MediumAttachment> attachment;
10871 attachment.createObject();
10872 rc = attachment->init(this,
10873 diff,
10874 pAtt->getControllerName(),
10875 pAtt->getPort(),
10876 pAtt->getDevice(),
10877 DeviceType_HardDisk,
10878 true /* aImplicit */,
10879 false /* aPassthrough */,
10880 false /* aTempEject */,
10881 pAtt->getNonRotational(),
10882 pAtt->getDiscard(),
10883 pAtt->getBandwidthGroup());
10884 if (FAILED(rc)) throw rc;
10885
10886 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10887 AssertComRCThrowRC(rc);
10888 mMediaData->mAttachments.push_back(attachment);
10889 }
10890 }
10891 catch (HRESULT aRC) { rc = aRC; }
10892
10893 /* unlock all hard disks we locked when there is no VM */
10894 if (!aOnline)
10895 {
10896 ErrorInfoKeeper eik;
10897
10898 HRESULT rc1 = lockedMediaMap->Clear();
10899 AssertComRC(rc1);
10900 }
10901
10902 return rc;
10903}
10904
10905/**
10906 * Deletes implicit differencing hard disks created either by
10907 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10908 *
10909 * Note that to delete hard disks created by #AttachDevice() this method is
10910 * called from #fixupMedia() when the changes are rolled back.
10911 *
10912 * @note Locks this object and the media tree for writing.
10913 */
10914HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10915{
10916 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10917
10918 AutoCaller autoCaller(this);
10919 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10920
10921 AutoMultiWriteLock2 alock(this->lockHandle(),
10922 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10923
10924 /* We absolutely must have backed up state. */
10925 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10926
10927 /* Check if there are any implicitly created diff images. */
10928 bool fImplicitDiffs = false;
10929 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10930 it != mMediaData->mAttachments.end();
10931 ++it)
10932 {
10933 const ComObjPtr<MediumAttachment> &pAtt = *it;
10934 if (pAtt->isImplicit())
10935 {
10936 fImplicitDiffs = true;
10937 break;
10938 }
10939 }
10940 /* If there is nothing to do, leave early. This saves lots of image locking
10941 * effort. It also avoids a MachineStateChanged event without real reason.
10942 * This is important e.g. when loading a VM config, because there should be
10943 * no events. Otherwise API clients can become thoroughly confused for
10944 * inaccessible VMs (the code for loading VM configs uses this method for
10945 * cleanup if the config makes no sense), as they take such events as an
10946 * indication that the VM is alive, and they would force the VM config to
10947 * be reread, leading to an endless loop. */
10948 if (!fImplicitDiffs)
10949 return S_OK;
10950
10951 HRESULT rc = S_OK;
10952 MachineState_T oldState = mData->mMachineState;
10953
10954 /* will release the lock before the potentially lengthy operation,
10955 * so protect with the special state (unless already protected) */
10956 if ( oldState != MachineState_Saving
10957 && oldState != MachineState_LiveSnapshotting
10958 && oldState != MachineState_RestoringSnapshot
10959 && oldState != MachineState_DeletingSnapshot
10960 && oldState != MachineState_DeletingSnapshotOnline
10961 && oldState != MachineState_DeletingSnapshotPaused
10962 )
10963 setMachineState(MachineState_SettingUp);
10964
10965 // use appropriate locked media map (online or offline)
10966 MediumLockListMap lockedMediaOffline;
10967 MediumLockListMap *lockedMediaMap;
10968 if (aOnline)
10969 lockedMediaMap = &mData->mSession.mLockedMedia;
10970 else
10971 lockedMediaMap = &lockedMediaOffline;
10972
10973 try
10974 {
10975 if (!aOnline)
10976 {
10977 /* lock all attached hard disks early to detect "in use"
10978 * situations before deleting actual diffs */
10979 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10980 it != mMediaData->mAttachments.end();
10981 ++it)
10982 {
10983 MediumAttachment* pAtt = *it;
10984 if (pAtt->getType() == DeviceType_HardDisk)
10985 {
10986 Medium* pMedium = pAtt->getMedium();
10987 Assert(pMedium);
10988
10989 MediumLockList *pMediumLockList(new MediumLockList());
10990 alock.release();
10991 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10992 false /* fMediumLockWrite */,
10993 NULL,
10994 *pMediumLockList);
10995 alock.acquire();
10996
10997 if (FAILED(rc))
10998 {
10999 delete pMediumLockList;
11000 throw rc;
11001 }
11002
11003 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11004 if (FAILED(rc))
11005 throw rc;
11006 }
11007 }
11008
11009 if (FAILED(rc))
11010 throw rc;
11011 } // end of offline
11012
11013 /* Lock lists are now up to date and include implicitly created media */
11014
11015 /* Go through remembered attachments and delete all implicitly created
11016 * diffs and fix up the attachment information */
11017 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11018 MediaData::AttachmentList implicitAtts;
11019 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11020 it != mMediaData->mAttachments.end();
11021 ++it)
11022 {
11023 ComObjPtr<MediumAttachment> pAtt = *it;
11024 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11025 if (pMedium.isNull())
11026 continue;
11027
11028 // Implicit attachments go on the list for deletion and back references are removed.
11029 if (pAtt->isImplicit())
11030 {
11031 /* Deassociate and mark for deletion */
11032 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11033 rc = pMedium->removeBackReference(mData->mUuid);
11034 if (FAILED(rc))
11035 throw rc;
11036 implicitAtts.push_back(pAtt);
11037 continue;
11038 }
11039
11040 /* Was this medium attached before? */
11041 if (!findAttachment(oldAtts, pMedium))
11042 {
11043 /* no: de-associate */
11044 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11045 rc = pMedium->removeBackReference(mData->mUuid);
11046 if (FAILED(rc))
11047 throw rc;
11048 continue;
11049 }
11050 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11051 }
11052
11053 /* If there are implicit attachments to delete, throw away the lock
11054 * map contents (which will unlock all media) since the medium
11055 * attachments will be rolled back. Below we need to completely
11056 * recreate the lock map anyway since it is infinitely complex to
11057 * do this incrementally (would need reconstructing each attachment
11058 * change, which would be extremely hairy). */
11059 if (implicitAtts.size() != 0)
11060 {
11061 ErrorInfoKeeper eik;
11062
11063 HRESULT rc1 = lockedMediaMap->Clear();
11064 AssertComRC(rc1);
11065 }
11066
11067 /* rollback hard disk changes */
11068 mMediaData.rollback();
11069
11070 MultiResult mrc(S_OK);
11071
11072 // Delete unused implicit diffs.
11073 if (implicitAtts.size() != 0)
11074 {
11075 alock.release();
11076
11077 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11078 it != implicitAtts.end();
11079 ++it)
11080 {
11081 // Remove medium associated with this attachment.
11082 ComObjPtr<MediumAttachment> pAtt = *it;
11083 Assert(pAtt);
11084 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11085 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11086 Assert(pMedium);
11087
11088 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11089 // continue on delete failure, just collect error messages
11090 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11091 mrc = rc;
11092 }
11093
11094 alock.acquire();
11095
11096 /* if there is a VM recreate media lock map as mentioned above,
11097 * otherwise it is a waste of time and we leave things unlocked */
11098 if (aOnline)
11099 {
11100 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11101 /* must never be NULL, but better safe than sorry */
11102 if (!pMachine.isNull())
11103 {
11104 alock.release();
11105 rc = mData->mSession.mMachine->lockMedia();
11106 alock.acquire();
11107 if (FAILED(rc))
11108 throw rc;
11109 }
11110 }
11111 }
11112 }
11113 catch (HRESULT aRC) {rc = aRC;}
11114
11115 if (mData->mMachineState == MachineState_SettingUp)
11116 setMachineState(oldState);
11117
11118 /* unlock all hard disks we locked when there is no VM */
11119 if (!aOnline)
11120 {
11121 ErrorInfoKeeper eik;
11122
11123 HRESULT rc1 = lockedMediaMap->Clear();
11124 AssertComRC(rc1);
11125 }
11126
11127 return rc;
11128}
11129
11130
11131/**
11132 * Looks through the given list of media attachments for one with the given parameters
11133 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11134 * can be searched as well if needed.
11135 *
11136 * @param list
11137 * @param aControllerName
11138 * @param aControllerPort
11139 * @param aDevice
11140 * @return
11141 */
11142MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11143 IN_BSTR aControllerName,
11144 LONG aControllerPort,
11145 LONG aDevice)
11146{
11147 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11148 it != ll.end();
11149 ++it)
11150 {
11151 MediumAttachment *pAttach = *it;
11152 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11153 return pAttach;
11154 }
11155
11156 return NULL;
11157}
11158
11159/**
11160 * Looks through the given list of media attachments for one with the given parameters
11161 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11162 * can be searched as well if needed.
11163 *
11164 * @param list
11165 * @param aControllerName
11166 * @param aControllerPort
11167 * @param aDevice
11168 * @return
11169 */
11170MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11171 ComObjPtr<Medium> pMedium)
11172{
11173 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11174 it != ll.end();
11175 ++it)
11176 {
11177 MediumAttachment *pAttach = *it;
11178 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11179 if (pMediumThis == pMedium)
11180 return pAttach;
11181 }
11182
11183 return NULL;
11184}
11185
11186/**
11187 * Looks through the given list of media attachments for one with the given parameters
11188 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11189 * can be searched as well if needed.
11190 *
11191 * @param list
11192 * @param aControllerName
11193 * @param aControllerPort
11194 * @param aDevice
11195 * @return
11196 */
11197MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11198 Guid &id)
11199{
11200 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11201 it != ll.end();
11202 ++it)
11203 {
11204 MediumAttachment *pAttach = *it;
11205 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11206 if (pMediumThis->getId() == id)
11207 return pAttach;
11208 }
11209
11210 return NULL;
11211}
11212
11213/**
11214 * Main implementation for Machine::DetachDevice. This also gets called
11215 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11216 *
11217 * @param pAttach Medium attachment to detach.
11218 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11219 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11220 * @return
11221 */
11222HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11223 AutoWriteLock &writeLock,
11224 Snapshot *pSnapshot)
11225{
11226 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11227 DeviceType_T mediumType = pAttach->getType();
11228
11229 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11230
11231 if (pAttach->isImplicit())
11232 {
11233 /* attempt to implicitly delete the implicitly created diff */
11234
11235 /// @todo move the implicit flag from MediumAttachment to Medium
11236 /// and forbid any hard disk operation when it is implicit. Or maybe
11237 /// a special media state for it to make it even more simple.
11238
11239 Assert(mMediaData.isBackedUp());
11240
11241 /* will release the lock before the potentially lengthy operation, so
11242 * protect with the special state */
11243 MachineState_T oldState = mData->mMachineState;
11244 setMachineState(MachineState_SettingUp);
11245
11246 writeLock.release();
11247
11248 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11249 true /*aWait*/);
11250
11251 writeLock.acquire();
11252
11253 setMachineState(oldState);
11254
11255 if (FAILED(rc)) return rc;
11256 }
11257
11258 setModified(IsModified_Storage);
11259 mMediaData.backup();
11260 mMediaData->mAttachments.remove(pAttach);
11261
11262 if (!oldmedium.isNull())
11263 {
11264 // if this is from a snapshot, do not defer detachment to commitMedia()
11265 if (pSnapshot)
11266 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11267 // else if non-hard disk media, do not defer detachment to commitMedia() either
11268 else if (mediumType != DeviceType_HardDisk)
11269 oldmedium->removeBackReference(mData->mUuid);
11270 }
11271
11272 return S_OK;
11273}
11274
11275/**
11276 * Goes thru all media of the given list and
11277 *
11278 * 1) calls detachDevice() on each of them for this machine and
11279 * 2) adds all Medium objects found in the process to the given list,
11280 * depending on cleanupMode.
11281 *
11282 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11283 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11284 * media to the list.
11285 *
11286 * This gets called from Machine::Unregister, both for the actual Machine and
11287 * the SnapshotMachine objects that might be found in the snapshots.
11288 *
11289 * Requires caller and locking. The machine lock must be passed in because it
11290 * will be passed on to detachDevice which needs it for temporary unlocking.
11291 *
11292 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11293 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11294 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11295 * otherwise no media get added.
11296 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11297 * @return
11298 */
11299HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11300 Snapshot *pSnapshot,
11301 CleanupMode_T cleanupMode,
11302 MediaList &llMedia)
11303{
11304 Assert(isWriteLockOnCurrentThread());
11305
11306 HRESULT rc;
11307
11308 // make a temporary list because detachDevice invalidates iterators into
11309 // mMediaData->mAttachments
11310 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11311
11312 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11313 it != llAttachments2.end();
11314 ++it)
11315 {
11316 ComObjPtr<MediumAttachment> &pAttach = *it;
11317 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11318
11319 if (!pMedium.isNull())
11320 {
11321 AutoCaller mac(pMedium);
11322 if (FAILED(mac.rc())) return mac.rc();
11323 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11324 DeviceType_T devType = pMedium->getDeviceType();
11325 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11326 && devType == DeviceType_HardDisk)
11327 || (cleanupMode == CleanupMode_Full)
11328 )
11329 {
11330 llMedia.push_back(pMedium);
11331 ComObjPtr<Medium> pParent = pMedium->getParent();
11332 /*
11333 * Search for medias which are not attached to any machine, but
11334 * in the chain to an attached disk. Mediums are only consided
11335 * if they are:
11336 * - have only one child
11337 * - no references to any machines
11338 * - are of normal medium type
11339 */
11340 while (!pParent.isNull())
11341 {
11342 AutoCaller mac1(pParent);
11343 if (FAILED(mac1.rc())) return mac1.rc();
11344 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11345 if (pParent->getChildren().size() == 1)
11346 {
11347 if ( pParent->getMachineBackRefCount() == 0
11348 && pParent->getType() == MediumType_Normal
11349 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11350 llMedia.push_back(pParent);
11351 }
11352 else
11353 break;
11354 pParent = pParent->getParent();
11355 }
11356 }
11357 }
11358
11359 // real machine: then we need to use the proper method
11360 rc = detachDevice(pAttach, writeLock, pSnapshot);
11361
11362 if (FAILED(rc))
11363 return rc;
11364 }
11365
11366 return S_OK;
11367}
11368
11369/**
11370 * Perform deferred hard disk detachments.
11371 *
11372 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11373 * backed up).
11374 *
11375 * If @a aOnline is @c true then this method will also unlock the old hard disks
11376 * for which the new implicit diffs were created and will lock these new diffs for
11377 * writing.
11378 *
11379 * @param aOnline Whether the VM was online prior to this operation.
11380 *
11381 * @note Locks this object for writing!
11382 */
11383void Machine::commitMedia(bool aOnline /*= false*/)
11384{
11385 AutoCaller autoCaller(this);
11386 AssertComRCReturnVoid(autoCaller.rc());
11387
11388 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11389
11390 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11391
11392 HRESULT rc = S_OK;
11393
11394 /* no attach/detach operations -- nothing to do */
11395 if (!mMediaData.isBackedUp())
11396 return;
11397
11398 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11399 bool fMediaNeedsLocking = false;
11400
11401 /* enumerate new attachments */
11402 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11403 it != mMediaData->mAttachments.end();
11404 ++it)
11405 {
11406 MediumAttachment *pAttach = *it;
11407
11408 pAttach->commit();
11409
11410 Medium* pMedium = pAttach->getMedium();
11411 bool fImplicit = pAttach->isImplicit();
11412
11413 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11414 (pMedium) ? pMedium->getName().c_str() : "NULL",
11415 fImplicit));
11416
11417 /** @todo convert all this Machine-based voodoo to MediumAttachment
11418 * based commit logic. */
11419 if (fImplicit)
11420 {
11421 /* convert implicit attachment to normal */
11422 pAttach->setImplicit(false);
11423
11424 if ( aOnline
11425 && pMedium
11426 && pAttach->getType() == DeviceType_HardDisk
11427 )
11428 {
11429 ComObjPtr<Medium> parent = pMedium->getParent();
11430 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11431
11432 /* update the appropriate lock list */
11433 MediumLockList *pMediumLockList;
11434 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11435 AssertComRC(rc);
11436 if (pMediumLockList)
11437 {
11438 /* unlock if there's a need to change the locking */
11439 if (!fMediaNeedsLocking)
11440 {
11441 rc = mData->mSession.mLockedMedia.Unlock();
11442 AssertComRC(rc);
11443 fMediaNeedsLocking = true;
11444 }
11445 rc = pMediumLockList->Update(parent, false);
11446 AssertComRC(rc);
11447 rc = pMediumLockList->Append(pMedium, true);
11448 AssertComRC(rc);
11449 }
11450 }
11451
11452 continue;
11453 }
11454
11455 if (pMedium)
11456 {
11457 /* was this medium attached before? */
11458 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11459 oldIt != oldAtts.end();
11460 ++oldIt)
11461 {
11462 MediumAttachment *pOldAttach = *oldIt;
11463 if (pOldAttach->getMedium() == pMedium)
11464 {
11465 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11466
11467 /* yes: remove from old to avoid de-association */
11468 oldAtts.erase(oldIt);
11469 break;
11470 }
11471 }
11472 }
11473 }
11474
11475 /* enumerate remaining old attachments and de-associate from the
11476 * current machine state */
11477 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11478 it != oldAtts.end();
11479 ++it)
11480 {
11481 MediumAttachment *pAttach = *it;
11482 Medium* pMedium = pAttach->getMedium();
11483
11484 /* Detach only hard disks, since DVD/floppy media is detached
11485 * instantly in MountMedium. */
11486 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11487 {
11488 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11489
11490 /* now de-associate from the current machine state */
11491 rc = pMedium->removeBackReference(mData->mUuid);
11492 AssertComRC(rc);
11493
11494 if (aOnline)
11495 {
11496 /* unlock since medium is not used anymore */
11497 MediumLockList *pMediumLockList;
11498 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11499 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11500 {
11501 /* this happens for online snapshots, there the attachment
11502 * is changing, but only to a diff image created under
11503 * the old one, so there is no separate lock list */
11504 Assert(!pMediumLockList);
11505 }
11506 else
11507 {
11508 AssertComRC(rc);
11509 if (pMediumLockList)
11510 {
11511 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11512 AssertComRC(rc);
11513 }
11514 }
11515 }
11516 }
11517 }
11518
11519 /* take media locks again so that the locking state is consistent */
11520 if (fMediaNeedsLocking)
11521 {
11522 Assert(aOnline);
11523 rc = mData->mSession.mLockedMedia.Lock();
11524 AssertComRC(rc);
11525 }
11526
11527 /* commit the hard disk changes */
11528 mMediaData.commit();
11529
11530 if (isSessionMachine())
11531 {
11532 /*
11533 * Update the parent machine to point to the new owner.
11534 * This is necessary because the stored parent will point to the
11535 * session machine otherwise and cause crashes or errors later
11536 * when the session machine gets invalid.
11537 */
11538 /** @todo Change the MediumAttachment class to behave like any other
11539 * class in this regard by creating peer MediumAttachment
11540 * objects for session machines and share the data with the peer
11541 * machine.
11542 */
11543 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11544 it != mMediaData->mAttachments.end();
11545 ++it)
11546 {
11547 (*it)->updateParentMachine(mPeer);
11548 }
11549
11550 /* attach new data to the primary machine and reshare it */
11551 mPeer->mMediaData.attach(mMediaData);
11552 }
11553
11554 return;
11555}
11556
11557/**
11558 * Perform deferred deletion of implicitly created diffs.
11559 *
11560 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11561 * backed up).
11562 *
11563 * @note Locks this object for writing!
11564 */
11565void Machine::rollbackMedia()
11566{
11567 AutoCaller autoCaller(this);
11568 AssertComRCReturnVoid(autoCaller.rc());
11569
11570 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11571 LogFlowThisFunc(("Entering rollbackMedia\n"));
11572
11573 HRESULT rc = S_OK;
11574
11575 /* no attach/detach operations -- nothing to do */
11576 if (!mMediaData.isBackedUp())
11577 return;
11578
11579 /* enumerate new attachments */
11580 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11581 it != mMediaData->mAttachments.end();
11582 ++it)
11583 {
11584 MediumAttachment *pAttach = *it;
11585 /* Fix up the backrefs for DVD/floppy media. */
11586 if (pAttach->getType() != DeviceType_HardDisk)
11587 {
11588 Medium* pMedium = pAttach->getMedium();
11589 if (pMedium)
11590 {
11591 rc = pMedium->removeBackReference(mData->mUuid);
11592 AssertComRC(rc);
11593 }
11594 }
11595
11596 (*it)->rollback();
11597
11598 pAttach = *it;
11599 /* Fix up the backrefs for DVD/floppy media. */
11600 if (pAttach->getType() != DeviceType_HardDisk)
11601 {
11602 Medium* pMedium = pAttach->getMedium();
11603 if (pMedium)
11604 {
11605 rc = pMedium->addBackReference(mData->mUuid);
11606 AssertComRC(rc);
11607 }
11608 }
11609 }
11610
11611 /** @todo convert all this Machine-based voodoo to MediumAttachment
11612 * based rollback logic. */
11613 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11614
11615 return;
11616}
11617
11618/**
11619 * Returns true if the settings file is located in the directory named exactly
11620 * as the machine; this means, among other things, that the machine directory
11621 * should be auto-renamed.
11622 *
11623 * @param aSettingsDir if not NULL, the full machine settings file directory
11624 * name will be assigned there.
11625 *
11626 * @note Doesn't lock anything.
11627 * @note Not thread safe (must be called from this object's lock).
11628 */
11629bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11630{
11631 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11632 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11633 if (aSettingsDir)
11634 *aSettingsDir = strMachineDirName;
11635 strMachineDirName.stripPath(); // vmname
11636 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11637 strConfigFileOnly.stripPath() // vmname.vbox
11638 .stripExt(); // vmname
11639 /** @todo hack, make somehow use of ComposeMachineFilename */
11640 if (mUserData->s.fDirectoryIncludesUUID)
11641 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11642
11643 AssertReturn(!strMachineDirName.isEmpty(), false);
11644 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11645
11646 return strMachineDirName == strConfigFileOnly;
11647}
11648
11649/**
11650 * Discards all changes to machine settings.
11651 *
11652 * @param aNotify Whether to notify the direct session about changes or not.
11653 *
11654 * @note Locks objects for writing!
11655 */
11656void Machine::rollback(bool aNotify)
11657{
11658 AutoCaller autoCaller(this);
11659 AssertComRCReturn(autoCaller.rc(), (void)0);
11660
11661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11662
11663 if (!mStorageControllers.isNull())
11664 {
11665 if (mStorageControllers.isBackedUp())
11666 {
11667 /* unitialize all new devices (absent in the backed up list). */
11668 StorageControllerList::const_iterator it = mStorageControllers->begin();
11669 StorageControllerList *backedList = mStorageControllers.backedUpData();
11670 while (it != mStorageControllers->end())
11671 {
11672 if ( std::find(backedList->begin(), backedList->end(), *it)
11673 == backedList->end()
11674 )
11675 {
11676 (*it)->uninit();
11677 }
11678 ++it;
11679 }
11680
11681 /* restore the list */
11682 mStorageControllers.rollback();
11683 }
11684
11685 /* rollback any changes to devices after restoring the list */
11686 if (mData->flModifications & IsModified_Storage)
11687 {
11688 StorageControllerList::const_iterator it = mStorageControllers->begin();
11689 while (it != mStorageControllers->end())
11690 {
11691 (*it)->rollback();
11692 ++it;
11693 }
11694 }
11695 }
11696
11697 mUserData.rollback();
11698
11699 mHWData.rollback();
11700
11701 if (mData->flModifications & IsModified_Storage)
11702 rollbackMedia();
11703
11704 if (mBIOSSettings)
11705 mBIOSSettings->rollback();
11706
11707 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11708 mVRDEServer->rollback();
11709
11710 if (mAudioAdapter)
11711 mAudioAdapter->rollback();
11712
11713 if (mUSBController && (mData->flModifications & IsModified_USB))
11714 mUSBController->rollback();
11715
11716 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11717 mBandwidthControl->rollback();
11718
11719 if (!mHWData.isNull())
11720 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11721 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11722 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11723 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11724
11725 if (mData->flModifications & IsModified_NetworkAdapters)
11726 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11727 if ( mNetworkAdapters[slot]
11728 && mNetworkAdapters[slot]->isModified())
11729 {
11730 mNetworkAdapters[slot]->rollback();
11731 networkAdapters[slot] = mNetworkAdapters[slot];
11732 }
11733
11734 if (mData->flModifications & IsModified_SerialPorts)
11735 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11736 if ( mSerialPorts[slot]
11737 && mSerialPorts[slot]->isModified())
11738 {
11739 mSerialPorts[slot]->rollback();
11740 serialPorts[slot] = mSerialPorts[slot];
11741 }
11742
11743 if (mData->flModifications & IsModified_ParallelPorts)
11744 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11745 if ( mParallelPorts[slot]
11746 && mParallelPorts[slot]->isModified())
11747 {
11748 mParallelPorts[slot]->rollback();
11749 parallelPorts[slot] = mParallelPorts[slot];
11750 }
11751
11752 if (aNotify)
11753 {
11754 /* inform the direct session about changes */
11755
11756 ComObjPtr<Machine> that = this;
11757 uint32_t flModifications = mData->flModifications;
11758 alock.release();
11759
11760 if (flModifications & IsModified_SharedFolders)
11761 that->onSharedFolderChange();
11762
11763 if (flModifications & IsModified_VRDEServer)
11764 that->onVRDEServerChange(/* aRestart */ TRUE);
11765 if (flModifications & IsModified_USB)
11766 that->onUSBControllerChange();
11767
11768 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11769 if (networkAdapters[slot])
11770 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11771 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11772 if (serialPorts[slot])
11773 that->onSerialPortChange(serialPorts[slot]);
11774 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11775 if (parallelPorts[slot])
11776 that->onParallelPortChange(parallelPorts[slot]);
11777
11778 if (flModifications & IsModified_Storage)
11779 that->onStorageControllerChange();
11780
11781#if 0
11782 if (flModifications & IsModified_BandwidthControl)
11783 that->onBandwidthControlChange();
11784#endif
11785 }
11786}
11787
11788/**
11789 * Commits all the changes to machine settings.
11790 *
11791 * Note that this operation is supposed to never fail.
11792 *
11793 * @note Locks this object and children for writing.
11794 */
11795void Machine::commit()
11796{
11797 AutoCaller autoCaller(this);
11798 AssertComRCReturnVoid(autoCaller.rc());
11799
11800 AutoCaller peerCaller(mPeer);
11801 AssertComRCReturnVoid(peerCaller.rc());
11802
11803 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11804
11805 /*
11806 * use safe commit to ensure Snapshot machines (that share mUserData)
11807 * will still refer to a valid memory location
11808 */
11809 mUserData.commitCopy();
11810
11811 mHWData.commit();
11812
11813 if (mMediaData.isBackedUp())
11814 commitMedia(Global::IsOnline(mData->mMachineState));
11815
11816 mBIOSSettings->commit();
11817 mVRDEServer->commit();
11818 mAudioAdapter->commit();
11819 mUSBController->commit();
11820 mBandwidthControl->commit();
11821
11822 /* Since mNetworkAdapters is a list which might have been changed (resized)
11823 * without using the Backupable<> template we need to handle the copying
11824 * of the list entries manually, including the creation of peers for the
11825 * new objects. */
11826 bool commitNetworkAdapters = false;
11827 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11828 if (mPeer)
11829 {
11830 /* commit everything, even the ones which will go away */
11831 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11832 mNetworkAdapters[slot]->commit();
11833 /* copy over the new entries, creating a peer and uninit the original */
11834 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11835 for (size_t slot = 0; slot < newSize; slot++)
11836 {
11837 /* look if this adapter has a peer device */
11838 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11839 if (!peer)
11840 {
11841 /* no peer means the adapter is a newly created one;
11842 * create a peer owning data this data share it with */
11843 peer.createObject();
11844 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11845 }
11846 mPeer->mNetworkAdapters[slot] = peer;
11847 }
11848 /* uninit any no longer needed network adapters */
11849 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11850 mNetworkAdapters[slot]->uninit();
11851 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11852 {
11853 if (mPeer->mNetworkAdapters[slot])
11854 mPeer->mNetworkAdapters[slot]->uninit();
11855 }
11856 /* Keep the original network adapter count until this point, so that
11857 * discarding a chipset type change will not lose settings. */
11858 mNetworkAdapters.resize(newSize);
11859 mPeer->mNetworkAdapters.resize(newSize);
11860 }
11861 else
11862 {
11863 /* we have no peer (our parent is the newly created machine);
11864 * just commit changes to the network adapters */
11865 commitNetworkAdapters = true;
11866 }
11867 if (commitNetworkAdapters)
11868 {
11869 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11870 mNetworkAdapters[slot]->commit();
11871 }
11872
11873 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11874 mSerialPorts[slot]->commit();
11875 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11876 mParallelPorts[slot]->commit();
11877
11878 bool commitStorageControllers = false;
11879
11880 if (mStorageControllers.isBackedUp())
11881 {
11882 mStorageControllers.commit();
11883
11884 if (mPeer)
11885 {
11886 /* Commit all changes to new controllers (this will reshare data with
11887 * peers for those who have peers) */
11888 StorageControllerList *newList = new StorageControllerList();
11889 StorageControllerList::const_iterator it = mStorageControllers->begin();
11890 while (it != mStorageControllers->end())
11891 {
11892 (*it)->commit();
11893
11894 /* look if this controller has a peer device */
11895 ComObjPtr<StorageController> peer = (*it)->getPeer();
11896 if (!peer)
11897 {
11898 /* no peer means the device is a newly created one;
11899 * create a peer owning data this device share it with */
11900 peer.createObject();
11901 peer->init(mPeer, *it, true /* aReshare */);
11902 }
11903 else
11904 {
11905 /* remove peer from the old list */
11906 mPeer->mStorageControllers->remove(peer);
11907 }
11908 /* and add it to the new list */
11909 newList->push_back(peer);
11910
11911 ++it;
11912 }
11913
11914 /* uninit old peer's controllers that are left */
11915 it = mPeer->mStorageControllers->begin();
11916 while (it != mPeer->mStorageControllers->end())
11917 {
11918 (*it)->uninit();
11919 ++it;
11920 }
11921
11922 /* attach new list of controllers to our peer */
11923 mPeer->mStorageControllers.attach(newList);
11924 }
11925 else
11926 {
11927 /* we have no peer (our parent is the newly created machine);
11928 * just commit changes to devices */
11929 commitStorageControllers = true;
11930 }
11931 }
11932 else
11933 {
11934 /* the list of controllers itself is not changed,
11935 * just commit changes to controllers themselves */
11936 commitStorageControllers = true;
11937 }
11938
11939 if (commitStorageControllers)
11940 {
11941 StorageControllerList::const_iterator it = mStorageControllers->begin();
11942 while (it != mStorageControllers->end())
11943 {
11944 (*it)->commit();
11945 ++it;
11946 }
11947 }
11948
11949 if (isSessionMachine())
11950 {
11951 /* attach new data to the primary machine and reshare it */
11952 mPeer->mUserData.attach(mUserData);
11953 mPeer->mHWData.attach(mHWData);
11954 /* mMediaData is reshared by fixupMedia */
11955 // mPeer->mMediaData.attach(mMediaData);
11956 Assert(mPeer->mMediaData.data() == mMediaData.data());
11957 }
11958}
11959
11960/**
11961 * Copies all the hardware data from the given machine.
11962 *
11963 * Currently, only called when the VM is being restored from a snapshot. In
11964 * particular, this implies that the VM is not running during this method's
11965 * call.
11966 *
11967 * @note This method must be called from under this object's lock.
11968 *
11969 * @note This method doesn't call #commit(), so all data remains backed up and
11970 * unsaved.
11971 */
11972void Machine::copyFrom(Machine *aThat)
11973{
11974 AssertReturnVoid(!isSnapshotMachine());
11975 AssertReturnVoid(aThat->isSnapshotMachine());
11976
11977 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11978
11979 mHWData.assignCopy(aThat->mHWData);
11980
11981 // create copies of all shared folders (mHWData after attaching a copy
11982 // contains just references to original objects)
11983 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11984 it != mHWData->mSharedFolders.end();
11985 ++it)
11986 {
11987 ComObjPtr<SharedFolder> folder;
11988 folder.createObject();
11989 HRESULT rc = folder->initCopy(getMachine(), *it);
11990 AssertComRC(rc);
11991 *it = folder;
11992 }
11993
11994 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11995 mVRDEServer->copyFrom(aThat->mVRDEServer);
11996 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11997 mUSBController->copyFrom(aThat->mUSBController);
11998 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11999
12000 /* create private copies of all controllers */
12001 mStorageControllers.backup();
12002 mStorageControllers->clear();
12003 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12004 it != aThat->mStorageControllers->end();
12005 ++it)
12006 {
12007 ComObjPtr<StorageController> ctrl;
12008 ctrl.createObject();
12009 ctrl->initCopy(this, *it);
12010 mStorageControllers->push_back(ctrl);
12011 }
12012
12013 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12014 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12015 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12016 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12017 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12018 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12019 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12020}
12021
12022/**
12023 * Returns whether the given storage controller is hotplug capable.
12024 *
12025 * @returns true if the controller supports hotplugging
12026 * false otherwise.
12027 * @param enmCtrlType The controller type to check for.
12028 */
12029bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12030{
12031 switch (enmCtrlType)
12032 {
12033 case StorageControllerType_IntelAhci:
12034 return true;
12035 case StorageControllerType_LsiLogic:
12036 case StorageControllerType_LsiLogicSas:
12037 case StorageControllerType_BusLogic:
12038 case StorageControllerType_PIIX3:
12039 case StorageControllerType_PIIX4:
12040 case StorageControllerType_ICH6:
12041 case StorageControllerType_I82078:
12042 default:
12043 return false;
12044 }
12045}
12046
12047#ifdef VBOX_WITH_RESOURCE_USAGE_API
12048
12049void Machine::getDiskList(MediaList &list)
12050{
12051 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12052 it != mMediaData->mAttachments.end();
12053 ++it)
12054 {
12055 MediumAttachment* pAttach = *it;
12056 /* just in case */
12057 AssertStmt(pAttach, continue);
12058
12059 AutoCaller localAutoCallerA(pAttach);
12060 if (FAILED(localAutoCallerA.rc())) continue;
12061
12062 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12063
12064 if (pAttach->getType() == DeviceType_HardDisk)
12065 list.push_back(pAttach->getMedium());
12066 }
12067}
12068
12069void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12070{
12071 AssertReturnVoid(isWriteLockOnCurrentThread());
12072 AssertPtrReturnVoid(aCollector);
12073
12074 pm::CollectorHAL *hal = aCollector->getHAL();
12075 /* Create sub metrics */
12076 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12077 "Percentage of processor time spent in user mode by the VM process.");
12078 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12079 "Percentage of processor time spent in kernel mode by the VM process.");
12080 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12081 "Size of resident portion of VM process in memory.");
12082 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12083 "Actual size of all VM disks combined.");
12084 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12085 "Network receive rate.");
12086 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12087 "Network transmit rate.");
12088 /* Create and register base metrics */
12089 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12090 cpuLoadUser, cpuLoadKernel);
12091 aCollector->registerBaseMetric(cpuLoad);
12092 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12093 ramUsageUsed);
12094 aCollector->registerBaseMetric(ramUsage);
12095 MediaList disks;
12096 getDiskList(disks);
12097 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12098 diskUsageUsed);
12099 aCollector->registerBaseMetric(diskUsage);
12100
12101 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12102 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12103 new pm::AggregateAvg()));
12104 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12105 new pm::AggregateMin()));
12106 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12107 new pm::AggregateMax()));
12108 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12109 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12110 new pm::AggregateAvg()));
12111 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12112 new pm::AggregateMin()));
12113 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12114 new pm::AggregateMax()));
12115
12116 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12117 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12118 new pm::AggregateAvg()));
12119 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12120 new pm::AggregateMin()));
12121 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12122 new pm::AggregateMax()));
12123
12124 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12125 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12126 new pm::AggregateAvg()));
12127 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12128 new pm::AggregateMin()));
12129 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12130 new pm::AggregateMax()));
12131
12132
12133 /* Guest metrics collector */
12134 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12135 aCollector->registerGuest(mCollectorGuest);
12136 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12137 this, __PRETTY_FUNCTION__, mCollectorGuest));
12138
12139 /* Create sub metrics */
12140 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12141 "Percentage of processor time spent in user mode as seen by the guest.");
12142 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12143 "Percentage of processor time spent in kernel mode as seen by the guest.");
12144 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12145 "Percentage of processor time spent idling as seen by the guest.");
12146
12147 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12148 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12149 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12150 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12151 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12152 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12153
12154 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12155
12156 /* Create and register base metrics */
12157 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12158 machineNetRx, machineNetTx);
12159 aCollector->registerBaseMetric(machineNetRate);
12160
12161 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12162 guestLoadUser, guestLoadKernel, guestLoadIdle);
12163 aCollector->registerBaseMetric(guestCpuLoad);
12164
12165 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12166 guestMemTotal, guestMemFree,
12167 guestMemBalloon, guestMemShared,
12168 guestMemCache, guestPagedTotal);
12169 aCollector->registerBaseMetric(guestCpuMem);
12170
12171 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12172 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12173 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12174 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12175
12176 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12177 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12178 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12179 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12180
12181 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12182 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12183 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12184 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12185
12186 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12187 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12188 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12189 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12190
12191 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12192 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12193 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12194 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12195
12196 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12197 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12198 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12199 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12200
12201 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12202 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12203 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12204 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12205
12206 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12207 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12208 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12209 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12210
12211 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12212 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12213 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12214 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12215
12216 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12217 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12218 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12219 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12220
12221 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12222 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12223 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12224 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12225}
12226
12227void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12228{
12229 AssertReturnVoid(isWriteLockOnCurrentThread());
12230
12231 if (aCollector)
12232 {
12233 aCollector->unregisterMetricsFor(aMachine);
12234 aCollector->unregisterBaseMetricsFor(aMachine);
12235 }
12236}
12237
12238#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12239
12240
12241////////////////////////////////////////////////////////////////////////////////
12242
12243DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12244
12245HRESULT SessionMachine::FinalConstruct()
12246{
12247 LogFlowThisFunc(("\n"));
12248
12249#if defined(RT_OS_WINDOWS)
12250 mIPCSem = NULL;
12251#elif defined(RT_OS_OS2)
12252 mIPCSem = NULLHANDLE;
12253#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12254 mIPCSem = -1;
12255#else
12256# error "Port me!"
12257#endif
12258
12259 return BaseFinalConstruct();
12260}
12261
12262void SessionMachine::FinalRelease()
12263{
12264 LogFlowThisFunc(("\n"));
12265
12266 uninit(Uninit::Unexpected);
12267
12268 BaseFinalRelease();
12269}
12270
12271/**
12272 * @note Must be called only by Machine::openSession() from its own write lock.
12273 */
12274HRESULT SessionMachine::init(Machine *aMachine)
12275{
12276 LogFlowThisFuncEnter();
12277 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12278
12279 AssertReturn(aMachine, E_INVALIDARG);
12280
12281 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12282
12283 /* Enclose the state transition NotReady->InInit->Ready */
12284 AutoInitSpan autoInitSpan(this);
12285 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12286
12287 /* create the interprocess semaphore */
12288#if defined(RT_OS_WINDOWS)
12289 mIPCSemName = aMachine->mData->m_strConfigFileFull;
12290 for (size_t i = 0; i < mIPCSemName.length(); i++)
12291 if (mIPCSemName.raw()[i] == '\\')
12292 mIPCSemName.raw()[i] = '/';
12293 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12294 ComAssertMsgRet(mIPCSem,
12295 ("Cannot create IPC mutex '%ls', err=%d",
12296 mIPCSemName.raw(), ::GetLastError()),
12297 E_FAIL);
12298#elif defined(RT_OS_OS2)
12299 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12300 aMachine->mData->mUuid.raw());
12301 mIPCSemName = ipcSem;
12302 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12303 ComAssertMsgRet(arc == NO_ERROR,
12304 ("Cannot create IPC mutex '%s', arc=%ld",
12305 ipcSem.c_str(), arc),
12306 E_FAIL);
12307#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12308# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12309# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12310 /** @todo Check that this still works correctly. */
12311 AssertCompileSize(key_t, 8);
12312# else
12313 AssertCompileSize(key_t, 4);
12314# endif
12315 key_t key;
12316 mIPCSem = -1;
12317 mIPCKey = "0";
12318 for (uint32_t i = 0; i < 1 << 24; i++)
12319 {
12320 key = ((uint32_t)'V' << 24) | i;
12321 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12322 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12323 {
12324 mIPCSem = sem;
12325 if (sem >= 0)
12326 mIPCKey = BstrFmt("%u", key);
12327 break;
12328 }
12329 }
12330# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12331 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12332 char *pszSemName = NULL;
12333 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12334 key_t key = ::ftok(pszSemName, 'V');
12335 RTStrFree(pszSemName);
12336
12337 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12338# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12339
12340 int errnoSave = errno;
12341 if (mIPCSem < 0 && errnoSave == ENOSYS)
12342 {
12343 setError(E_FAIL,
12344 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12345 "support for SysV IPC. Check the host kernel configuration for "
12346 "CONFIG_SYSVIPC=y"));
12347 return E_FAIL;
12348 }
12349 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12350 * the IPC semaphores */
12351 if (mIPCSem < 0 && errnoSave == ENOSPC)
12352 {
12353#ifdef RT_OS_LINUX
12354 setError(E_FAIL,
12355 tr("Cannot create IPC semaphore because the system limit for the "
12356 "maximum number of semaphore sets (SEMMNI), or the system wide "
12357 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12358 "current set of SysV IPC semaphores can be determined from "
12359 "the file /proc/sysvipc/sem"));
12360#else
12361 setError(E_FAIL,
12362 tr("Cannot create IPC semaphore because the system-imposed limit "
12363 "on the maximum number of allowed semaphores or semaphore "
12364 "identifiers system-wide would be exceeded"));
12365#endif
12366 return E_FAIL;
12367 }
12368 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12369 E_FAIL);
12370 /* set the initial value to 1 */
12371 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12372 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12373 E_FAIL);
12374#else
12375# error "Port me!"
12376#endif
12377
12378 /* memorize the peer Machine */
12379 unconst(mPeer) = aMachine;
12380 /* share the parent pointer */
12381 unconst(mParent) = aMachine->mParent;
12382
12383 /* take the pointers to data to share */
12384 mData.share(aMachine->mData);
12385 mSSData.share(aMachine->mSSData);
12386
12387 mUserData.share(aMachine->mUserData);
12388 mHWData.share(aMachine->mHWData);
12389 mMediaData.share(aMachine->mMediaData);
12390
12391 mStorageControllers.allocate();
12392 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12393 it != aMachine->mStorageControllers->end();
12394 ++it)
12395 {
12396 ComObjPtr<StorageController> ctl;
12397 ctl.createObject();
12398 ctl->init(this, *it);
12399 mStorageControllers->push_back(ctl);
12400 }
12401
12402 unconst(mBIOSSettings).createObject();
12403 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12404 /* create another VRDEServer object that will be mutable */
12405 unconst(mVRDEServer).createObject();
12406 mVRDEServer->init(this, aMachine->mVRDEServer);
12407 /* create another audio adapter object that will be mutable */
12408 unconst(mAudioAdapter).createObject();
12409 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12410 /* create a list of serial ports that will be mutable */
12411 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12412 {
12413 unconst(mSerialPorts[slot]).createObject();
12414 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12415 }
12416 /* create a list of parallel ports that will be mutable */
12417 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12418 {
12419 unconst(mParallelPorts[slot]).createObject();
12420 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12421 }
12422 /* create another USB controller object that will be mutable */
12423 unconst(mUSBController).createObject();
12424 mUSBController->init(this, aMachine->mUSBController);
12425
12426 /* create a list of network adapters that will be mutable */
12427 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12428 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12429 {
12430 unconst(mNetworkAdapters[slot]).createObject();
12431 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12432 }
12433
12434 /* create another bandwidth control object that will be mutable */
12435 unconst(mBandwidthControl).createObject();
12436 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12437
12438 /* default is to delete saved state on Saved -> PoweredOff transition */
12439 mRemoveSavedState = true;
12440
12441 /* Confirm a successful initialization when it's the case */
12442 autoInitSpan.setSucceeded();
12443
12444 LogFlowThisFuncLeave();
12445 return S_OK;
12446}
12447
12448/**
12449 * Uninitializes this session object. If the reason is other than
12450 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12451 *
12452 * @param aReason uninitialization reason
12453 *
12454 * @note Locks mParent + this object for writing.
12455 */
12456void SessionMachine::uninit(Uninit::Reason aReason)
12457{
12458 LogFlowThisFuncEnter();
12459 LogFlowThisFunc(("reason=%d\n", aReason));
12460
12461 /*
12462 * Strongly reference ourselves to prevent this object deletion after
12463 * mData->mSession.mMachine.setNull() below (which can release the last
12464 * reference and call the destructor). Important: this must be done before
12465 * accessing any members (and before AutoUninitSpan that does it as well).
12466 * This self reference will be released as the very last step on return.
12467 */
12468 ComObjPtr<SessionMachine> selfRef = this;
12469
12470 /* Enclose the state transition Ready->InUninit->NotReady */
12471 AutoUninitSpan autoUninitSpan(this);
12472 if (autoUninitSpan.uninitDone())
12473 {
12474 LogFlowThisFunc(("Already uninitialized\n"));
12475 LogFlowThisFuncLeave();
12476 return;
12477 }
12478
12479 if (autoUninitSpan.initFailed())
12480 {
12481 /* We've been called by init() because it's failed. It's not really
12482 * necessary (nor it's safe) to perform the regular uninit sequence
12483 * below, the following is enough.
12484 */
12485 LogFlowThisFunc(("Initialization failed.\n"));
12486#if defined(RT_OS_WINDOWS)
12487 if (mIPCSem)
12488 ::CloseHandle(mIPCSem);
12489 mIPCSem = NULL;
12490#elif defined(RT_OS_OS2)
12491 if (mIPCSem != NULLHANDLE)
12492 ::DosCloseMutexSem(mIPCSem);
12493 mIPCSem = NULLHANDLE;
12494#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12495 if (mIPCSem >= 0)
12496 ::semctl(mIPCSem, 0, IPC_RMID);
12497 mIPCSem = -1;
12498# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12499 mIPCKey = "0";
12500# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12501#else
12502# error "Port me!"
12503#endif
12504 uninitDataAndChildObjects();
12505 mData.free();
12506 unconst(mParent) = NULL;
12507 unconst(mPeer) = NULL;
12508 LogFlowThisFuncLeave();
12509 return;
12510 }
12511
12512 MachineState_T lastState;
12513 {
12514 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12515 lastState = mData->mMachineState;
12516 }
12517 NOREF(lastState);
12518
12519#ifdef VBOX_WITH_USB
12520 // release all captured USB devices, but do this before requesting the locks below
12521 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12522 {
12523 /* Console::captureUSBDevices() is called in the VM process only after
12524 * setting the machine state to Starting or Restoring.
12525 * Console::detachAllUSBDevices() will be called upon successful
12526 * termination. So, we need to release USB devices only if there was
12527 * an abnormal termination of a running VM.
12528 *
12529 * This is identical to SessionMachine::DetachAllUSBDevices except
12530 * for the aAbnormal argument. */
12531 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12532 AssertComRC(rc);
12533 NOREF(rc);
12534
12535 USBProxyService *service = mParent->host()->usbProxyService();
12536 if (service)
12537 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12538 }
12539#endif /* VBOX_WITH_USB */
12540
12541 // we need to lock this object in uninit() because the lock is shared
12542 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12543 // and others need mParent lock, and USB needs host lock.
12544 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12545
12546#if 0
12547 // Trigger async cleanup tasks, avoid doing things here which are not
12548 // vital to be done immediately and maybe need more locks. This calls
12549 // Machine::unregisterMetrics().
12550 mParent->onMachineUninit(mPeer);
12551#else
12552 /*
12553 * It is safe to call Machine::unregisterMetrics() here because
12554 * PerformanceCollector::samplerCallback no longer accesses guest methods
12555 * holding the lock.
12556 */
12557 unregisterMetrics(mParent->performanceCollector(), mPeer);
12558#endif
12559 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12560 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12561 this, __PRETTY_FUNCTION__, mCollectorGuest));
12562 if (mCollectorGuest)
12563 {
12564 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12565 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12566 mCollectorGuest = NULL;
12567 }
12568
12569 if (aReason == Uninit::Abnormal)
12570 {
12571 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12572 Global::IsOnlineOrTransient(lastState)));
12573
12574 /* reset the state to Aborted */
12575 if (mData->mMachineState != MachineState_Aborted)
12576 setMachineState(MachineState_Aborted);
12577 }
12578
12579 // any machine settings modified?
12580 if (mData->flModifications)
12581 {
12582 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12583 rollback(false /* aNotify */);
12584 }
12585
12586 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12587 || !mConsoleTaskData.mSnapshot);
12588 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12589 {
12590 LogWarningThisFunc(("canceling failed save state request!\n"));
12591 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12592 }
12593 else if (!mConsoleTaskData.mSnapshot.isNull())
12594 {
12595 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12596
12597 /* delete all differencing hard disks created (this will also attach
12598 * their parents back by rolling back mMediaData) */
12599 rollbackMedia();
12600
12601 // delete the saved state file (it might have been already created)
12602 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12603 // think it's still in use
12604 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12605 mConsoleTaskData.mSnapshot->uninit();
12606 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12607 }
12608
12609 if (!mData->mSession.mType.isEmpty())
12610 {
12611 /* mType is not null when this machine's process has been started by
12612 * Machine::LaunchVMProcess(), therefore it is our child. We
12613 * need to queue the PID to reap the process (and avoid zombies on
12614 * Linux). */
12615 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12616 mParent->addProcessToReap(mData->mSession.mPID);
12617 }
12618
12619 mData->mSession.mPID = NIL_RTPROCESS;
12620
12621 if (aReason == Uninit::Unexpected)
12622 {
12623 /* Uninitialization didn't come from #checkForDeath(), so tell the
12624 * client watcher thread to update the set of machines that have open
12625 * sessions. */
12626 mParent->updateClientWatcher();
12627 }
12628
12629 /* uninitialize all remote controls */
12630 if (mData->mSession.mRemoteControls.size())
12631 {
12632 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12633 mData->mSession.mRemoteControls.size()));
12634
12635 Data::Session::RemoteControlList::iterator it =
12636 mData->mSession.mRemoteControls.begin();
12637 while (it != mData->mSession.mRemoteControls.end())
12638 {
12639 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12640 HRESULT rc = (*it)->Uninitialize();
12641 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12642 if (FAILED(rc))
12643 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12644 ++it;
12645 }
12646 mData->mSession.mRemoteControls.clear();
12647 }
12648
12649 /*
12650 * An expected uninitialization can come only from #checkForDeath().
12651 * Otherwise it means that something's gone really wrong (for example,
12652 * the Session implementation has released the VirtualBox reference
12653 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12654 * etc). However, it's also possible, that the client releases the IPC
12655 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12656 * but the VirtualBox release event comes first to the server process.
12657 * This case is practically possible, so we should not assert on an
12658 * unexpected uninit, just log a warning.
12659 */
12660
12661 if ((aReason == Uninit::Unexpected))
12662 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12663
12664 if (aReason != Uninit::Normal)
12665 {
12666 mData->mSession.mDirectControl.setNull();
12667 }
12668 else
12669 {
12670 /* this must be null here (see #OnSessionEnd()) */
12671 Assert(mData->mSession.mDirectControl.isNull());
12672 Assert(mData->mSession.mState == SessionState_Unlocking);
12673 Assert(!mData->mSession.mProgress.isNull());
12674 }
12675 if (mData->mSession.mProgress)
12676 {
12677 if (aReason == Uninit::Normal)
12678 mData->mSession.mProgress->notifyComplete(S_OK);
12679 else
12680 mData->mSession.mProgress->notifyComplete(E_FAIL,
12681 COM_IIDOF(ISession),
12682 getComponentName(),
12683 tr("The VM session was aborted"));
12684 mData->mSession.mProgress.setNull();
12685 }
12686
12687 /* remove the association between the peer machine and this session machine */
12688 Assert( (SessionMachine*)mData->mSession.mMachine == this
12689 || aReason == Uninit::Unexpected);
12690
12691 /* reset the rest of session data */
12692 mData->mSession.mMachine.setNull();
12693 mData->mSession.mState = SessionState_Unlocked;
12694 mData->mSession.mType.setNull();
12695
12696 /* close the interprocess semaphore before leaving the exclusive lock */
12697#if defined(RT_OS_WINDOWS)
12698 if (mIPCSem)
12699 ::CloseHandle(mIPCSem);
12700 mIPCSem = NULL;
12701#elif defined(RT_OS_OS2)
12702 if (mIPCSem != NULLHANDLE)
12703 ::DosCloseMutexSem(mIPCSem);
12704 mIPCSem = NULLHANDLE;
12705#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12706 if (mIPCSem >= 0)
12707 ::semctl(mIPCSem, 0, IPC_RMID);
12708 mIPCSem = -1;
12709# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12710 mIPCKey = "0";
12711# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12712#else
12713# error "Port me!"
12714#endif
12715
12716 /* fire an event */
12717 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12718
12719 uninitDataAndChildObjects();
12720
12721 /* free the essential data structure last */
12722 mData.free();
12723
12724 /* release the exclusive lock before setting the below two to NULL */
12725 multilock.release();
12726
12727 unconst(mParent) = NULL;
12728 unconst(mPeer) = NULL;
12729
12730 LogFlowThisFuncLeave();
12731}
12732
12733// util::Lockable interface
12734////////////////////////////////////////////////////////////////////////////////
12735
12736/**
12737 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12738 * with the primary Machine instance (mPeer).
12739 */
12740RWLockHandle *SessionMachine::lockHandle() const
12741{
12742 AssertReturn(mPeer != NULL, NULL);
12743 return mPeer->lockHandle();
12744}
12745
12746// IInternalMachineControl methods
12747////////////////////////////////////////////////////////////////////////////////
12748
12749/**
12750 * Passes collected guest statistics to performance collector object
12751 */
12752STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12753 ULONG aCpuKernel, ULONG aCpuIdle,
12754 ULONG aMemTotal, ULONG aMemFree,
12755 ULONG aMemBalloon, ULONG aMemShared,
12756 ULONG aMemCache, ULONG aPageTotal,
12757 ULONG aAllocVMM, ULONG aFreeVMM,
12758 ULONG aBalloonedVMM, ULONG aSharedVMM,
12759 ULONG aVmNetRx, ULONG aVmNetTx)
12760{
12761 if (mCollectorGuest)
12762 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12763 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12764 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12765 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12766
12767 return S_OK;
12768}
12769
12770/**
12771 * @note Locks this object for writing.
12772 */
12773STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12774{
12775 AutoCaller autoCaller(this);
12776 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12777
12778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12779
12780 mRemoveSavedState = aRemove;
12781
12782 return S_OK;
12783}
12784
12785/**
12786 * @note Locks the same as #setMachineState() does.
12787 */
12788STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12789{
12790 return setMachineState(aMachineState);
12791}
12792
12793/**
12794 * @note Locks this object for reading.
12795 */
12796STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12797{
12798 AutoCaller autoCaller(this);
12799 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12800
12801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12802
12803#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12804 mIPCSemName.cloneTo(aId);
12805 return S_OK;
12806#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12807# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12808 mIPCKey.cloneTo(aId);
12809# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12810 mData->m_strConfigFileFull.cloneTo(aId);
12811# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12812 return S_OK;
12813#else
12814# error "Port me!"
12815#endif
12816}
12817
12818/**
12819 * @note Locks this object for writing.
12820 */
12821STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12822{
12823 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12824 AutoCaller autoCaller(this);
12825 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12826
12827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12828
12829 if (mData->mSession.mState != SessionState_Locked)
12830 return VBOX_E_INVALID_OBJECT_STATE;
12831
12832 if (!mData->mSession.mProgress.isNull())
12833 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12834
12835 LogFlowThisFunc(("returns S_OK.\n"));
12836 return S_OK;
12837}
12838
12839/**
12840 * @note Locks this object for writing.
12841 */
12842STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12843{
12844 AutoCaller autoCaller(this);
12845 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12846
12847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12848
12849 if (mData->mSession.mState != SessionState_Locked)
12850 return VBOX_E_INVALID_OBJECT_STATE;
12851
12852 /* Finalize the LaunchVMProcess progress object. */
12853 if (mData->mSession.mProgress)
12854 {
12855 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12856 mData->mSession.mProgress.setNull();
12857 }
12858
12859 if (SUCCEEDED((HRESULT)iResult))
12860 {
12861#ifdef VBOX_WITH_RESOURCE_USAGE_API
12862 /* The VM has been powered up successfully, so it makes sense
12863 * now to offer the performance metrics for a running machine
12864 * object. Doing it earlier wouldn't be safe. */
12865 registerMetrics(mParent->performanceCollector(), mPeer,
12866 mData->mSession.mPID);
12867#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12868 }
12869
12870 return S_OK;
12871}
12872
12873/**
12874 * @note Locks this object for writing.
12875 */
12876STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12877{
12878 LogFlowThisFuncEnter();
12879
12880 CheckComArgOutPointerValid(aProgress);
12881
12882 AutoCaller autoCaller(this);
12883 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12884
12885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12886
12887 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12888 E_FAIL);
12889
12890 /* create a progress object to track operation completion */
12891 ComObjPtr<Progress> pProgress;
12892 pProgress.createObject();
12893 pProgress->init(getVirtualBox(),
12894 static_cast<IMachine *>(this) /* aInitiator */,
12895 Bstr(tr("Stopping the virtual machine")).raw(),
12896 FALSE /* aCancelable */);
12897
12898 /* fill in the console task data */
12899 mConsoleTaskData.mLastState = mData->mMachineState;
12900 mConsoleTaskData.mProgress = pProgress;
12901
12902 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12903 setMachineState(MachineState_Stopping);
12904
12905 pProgress.queryInterfaceTo(aProgress);
12906
12907 return S_OK;
12908}
12909
12910/**
12911 * @note Locks this object for writing.
12912 */
12913STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12914{
12915 LogFlowThisFuncEnter();
12916
12917 AutoCaller autoCaller(this);
12918 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12919
12920 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12921
12922 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12923 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12924 && mConsoleTaskData.mLastState != MachineState_Null,
12925 E_FAIL);
12926
12927 /*
12928 * On failure, set the state to the state we had when BeginPoweringDown()
12929 * was called (this is expected by Console::PowerDown() and the associated
12930 * task). On success the VM process already changed the state to
12931 * MachineState_PoweredOff, so no need to do anything.
12932 */
12933 if (FAILED(iResult))
12934 setMachineState(mConsoleTaskData.mLastState);
12935
12936 /* notify the progress object about operation completion */
12937 Assert(mConsoleTaskData.mProgress);
12938 if (SUCCEEDED(iResult))
12939 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12940 else
12941 {
12942 Utf8Str strErrMsg(aErrMsg);
12943 if (strErrMsg.length())
12944 mConsoleTaskData.mProgress->notifyComplete(iResult,
12945 COM_IIDOF(ISession),
12946 getComponentName(),
12947 strErrMsg.c_str());
12948 else
12949 mConsoleTaskData.mProgress->notifyComplete(iResult);
12950 }
12951
12952 /* clear out the temporary saved state data */
12953 mConsoleTaskData.mLastState = MachineState_Null;
12954 mConsoleTaskData.mProgress.setNull();
12955
12956 LogFlowThisFuncLeave();
12957 return S_OK;
12958}
12959
12960
12961/**
12962 * Goes through the USB filters of the given machine to see if the given
12963 * device matches any filter or not.
12964 *
12965 * @note Locks the same as USBController::hasMatchingFilter() does.
12966 */
12967STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12968 BOOL *aMatched,
12969 ULONG *aMaskedIfs)
12970{
12971 LogFlowThisFunc(("\n"));
12972
12973 CheckComArgNotNull(aUSBDevice);
12974 CheckComArgOutPointerValid(aMatched);
12975
12976 AutoCaller autoCaller(this);
12977 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12978
12979#ifdef VBOX_WITH_USB
12980 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12981#else
12982 NOREF(aUSBDevice);
12983 NOREF(aMaskedIfs);
12984 *aMatched = FALSE;
12985#endif
12986
12987 return S_OK;
12988}
12989
12990/**
12991 * @note Locks the same as Host::captureUSBDevice() does.
12992 */
12993STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12994{
12995 LogFlowThisFunc(("\n"));
12996
12997 AutoCaller autoCaller(this);
12998 AssertComRCReturnRC(autoCaller.rc());
12999
13000#ifdef VBOX_WITH_USB
13001 /* if captureDeviceForVM() fails, it must have set extended error info */
13002 clearError();
13003 MultiResult rc = mParent->host()->checkUSBProxyService();
13004 if (FAILED(rc)) return rc;
13005
13006 USBProxyService *service = mParent->host()->usbProxyService();
13007 AssertReturn(service, E_FAIL);
13008 return service->captureDeviceForVM(this, Guid(aId).ref());
13009#else
13010 NOREF(aId);
13011 return E_NOTIMPL;
13012#endif
13013}
13014
13015/**
13016 * @note Locks the same as Host::detachUSBDevice() does.
13017 */
13018STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13019{
13020 LogFlowThisFunc(("\n"));
13021
13022 AutoCaller autoCaller(this);
13023 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13024
13025#ifdef VBOX_WITH_USB
13026 USBProxyService *service = mParent->host()->usbProxyService();
13027 AssertReturn(service, E_FAIL);
13028 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13029#else
13030 NOREF(aId);
13031 NOREF(aDone);
13032 return E_NOTIMPL;
13033#endif
13034}
13035
13036/**
13037 * Inserts all machine filters to the USB proxy service and then calls
13038 * Host::autoCaptureUSBDevices().
13039 *
13040 * Called by Console from the VM process upon VM startup.
13041 *
13042 * @note Locks what called methods lock.
13043 */
13044STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13045{
13046 LogFlowThisFunc(("\n"));
13047
13048 AutoCaller autoCaller(this);
13049 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13050
13051#ifdef VBOX_WITH_USB
13052 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
13053 AssertComRC(rc);
13054 NOREF(rc);
13055
13056 USBProxyService *service = mParent->host()->usbProxyService();
13057 AssertReturn(service, E_FAIL);
13058 return service->autoCaptureDevicesForVM(this);
13059#else
13060 return S_OK;
13061#endif
13062}
13063
13064/**
13065 * Removes all machine filters from the USB proxy service and then calls
13066 * Host::detachAllUSBDevices().
13067 *
13068 * Called by Console from the VM process upon normal VM termination or by
13069 * SessionMachine::uninit() upon abnormal VM termination (from under the
13070 * Machine/SessionMachine lock).
13071 *
13072 * @note Locks what called methods lock.
13073 */
13074STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13075{
13076 LogFlowThisFunc(("\n"));
13077
13078 AutoCaller autoCaller(this);
13079 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13080
13081#ifdef VBOX_WITH_USB
13082 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
13083 AssertComRC(rc);
13084 NOREF(rc);
13085
13086 USBProxyService *service = mParent->host()->usbProxyService();
13087 AssertReturn(service, E_FAIL);
13088 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13089#else
13090 NOREF(aDone);
13091 return S_OK;
13092#endif
13093}
13094
13095/**
13096 * @note Locks this object for writing.
13097 */
13098STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13099 IProgress **aProgress)
13100{
13101 LogFlowThisFuncEnter();
13102
13103 AssertReturn(aSession, E_INVALIDARG);
13104 AssertReturn(aProgress, E_INVALIDARG);
13105
13106 AutoCaller autoCaller(this);
13107
13108 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13109 /*
13110 * We don't assert below because it might happen that a non-direct session
13111 * informs us it is closed right after we've been uninitialized -- it's ok.
13112 */
13113 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13114
13115 /* get IInternalSessionControl interface */
13116 ComPtr<IInternalSessionControl> control(aSession);
13117
13118 ComAssertRet(!control.isNull(), E_INVALIDARG);
13119
13120 /* Creating a Progress object requires the VirtualBox lock, and
13121 * thus locking it here is required by the lock order rules. */
13122 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13123
13124 if (control == mData->mSession.mDirectControl)
13125 {
13126 ComAssertRet(aProgress, E_POINTER);
13127
13128 /* The direct session is being normally closed by the client process
13129 * ----------------------------------------------------------------- */
13130
13131 /* go to the closing state (essential for all open*Session() calls and
13132 * for #checkForDeath()) */
13133 Assert(mData->mSession.mState == SessionState_Locked);
13134 mData->mSession.mState = SessionState_Unlocking;
13135
13136 /* set direct control to NULL to release the remote instance */
13137 mData->mSession.mDirectControl.setNull();
13138 LogFlowThisFunc(("Direct control is set to NULL\n"));
13139
13140 if (mData->mSession.mProgress)
13141 {
13142 /* finalize the progress, someone might wait if a frontend
13143 * closes the session before powering on the VM. */
13144 mData->mSession.mProgress->notifyComplete(E_FAIL,
13145 COM_IIDOF(ISession),
13146 getComponentName(),
13147 tr("The VM session was closed before any attempt to power it on"));
13148 mData->mSession.mProgress.setNull();
13149 }
13150
13151 /* Create the progress object the client will use to wait until
13152 * #checkForDeath() is called to uninitialize this session object after
13153 * it releases the IPC semaphore.
13154 * Note! Because we're "reusing" mProgress here, this must be a proxy
13155 * object just like for LaunchVMProcess. */
13156 Assert(mData->mSession.mProgress.isNull());
13157 ComObjPtr<ProgressProxy> progress;
13158 progress.createObject();
13159 ComPtr<IUnknown> pPeer(mPeer);
13160 progress->init(mParent, pPeer,
13161 Bstr(tr("Closing session")).raw(),
13162 FALSE /* aCancelable */);
13163 progress.queryInterfaceTo(aProgress);
13164 mData->mSession.mProgress = progress;
13165 }
13166 else
13167 {
13168 /* the remote session is being normally closed */
13169 Data::Session::RemoteControlList::iterator it =
13170 mData->mSession.mRemoteControls.begin();
13171 while (it != mData->mSession.mRemoteControls.end())
13172 {
13173 if (control == *it)
13174 break;
13175 ++it;
13176 }
13177 BOOL found = it != mData->mSession.mRemoteControls.end();
13178 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13179 E_INVALIDARG);
13180 // This MUST be erase(it), not remove(*it) as the latter triggers a
13181 // very nasty use after free due to the place where the value "lives".
13182 mData->mSession.mRemoteControls.erase(it);
13183 }
13184
13185 /* signal the client watcher thread, because the client is going away */
13186 mParent->updateClientWatcher();
13187
13188 LogFlowThisFuncLeave();
13189 return S_OK;
13190}
13191
13192/**
13193 * @note Locks this object for writing.
13194 */
13195STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13196{
13197 LogFlowThisFuncEnter();
13198
13199 CheckComArgOutPointerValid(aProgress);
13200 CheckComArgOutPointerValid(aStateFilePath);
13201
13202 AutoCaller autoCaller(this);
13203 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13204
13205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13206
13207 AssertReturn( mData->mMachineState == MachineState_Paused
13208 && mConsoleTaskData.mLastState == MachineState_Null
13209 && mConsoleTaskData.strStateFilePath.isEmpty(),
13210 E_FAIL);
13211
13212 /* create a progress object to track operation completion */
13213 ComObjPtr<Progress> pProgress;
13214 pProgress.createObject();
13215 pProgress->init(getVirtualBox(),
13216 static_cast<IMachine *>(this) /* aInitiator */,
13217 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13218 FALSE /* aCancelable */);
13219
13220 Utf8Str strStateFilePath;
13221 /* stateFilePath is null when the machine is not running */
13222 if (mData->mMachineState == MachineState_Paused)
13223 composeSavedStateFilename(strStateFilePath);
13224
13225 /* fill in the console task data */
13226 mConsoleTaskData.mLastState = mData->mMachineState;
13227 mConsoleTaskData.strStateFilePath = strStateFilePath;
13228 mConsoleTaskData.mProgress = pProgress;
13229
13230 /* set the state to Saving (this is expected by Console::SaveState()) */
13231 setMachineState(MachineState_Saving);
13232
13233 strStateFilePath.cloneTo(aStateFilePath);
13234 pProgress.queryInterfaceTo(aProgress);
13235
13236 return S_OK;
13237}
13238
13239/**
13240 * @note Locks mParent + this object for writing.
13241 */
13242STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13243{
13244 LogFlowThisFunc(("\n"));
13245
13246 AutoCaller autoCaller(this);
13247 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13248
13249 /* endSavingState() need mParent lock */
13250 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13251
13252 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13253 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13254 && mConsoleTaskData.mLastState != MachineState_Null
13255 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13256 E_FAIL);
13257
13258 /*
13259 * On failure, set the state to the state we had when BeginSavingState()
13260 * was called (this is expected by Console::SaveState() and the associated
13261 * task). On success the VM process already changed the state to
13262 * MachineState_Saved, so no need to do anything.
13263 */
13264 if (FAILED(iResult))
13265 setMachineState(mConsoleTaskData.mLastState);
13266
13267 return endSavingState(iResult, aErrMsg);
13268}
13269
13270/**
13271 * @note Locks this object for writing.
13272 */
13273STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13274{
13275 LogFlowThisFunc(("\n"));
13276
13277 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13278
13279 AutoCaller autoCaller(this);
13280 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13281
13282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13283
13284 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13285 || mData->mMachineState == MachineState_Teleported
13286 || mData->mMachineState == MachineState_Aborted
13287 , E_FAIL); /** @todo setError. */
13288
13289 Utf8Str stateFilePathFull = aSavedStateFile;
13290 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13291 if (RT_FAILURE(vrc))
13292 return setError(VBOX_E_FILE_ERROR,
13293 tr("Invalid saved state file path '%ls' (%Rrc)"),
13294 aSavedStateFile,
13295 vrc);
13296
13297 mSSData->strStateFilePath = stateFilePathFull;
13298
13299 /* The below setMachineState() will detect the state transition and will
13300 * update the settings file */
13301
13302 return setMachineState(MachineState_Saved);
13303}
13304
13305STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13306 ComSafeArrayOut(BSTR, aValues),
13307 ComSafeArrayOut(LONG64, aTimestamps),
13308 ComSafeArrayOut(BSTR, aFlags))
13309{
13310 LogFlowThisFunc(("\n"));
13311
13312#ifdef VBOX_WITH_GUEST_PROPS
13313 using namespace guestProp;
13314
13315 AutoCaller autoCaller(this);
13316 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13317
13318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13319
13320 CheckComArgOutSafeArrayPointerValid(aNames);
13321 CheckComArgOutSafeArrayPointerValid(aValues);
13322 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13323 CheckComArgOutSafeArrayPointerValid(aFlags);
13324
13325 size_t cEntries = mHWData->mGuestProperties.size();
13326 com::SafeArray<BSTR> names(cEntries);
13327 com::SafeArray<BSTR> values(cEntries);
13328 com::SafeArray<LONG64> timestamps(cEntries);
13329 com::SafeArray<BSTR> flags(cEntries);
13330 unsigned i = 0;
13331 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13332 it != mHWData->mGuestProperties.end();
13333 ++it)
13334 {
13335 char szFlags[MAX_FLAGS_LEN + 1];
13336 it->first.cloneTo(&names[i]);
13337 it->second.strValue.cloneTo(&values[i]);
13338 timestamps[i] = it->second.mTimestamp;
13339 /* If it is NULL, keep it NULL. */
13340 if (it->second.mFlags)
13341 {
13342 writeFlags(it->second.mFlags, szFlags);
13343 Bstr(szFlags).cloneTo(&flags[i]);
13344 }
13345 else
13346 flags[i] = NULL;
13347 ++i;
13348 }
13349 names.detachTo(ComSafeArrayOutArg(aNames));
13350 values.detachTo(ComSafeArrayOutArg(aValues));
13351 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13352 flags.detachTo(ComSafeArrayOutArg(aFlags));
13353 return S_OK;
13354#else
13355 ReturnComNotImplemented();
13356#endif
13357}
13358
13359STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13360 IN_BSTR aValue,
13361 LONG64 aTimestamp,
13362 IN_BSTR aFlags)
13363{
13364 LogFlowThisFunc(("\n"));
13365
13366#ifdef VBOX_WITH_GUEST_PROPS
13367 using namespace guestProp;
13368
13369 CheckComArgStrNotEmptyOrNull(aName);
13370 CheckComArgNotNull(aValue);
13371 CheckComArgNotNull(aFlags);
13372
13373 try
13374 {
13375 /*
13376 * Convert input up front.
13377 */
13378 Utf8Str utf8Name(aName);
13379 uint32_t fFlags = NILFLAG;
13380 if (aFlags)
13381 {
13382 Utf8Str utf8Flags(aFlags);
13383 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13384 AssertRCReturn(vrc, E_INVALIDARG);
13385 }
13386
13387 /*
13388 * Now grab the object lock, validate the state and do the update.
13389 */
13390 AutoCaller autoCaller(this);
13391 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13392
13393 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13394
13395 switch (mData->mMachineState)
13396 {
13397 case MachineState_Paused:
13398 case MachineState_Running:
13399 case MachineState_Teleporting:
13400 case MachineState_TeleportingPausedVM:
13401 case MachineState_LiveSnapshotting:
13402 case MachineState_DeletingSnapshotOnline:
13403 case MachineState_DeletingSnapshotPaused:
13404 case MachineState_Saving:
13405 case MachineState_Stopping:
13406 break;
13407
13408 default:
13409 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13410 VBOX_E_INVALID_VM_STATE);
13411 }
13412
13413 setModified(IsModified_MachineData);
13414 mHWData.backup();
13415
13416 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13417 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13418 if (it != mHWData->mGuestProperties.end())
13419 {
13420 if (!fDelete)
13421 {
13422 it->second.strValue = aValue;
13423 it->second.mTimestamp = aTimestamp;
13424 it->second.mFlags = fFlags;
13425 }
13426 else
13427 mHWData->mGuestProperties.erase(it);
13428
13429 mData->mGuestPropertiesModified = TRUE;
13430 }
13431 else if (!fDelete)
13432 {
13433 HWData::GuestProperty prop;
13434 prop.strValue = aValue;
13435 prop.mTimestamp = aTimestamp;
13436 prop.mFlags = fFlags;
13437
13438 mHWData->mGuestProperties[utf8Name] = prop;
13439 mData->mGuestPropertiesModified = TRUE;
13440 }
13441
13442 /*
13443 * Send a callback notification if appropriate
13444 */
13445 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13446 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13447 RTSTR_MAX,
13448 utf8Name.c_str(),
13449 RTSTR_MAX, NULL)
13450 )
13451 {
13452 alock.release();
13453
13454 mParent->onGuestPropertyChange(mData->mUuid,
13455 aName,
13456 aValue,
13457 aFlags);
13458 }
13459 }
13460 catch (...)
13461 {
13462 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13463 }
13464 return S_OK;
13465#else
13466 ReturnComNotImplemented();
13467#endif
13468}
13469
13470STDMETHODIMP SessionMachine::LockMedia()
13471{
13472 AutoCaller autoCaller(this);
13473 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13474
13475 AutoMultiWriteLock2 alock(this->lockHandle(),
13476 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13477
13478 AssertReturn( mData->mMachineState == MachineState_Starting
13479 || mData->mMachineState == MachineState_Restoring
13480 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13481
13482 clearError();
13483 alock.release();
13484 return lockMedia();
13485}
13486
13487STDMETHODIMP SessionMachine::UnlockMedia()
13488{
13489 unlockMedia();
13490 return S_OK;
13491}
13492
13493STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13494 IMediumAttachment **aNewAttachment)
13495{
13496 CheckComArgNotNull(aAttachment);
13497 CheckComArgOutPointerValid(aNewAttachment);
13498
13499 AutoCaller autoCaller(this);
13500 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13501
13502 // request the host lock first, since might be calling Host methods for getting host drives;
13503 // next, protect the media tree all the while we're in here, as well as our member variables
13504 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13505 this->lockHandle(),
13506 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13507
13508 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13509
13510 Bstr ctrlName;
13511 LONG lPort;
13512 LONG lDevice;
13513 bool fTempEject;
13514 {
13515 AutoCaller autoAttachCaller(this);
13516 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13517
13518 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13519
13520 /* Need to query the details first, as the IMediumAttachment reference
13521 * might be to the original settings, which we are going to change. */
13522 ctrlName = pAttach->getControllerName();
13523 lPort = pAttach->getPort();
13524 lDevice = pAttach->getDevice();
13525 fTempEject = pAttach->getTempEject();
13526 }
13527
13528 if (!fTempEject)
13529 {
13530 /* Remember previously mounted medium. The medium before taking the
13531 * backup is not necessarily the same thing. */
13532 ComObjPtr<Medium> oldmedium;
13533 oldmedium = pAttach->getMedium();
13534
13535 setModified(IsModified_Storage);
13536 mMediaData.backup();
13537
13538 // The backup operation makes the pAttach reference point to the
13539 // old settings. Re-get the correct reference.
13540 pAttach = findAttachment(mMediaData->mAttachments,
13541 ctrlName.raw(),
13542 lPort,
13543 lDevice);
13544
13545 {
13546 AutoCaller autoAttachCaller(this);
13547 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13548
13549 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13550 if (!oldmedium.isNull())
13551 oldmedium->removeBackReference(mData->mUuid);
13552
13553 pAttach->updateMedium(NULL);
13554 pAttach->updateEjected();
13555 }
13556
13557 setModified(IsModified_Storage);
13558 }
13559 else
13560 {
13561 {
13562 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13563 pAttach->updateEjected();
13564 }
13565 }
13566
13567 pAttach.queryInterfaceTo(aNewAttachment);
13568
13569 return S_OK;
13570}
13571
13572// public methods only for internal purposes
13573/////////////////////////////////////////////////////////////////////////////
13574
13575/**
13576 * Called from the client watcher thread to check for expected or unexpected
13577 * death of the client process that has a direct session to this machine.
13578 *
13579 * On Win32 and on OS/2, this method is called only when we've got the
13580 * mutex (i.e. the client has either died or terminated normally) so it always
13581 * returns @c true (the client is terminated, the session machine is
13582 * uninitialized).
13583 *
13584 * On other platforms, the method returns @c true if the client process has
13585 * terminated normally or abnormally and the session machine was uninitialized,
13586 * and @c false if the client process is still alive.
13587 *
13588 * @note Locks this object for writing.
13589 */
13590bool SessionMachine::checkForDeath()
13591{
13592 Uninit::Reason reason;
13593 bool terminated = false;
13594
13595 /* Enclose autoCaller with a block because calling uninit() from under it
13596 * will deadlock. */
13597 {
13598 AutoCaller autoCaller(this);
13599 if (!autoCaller.isOk())
13600 {
13601 /* return true if not ready, to cause the client watcher to exclude
13602 * the corresponding session from watching */
13603 LogFlowThisFunc(("Already uninitialized!\n"));
13604 return true;
13605 }
13606
13607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13608
13609 /* Determine the reason of death: if the session state is Closing here,
13610 * everything is fine. Otherwise it means that the client did not call
13611 * OnSessionEnd() before it released the IPC semaphore. This may happen
13612 * either because the client process has abnormally terminated, or
13613 * because it simply forgot to call ISession::Close() before exiting. We
13614 * threat the latter also as an abnormal termination (see
13615 * Session::uninit() for details). */
13616 reason = mData->mSession.mState == SessionState_Unlocking ?
13617 Uninit::Normal :
13618 Uninit::Abnormal;
13619
13620#if defined(RT_OS_WINDOWS)
13621
13622 AssertMsg(mIPCSem, ("semaphore must be created"));
13623
13624 /* release the IPC mutex */
13625 ::ReleaseMutex(mIPCSem);
13626
13627 terminated = true;
13628
13629#elif defined(RT_OS_OS2)
13630
13631 AssertMsg(mIPCSem, ("semaphore must be created"));
13632
13633 /* release the IPC mutex */
13634 ::DosReleaseMutexSem(mIPCSem);
13635
13636 terminated = true;
13637
13638#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13639
13640 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13641
13642 int val = ::semctl(mIPCSem, 0, GETVAL);
13643 if (val > 0)
13644 {
13645 /* the semaphore is signaled, meaning the session is terminated */
13646 terminated = true;
13647 }
13648
13649#else
13650# error "Port me!"
13651#endif
13652
13653 } /* AutoCaller block */
13654
13655 if (terminated)
13656 uninit(reason);
13657
13658 return terminated;
13659}
13660
13661/**
13662 * @note Locks this object for reading.
13663 */
13664HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13665{
13666 LogFlowThisFunc(("\n"));
13667
13668 AutoCaller autoCaller(this);
13669 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13670
13671 ComPtr<IInternalSessionControl> directControl;
13672 {
13673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13674 directControl = mData->mSession.mDirectControl;
13675 }
13676
13677 /* ignore notifications sent after #OnSessionEnd() is called */
13678 if (!directControl)
13679 return S_OK;
13680
13681 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13682}
13683
13684/**
13685 * @note Locks this object for reading.
13686 */
13687HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13688 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13689{
13690 LogFlowThisFunc(("\n"));
13691
13692 AutoCaller autoCaller(this);
13693 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13694
13695 ComPtr<IInternalSessionControl> directControl;
13696 {
13697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13698 directControl = mData->mSession.mDirectControl;
13699 }
13700
13701 /* ignore notifications sent after #OnSessionEnd() is called */
13702 if (!directControl)
13703 return S_OK;
13704 /*
13705 * instead acting like callback we ask IVirtualBox deliver corresponding event
13706 */
13707
13708 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13709 return S_OK;
13710}
13711
13712/**
13713 * @note Locks this object for reading.
13714 */
13715HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13716{
13717 LogFlowThisFunc(("\n"));
13718
13719 AutoCaller autoCaller(this);
13720 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13721
13722 ComPtr<IInternalSessionControl> directControl;
13723 {
13724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13725 directControl = mData->mSession.mDirectControl;
13726 }
13727
13728 /* ignore notifications sent after #OnSessionEnd() is called */
13729 if (!directControl)
13730 return S_OK;
13731
13732 return directControl->OnSerialPortChange(serialPort);
13733}
13734
13735/**
13736 * @note Locks this object for reading.
13737 */
13738HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13739{
13740 LogFlowThisFunc(("\n"));
13741
13742 AutoCaller autoCaller(this);
13743 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13744
13745 ComPtr<IInternalSessionControl> directControl;
13746 {
13747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13748 directControl = mData->mSession.mDirectControl;
13749 }
13750
13751 /* ignore notifications sent after #OnSessionEnd() is called */
13752 if (!directControl)
13753 return S_OK;
13754
13755 return directControl->OnParallelPortChange(parallelPort);
13756}
13757
13758/**
13759 * @note Locks this object for reading.
13760 */
13761HRESULT SessionMachine::onStorageControllerChange()
13762{
13763 LogFlowThisFunc(("\n"));
13764
13765 AutoCaller autoCaller(this);
13766 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13767
13768 ComPtr<IInternalSessionControl> directControl;
13769 {
13770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13771 directControl = mData->mSession.mDirectControl;
13772 }
13773
13774 /* ignore notifications sent after #OnSessionEnd() is called */
13775 if (!directControl)
13776 return S_OK;
13777
13778 return directControl->OnStorageControllerChange();
13779}
13780
13781/**
13782 * @note Locks this object for reading.
13783 */
13784HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13785{
13786 LogFlowThisFunc(("\n"));
13787
13788 AutoCaller autoCaller(this);
13789 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13790
13791 ComPtr<IInternalSessionControl> directControl;
13792 {
13793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13794 directControl = mData->mSession.mDirectControl;
13795 }
13796
13797 /* ignore notifications sent after #OnSessionEnd() is called */
13798 if (!directControl)
13799 return S_OK;
13800
13801 return directControl->OnMediumChange(aAttachment, aForce);
13802}
13803
13804/**
13805 * @note Locks this object for reading.
13806 */
13807HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13808{
13809 LogFlowThisFunc(("\n"));
13810
13811 AutoCaller autoCaller(this);
13812 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13813
13814 ComPtr<IInternalSessionControl> directControl;
13815 {
13816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13817 directControl = mData->mSession.mDirectControl;
13818 }
13819
13820 /* ignore notifications sent after #OnSessionEnd() is called */
13821 if (!directControl)
13822 return S_OK;
13823
13824 return directControl->OnCPUChange(aCPU, aRemove);
13825}
13826
13827HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13828{
13829 LogFlowThisFunc(("\n"));
13830
13831 AutoCaller autoCaller(this);
13832 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13833
13834 ComPtr<IInternalSessionControl> directControl;
13835 {
13836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13837 directControl = mData->mSession.mDirectControl;
13838 }
13839
13840 /* ignore notifications sent after #OnSessionEnd() is called */
13841 if (!directControl)
13842 return S_OK;
13843
13844 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13845}
13846
13847/**
13848 * @note Locks this object for reading.
13849 */
13850HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13851{
13852 LogFlowThisFunc(("\n"));
13853
13854 AutoCaller autoCaller(this);
13855 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13856
13857 ComPtr<IInternalSessionControl> directControl;
13858 {
13859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13860 directControl = mData->mSession.mDirectControl;
13861 }
13862
13863 /* ignore notifications sent after #OnSessionEnd() is called */
13864 if (!directControl)
13865 return S_OK;
13866
13867 return directControl->OnVRDEServerChange(aRestart);
13868}
13869
13870/**
13871 * @note Locks this object for reading.
13872 */
13873HRESULT SessionMachine::onVideoCaptureChange()
13874{
13875 LogFlowThisFunc(("\n"));
13876
13877 AutoCaller autoCaller(this);
13878 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13879
13880 ComPtr<IInternalSessionControl> directControl;
13881 {
13882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13883 directControl = mData->mSession.mDirectControl;
13884 }
13885
13886 /* ignore notifications sent after #OnSessionEnd() is called */
13887 if (!directControl)
13888 return S_OK;
13889
13890 return directControl->OnVideoCaptureChange();
13891}
13892
13893/**
13894 * @note Locks this object for reading.
13895 */
13896HRESULT SessionMachine::onUSBControllerChange()
13897{
13898 LogFlowThisFunc(("\n"));
13899
13900 AutoCaller autoCaller(this);
13901 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13902
13903 ComPtr<IInternalSessionControl> directControl;
13904 {
13905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13906 directControl = mData->mSession.mDirectControl;
13907 }
13908
13909 /* ignore notifications sent after #OnSessionEnd() is called */
13910 if (!directControl)
13911 return S_OK;
13912
13913 return directControl->OnUSBControllerChange();
13914}
13915
13916/**
13917 * @note Locks this object for reading.
13918 */
13919HRESULT SessionMachine::onSharedFolderChange()
13920{
13921 LogFlowThisFunc(("\n"));
13922
13923 AutoCaller autoCaller(this);
13924 AssertComRCReturnRC(autoCaller.rc());
13925
13926 ComPtr<IInternalSessionControl> directControl;
13927 {
13928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13929 directControl = mData->mSession.mDirectControl;
13930 }
13931
13932 /* ignore notifications sent after #OnSessionEnd() is called */
13933 if (!directControl)
13934 return S_OK;
13935
13936 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13937}
13938
13939/**
13940 * @note Locks this object for reading.
13941 */
13942HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13943{
13944 LogFlowThisFunc(("\n"));
13945
13946 AutoCaller autoCaller(this);
13947 AssertComRCReturnRC(autoCaller.rc());
13948
13949 ComPtr<IInternalSessionControl> directControl;
13950 {
13951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13952 directControl = mData->mSession.mDirectControl;
13953 }
13954
13955 /* ignore notifications sent after #OnSessionEnd() is called */
13956 if (!directControl)
13957 return S_OK;
13958
13959 return directControl->OnClipboardModeChange(aClipboardMode);
13960}
13961
13962/**
13963 * @note Locks this object for reading.
13964 */
13965HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13966{
13967 LogFlowThisFunc(("\n"));
13968
13969 AutoCaller autoCaller(this);
13970 AssertComRCReturnRC(autoCaller.rc());
13971
13972 ComPtr<IInternalSessionControl> directControl;
13973 {
13974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13975 directControl = mData->mSession.mDirectControl;
13976 }
13977
13978 /* ignore notifications sent after #OnSessionEnd() is called */
13979 if (!directControl)
13980 return S_OK;
13981
13982 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13983}
13984
13985/**
13986 * @note Locks this object for reading.
13987 */
13988HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13989{
13990 LogFlowThisFunc(("\n"));
13991
13992 AutoCaller autoCaller(this);
13993 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13994
13995 ComPtr<IInternalSessionControl> directControl;
13996 {
13997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13998 directControl = mData->mSession.mDirectControl;
13999 }
14000
14001 /* ignore notifications sent after #OnSessionEnd() is called */
14002 if (!directControl)
14003 return S_OK;
14004
14005 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14006}
14007
14008/**
14009 * @note Locks this object for reading.
14010 */
14011HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14012{
14013 LogFlowThisFunc(("\n"));
14014
14015 AutoCaller autoCaller(this);
14016 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14017
14018 ComPtr<IInternalSessionControl> directControl;
14019 {
14020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14021 directControl = mData->mSession.mDirectControl;
14022 }
14023
14024 /* ignore notifications sent after #OnSessionEnd() is called */
14025 if (!directControl)
14026 return S_OK;
14027
14028 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14029}
14030
14031/**
14032 * Returns @c true if this machine's USB controller reports it has a matching
14033 * filter for the given USB device and @c false otherwise.
14034 *
14035 * @note locks this object for reading.
14036 */
14037bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14038{
14039 AutoCaller autoCaller(this);
14040 /* silently return if not ready -- this method may be called after the
14041 * direct machine session has been called */
14042 if (!autoCaller.isOk())
14043 return false;
14044
14045#ifdef VBOX_WITH_USB
14046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14047
14048 switch (mData->mMachineState)
14049 {
14050 case MachineState_Starting:
14051 case MachineState_Restoring:
14052 case MachineState_TeleportingIn:
14053 case MachineState_Paused:
14054 case MachineState_Running:
14055 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14056 * elsewhere... */
14057 alock.release();
14058 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
14059 default: break;
14060 }
14061#else
14062 NOREF(aDevice);
14063 NOREF(aMaskedIfs);
14064#endif
14065 return false;
14066}
14067
14068/**
14069 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14070 */
14071HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14072 IVirtualBoxErrorInfo *aError,
14073 ULONG aMaskedIfs)
14074{
14075 LogFlowThisFunc(("\n"));
14076
14077 AutoCaller autoCaller(this);
14078
14079 /* This notification may happen after the machine object has been
14080 * uninitialized (the session was closed), so don't assert. */
14081 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14082
14083 ComPtr<IInternalSessionControl> directControl;
14084 {
14085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14086 directControl = mData->mSession.mDirectControl;
14087 }
14088
14089 /* fail on notifications sent after #OnSessionEnd() is called, it is
14090 * expected by the caller */
14091 if (!directControl)
14092 return E_FAIL;
14093
14094 /* No locks should be held at this point. */
14095 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14096 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14097
14098 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14099}
14100
14101/**
14102 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14103 */
14104HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14105 IVirtualBoxErrorInfo *aError)
14106{
14107 LogFlowThisFunc(("\n"));
14108
14109 AutoCaller autoCaller(this);
14110
14111 /* This notification may happen after the machine object has been
14112 * uninitialized (the session was closed), so don't assert. */
14113 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14114
14115 ComPtr<IInternalSessionControl> directControl;
14116 {
14117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14118 directControl = mData->mSession.mDirectControl;
14119 }
14120
14121 /* fail on notifications sent after #OnSessionEnd() is called, it is
14122 * expected by the caller */
14123 if (!directControl)
14124 return E_FAIL;
14125
14126 /* No locks should be held at this point. */
14127 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14128 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14129
14130 return directControl->OnUSBDeviceDetach(aId, aError);
14131}
14132
14133// protected methods
14134/////////////////////////////////////////////////////////////////////////////
14135
14136/**
14137 * Helper method to finalize saving the state.
14138 *
14139 * @note Must be called from under this object's lock.
14140 *
14141 * @param aRc S_OK if the snapshot has been taken successfully
14142 * @param aErrMsg human readable error message for failure
14143 *
14144 * @note Locks mParent + this objects for writing.
14145 */
14146HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14147{
14148 LogFlowThisFuncEnter();
14149
14150 AutoCaller autoCaller(this);
14151 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14152
14153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14154
14155 HRESULT rc = S_OK;
14156
14157 if (SUCCEEDED(aRc))
14158 {
14159 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14160
14161 /* save all VM settings */
14162 rc = saveSettings(NULL);
14163 // no need to check whether VirtualBox.xml needs saving also since
14164 // we can't have a name change pending at this point
14165 }
14166 else
14167 {
14168 // delete the saved state file (it might have been already created);
14169 // we need not check whether this is shared with a snapshot here because
14170 // we certainly created this saved state file here anew
14171 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14172 }
14173
14174 /* notify the progress object about operation completion */
14175 Assert(mConsoleTaskData.mProgress);
14176 if (SUCCEEDED(aRc))
14177 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14178 else
14179 {
14180 if (aErrMsg.length())
14181 mConsoleTaskData.mProgress->notifyComplete(aRc,
14182 COM_IIDOF(ISession),
14183 getComponentName(),
14184 aErrMsg.c_str());
14185 else
14186 mConsoleTaskData.mProgress->notifyComplete(aRc);
14187 }
14188
14189 /* clear out the temporary saved state data */
14190 mConsoleTaskData.mLastState = MachineState_Null;
14191 mConsoleTaskData.strStateFilePath.setNull();
14192 mConsoleTaskData.mProgress.setNull();
14193
14194 LogFlowThisFuncLeave();
14195 return rc;
14196}
14197
14198/**
14199 * Deletes the given file if it is no longer in use by either the current machine state
14200 * (if the machine is "saved") or any of the machine's snapshots.
14201 *
14202 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14203 * but is different for each SnapshotMachine. When calling this, the order of calling this
14204 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14205 * is therefore critical. I know, it's all rather messy.
14206 *
14207 * @param strStateFile
14208 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14209 */
14210void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14211 Snapshot *pSnapshotToIgnore)
14212{
14213 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14214 if ( (strStateFile.isNotEmpty())
14215 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14216 )
14217 // ... and it must also not be shared with other snapshots
14218 if ( !mData->mFirstSnapshot
14219 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14220 // this checks the SnapshotMachine's state file paths
14221 )
14222 RTFileDelete(strStateFile.c_str());
14223}
14224
14225/**
14226 * Locks the attached media.
14227 *
14228 * All attached hard disks are locked for writing and DVD/floppy are locked for
14229 * reading. Parents of attached hard disks (if any) are locked for reading.
14230 *
14231 * This method also performs accessibility check of all media it locks: if some
14232 * media is inaccessible, the method will return a failure and a bunch of
14233 * extended error info objects per each inaccessible medium.
14234 *
14235 * Note that this method is atomic: if it returns a success, all media are
14236 * locked as described above; on failure no media is locked at all (all
14237 * succeeded individual locks will be undone).
14238 *
14239 * The caller is responsible for doing the necessary state sanity checks.
14240 *
14241 * The locks made by this method must be undone by calling #unlockMedia() when
14242 * no more needed.
14243 */
14244HRESULT SessionMachine::lockMedia()
14245{
14246 AutoCaller autoCaller(this);
14247 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14248
14249 AutoMultiWriteLock2 alock(this->lockHandle(),
14250 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14251
14252 /* bail out if trying to lock things with already set up locking */
14253 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14254
14255 MultiResult mrc(S_OK);
14256
14257 /* Collect locking information for all medium objects attached to the VM. */
14258 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14259 it != mMediaData->mAttachments.end();
14260 ++it)
14261 {
14262 MediumAttachment* pAtt = *it;
14263 DeviceType_T devType = pAtt->getType();
14264 Medium *pMedium = pAtt->getMedium();
14265
14266 MediumLockList *pMediumLockList(new MediumLockList());
14267 // There can be attachments without a medium (floppy/dvd), and thus
14268 // it's impossible to create a medium lock list. It still makes sense
14269 // to have the empty medium lock list in the map in case a medium is
14270 // attached later.
14271 if (pMedium != NULL)
14272 {
14273 MediumType_T mediumType = pMedium->getType();
14274 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14275 || mediumType == MediumType_Shareable;
14276 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14277
14278 alock.release();
14279 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14280 !fIsReadOnlyLock /* fMediumLockWrite */,
14281 NULL,
14282 *pMediumLockList);
14283 alock.acquire();
14284 if (FAILED(mrc))
14285 {
14286 delete pMediumLockList;
14287 mData->mSession.mLockedMedia.Clear();
14288 break;
14289 }
14290 }
14291
14292 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14293 if (FAILED(rc))
14294 {
14295 mData->mSession.mLockedMedia.Clear();
14296 mrc = setError(rc,
14297 tr("Collecting locking information for all attached media failed"));
14298 break;
14299 }
14300 }
14301
14302 if (SUCCEEDED(mrc))
14303 {
14304 /* Now lock all media. If this fails, nothing is locked. */
14305 alock.release();
14306 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14307 alock.acquire();
14308 if (FAILED(rc))
14309 {
14310 mrc = setError(rc,
14311 tr("Locking of attached media failed"));
14312 }
14313 }
14314
14315 return mrc;
14316}
14317
14318/**
14319 * Undoes the locks made by by #lockMedia().
14320 */
14321void SessionMachine::unlockMedia()
14322{
14323 AutoCaller autoCaller(this);
14324 AssertComRCReturnVoid(autoCaller.rc());
14325
14326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14327
14328 /* we may be holding important error info on the current thread;
14329 * preserve it */
14330 ErrorInfoKeeper eik;
14331
14332 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14333 AssertComRC(rc);
14334}
14335
14336/**
14337 * Helper to change the machine state (reimplementation).
14338 *
14339 * @note Locks this object for writing.
14340 * @note This method must not call saveSettings or SaveSettings, otherwise
14341 * it can cause crashes in random places due to unexpectedly committing
14342 * the current settings. The caller is responsible for that. The call
14343 * to saveStateSettings is fine, because this method does not commit.
14344 */
14345HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14346{
14347 LogFlowThisFuncEnter();
14348 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14349
14350 AutoCaller autoCaller(this);
14351 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14352
14353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14354
14355 MachineState_T oldMachineState = mData->mMachineState;
14356
14357 AssertMsgReturn(oldMachineState != aMachineState,
14358 ("oldMachineState=%s, aMachineState=%s\n",
14359 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14360 E_FAIL);
14361
14362 HRESULT rc = S_OK;
14363
14364 int stsFlags = 0;
14365 bool deleteSavedState = false;
14366
14367 /* detect some state transitions */
14368
14369 if ( ( oldMachineState == MachineState_Saved
14370 && aMachineState == MachineState_Restoring)
14371 || ( ( oldMachineState == MachineState_PoweredOff
14372 || oldMachineState == MachineState_Teleported
14373 || oldMachineState == MachineState_Aborted
14374 )
14375 && ( aMachineState == MachineState_TeleportingIn
14376 || aMachineState == MachineState_Starting
14377 )
14378 )
14379 )
14380 {
14381 /* The EMT thread is about to start */
14382
14383 /* Nothing to do here for now... */
14384
14385 /// @todo NEWMEDIA don't let mDVDDrive and other children
14386 /// change anything when in the Starting/Restoring state
14387 }
14388 else if ( ( oldMachineState == MachineState_Running
14389 || oldMachineState == MachineState_Paused
14390 || oldMachineState == MachineState_Teleporting
14391 || oldMachineState == MachineState_LiveSnapshotting
14392 || oldMachineState == MachineState_Stuck
14393 || oldMachineState == MachineState_Starting
14394 || oldMachineState == MachineState_Stopping
14395 || oldMachineState == MachineState_Saving
14396 || oldMachineState == MachineState_Restoring
14397 || oldMachineState == MachineState_TeleportingPausedVM
14398 || oldMachineState == MachineState_TeleportingIn
14399 )
14400 && ( aMachineState == MachineState_PoweredOff
14401 || aMachineState == MachineState_Saved
14402 || aMachineState == MachineState_Teleported
14403 || aMachineState == MachineState_Aborted
14404 )
14405 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14406 * snapshot */
14407 && ( mConsoleTaskData.mSnapshot.isNull()
14408 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14409 )
14410 )
14411 {
14412 /* The EMT thread has just stopped, unlock attached media. Note that as
14413 * opposed to locking that is done from Console, we do unlocking here
14414 * because the VM process may have aborted before having a chance to
14415 * properly unlock all media it locked. */
14416
14417 unlockMedia();
14418 }
14419
14420 if (oldMachineState == MachineState_Restoring)
14421 {
14422 if (aMachineState != MachineState_Saved)
14423 {
14424 /*
14425 * delete the saved state file once the machine has finished
14426 * restoring from it (note that Console sets the state from
14427 * Restoring to Saved if the VM couldn't restore successfully,
14428 * to give the user an ability to fix an error and retry --
14429 * we keep the saved state file in this case)
14430 */
14431 deleteSavedState = true;
14432 }
14433 }
14434 else if ( oldMachineState == MachineState_Saved
14435 && ( aMachineState == MachineState_PoweredOff
14436 || aMachineState == MachineState_Aborted
14437 || aMachineState == MachineState_Teleported
14438 )
14439 )
14440 {
14441 /*
14442 * delete the saved state after Console::ForgetSavedState() is called
14443 * or if the VM process (owning a direct VM session) crashed while the
14444 * VM was Saved
14445 */
14446
14447 /// @todo (dmik)
14448 // Not sure that deleting the saved state file just because of the
14449 // client death before it attempted to restore the VM is a good
14450 // thing. But when it crashes we need to go to the Aborted state
14451 // which cannot have the saved state file associated... The only
14452 // way to fix this is to make the Aborted condition not a VM state
14453 // but a bool flag: i.e., when a crash occurs, set it to true and
14454 // change the state to PoweredOff or Saved depending on the
14455 // saved state presence.
14456
14457 deleteSavedState = true;
14458 mData->mCurrentStateModified = TRUE;
14459 stsFlags |= SaveSTS_CurStateModified;
14460 }
14461
14462 if ( aMachineState == MachineState_Starting
14463 || aMachineState == MachineState_Restoring
14464 || aMachineState == MachineState_TeleportingIn
14465 )
14466 {
14467 /* set the current state modified flag to indicate that the current
14468 * state is no more identical to the state in the
14469 * current snapshot */
14470 if (!mData->mCurrentSnapshot.isNull())
14471 {
14472 mData->mCurrentStateModified = TRUE;
14473 stsFlags |= SaveSTS_CurStateModified;
14474 }
14475 }
14476
14477 if (deleteSavedState)
14478 {
14479 if (mRemoveSavedState)
14480 {
14481 Assert(!mSSData->strStateFilePath.isEmpty());
14482
14483 // it is safe to delete the saved state file if ...
14484 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14485 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14486 // ... none of the snapshots share the saved state file
14487 )
14488 RTFileDelete(mSSData->strStateFilePath.c_str());
14489 }
14490
14491 mSSData->strStateFilePath.setNull();
14492 stsFlags |= SaveSTS_StateFilePath;
14493 }
14494
14495 /* redirect to the underlying peer machine */
14496 mPeer->setMachineState(aMachineState);
14497
14498 if ( aMachineState == MachineState_PoweredOff
14499 || aMachineState == MachineState_Teleported
14500 || aMachineState == MachineState_Aborted
14501 || aMachineState == MachineState_Saved)
14502 {
14503 /* the machine has stopped execution
14504 * (or the saved state file was adopted) */
14505 stsFlags |= SaveSTS_StateTimeStamp;
14506 }
14507
14508 if ( ( oldMachineState == MachineState_PoweredOff
14509 || oldMachineState == MachineState_Aborted
14510 || oldMachineState == MachineState_Teleported
14511 )
14512 && aMachineState == MachineState_Saved)
14513 {
14514 /* the saved state file was adopted */
14515 Assert(!mSSData->strStateFilePath.isEmpty());
14516 stsFlags |= SaveSTS_StateFilePath;
14517 }
14518
14519#ifdef VBOX_WITH_GUEST_PROPS
14520 if ( aMachineState == MachineState_PoweredOff
14521 || aMachineState == MachineState_Aborted
14522 || aMachineState == MachineState_Teleported)
14523 {
14524 /* Make sure any transient guest properties get removed from the
14525 * property store on shutdown. */
14526
14527 HWData::GuestPropertyMap::const_iterator it;
14528 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14529 if (!fNeedsSaving)
14530 for (it = mHWData->mGuestProperties.begin();
14531 it != mHWData->mGuestProperties.end(); ++it)
14532 if ( (it->second.mFlags & guestProp::TRANSIENT)
14533 || (it->second.mFlags & guestProp::TRANSRESET))
14534 {
14535 fNeedsSaving = true;
14536 break;
14537 }
14538 if (fNeedsSaving)
14539 {
14540 mData->mCurrentStateModified = TRUE;
14541 stsFlags |= SaveSTS_CurStateModified;
14542 }
14543 }
14544#endif
14545
14546 rc = saveStateSettings(stsFlags);
14547
14548 if ( ( oldMachineState != MachineState_PoweredOff
14549 && oldMachineState != MachineState_Aborted
14550 && oldMachineState != MachineState_Teleported
14551 )
14552 && ( aMachineState == MachineState_PoweredOff
14553 || aMachineState == MachineState_Aborted
14554 || aMachineState == MachineState_Teleported
14555 )
14556 )
14557 {
14558 /* we've been shut down for any reason */
14559 /* no special action so far */
14560 }
14561
14562 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14563 LogFlowThisFuncLeave();
14564 return rc;
14565}
14566
14567/**
14568 * Sends the current machine state value to the VM process.
14569 *
14570 * @note Locks this object for reading, then calls a client process.
14571 */
14572HRESULT SessionMachine::updateMachineStateOnClient()
14573{
14574 AutoCaller autoCaller(this);
14575 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14576
14577 ComPtr<IInternalSessionControl> directControl;
14578 {
14579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14580 AssertReturn(!!mData, E_FAIL);
14581 directControl = mData->mSession.mDirectControl;
14582
14583 /* directControl may be already set to NULL here in #OnSessionEnd()
14584 * called too early by the direct session process while there is still
14585 * some operation (like deleting the snapshot) in progress. The client
14586 * process in this case is waiting inside Session::close() for the
14587 * "end session" process object to complete, while #uninit() called by
14588 * #checkForDeath() on the Watcher thread is waiting for the pending
14589 * operation to complete. For now, we accept this inconsistent behavior
14590 * and simply do nothing here. */
14591
14592 if (mData->mSession.mState == SessionState_Unlocking)
14593 return S_OK;
14594
14595 AssertReturn(!directControl.isNull(), E_FAIL);
14596 }
14597
14598 return directControl->UpdateMachineState(mData->mMachineState);
14599}
Note: See TracBrowser for help on using the repository browser.

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