VirtualBox

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

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

Override VM Icon Implementation xtracker id:5292

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