VirtualBox

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

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

The ICON data isn't required, it's optional so dont fail in copyMachineDataToSettings because of this. For the same reason, don't save the <Icon> element unconditionally, only when there is actual data. Because, we have to bump the settings version when using <Icon>.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 488.4 KB
Line 
1/* $Id: MachineImpl.cpp 46351 2013-05-31 20:25:22Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76#include <iprt/base64.h>
77
78#include <VBox/com/array.h>
79#include <VBox/com/list.h>
80
81#include <VBox/err.h>
82#include <VBox/param.h>
83#include <VBox/settings.h>
84#include <VBox/vmm/ssm.h>
85
86#ifdef VBOX_WITH_GUEST_PROPS
87# include <VBox/HostServices/GuestPropertySvc.h>
88# include <VBox/com/array.h>
89#endif
90
91#include "VBox/com/MultiResult.h"
92
93#include <algorithm>
94
95#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
96# define HOSTSUFF_EXE ".exe"
97#else /* !RT_OS_WINDOWS */
98# define HOSTSUFF_EXE ""
99#endif /* !RT_OS_WINDOWS */
100
101// defines / prototypes
102/////////////////////////////////////////////////////////////////////////////
103
104/////////////////////////////////////////////////////////////////////////////
105// Machine::Data structure
106/////////////////////////////////////////////////////////////////////////////
107
108Machine::Data::Data()
109{
110 mRegistered = FALSE;
111 pMachineConfigFile = NULL;
112 /* Contains hints on what has changed when the user is using the VM (config
113 * changes, running the VM, ...). This is used to decide if a config needs
114 * to be written to disk. */
115 flModifications = 0;
116 /* VM modification usually also trigger setting the current state to
117 * "Modified". Although this is not always the case. An e.g. is the VM
118 * initialization phase or when snapshot related data is changed. The
119 * actually behavior is controlled by the following flag. */
120 m_fAllowStateModification = false;
121 mAccessible = FALSE;
122 /* mUuid is initialized in Machine::init() */
123
124 mMachineState = MachineState_PoweredOff;
125 RTTimeNow(&mLastStateChange);
126
127 mMachineStateDeps = 0;
128 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
129 mMachineStateChangePending = 0;
130
131 mCurrentStateModified = TRUE;
132 mGuestPropertiesModified = FALSE;
133
134 mSession.mPID = NIL_RTPROCESS;
135 mSession.mState = SessionState_Unlocked;
136}
137
138Machine::Data::~Data()
139{
140 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
141 {
142 RTSemEventMultiDestroy(mMachineStateDepsSem);
143 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
144 }
145 if (pMachineConfigFile)
146 {
147 delete pMachineConfigFile;
148 pMachineConfigFile = NULL;
149 }
150}
151
152/////////////////////////////////////////////////////////////////////////////
153// Machine::HWData structure
154/////////////////////////////////////////////////////////////////////////////
155
156Machine::HWData::HWData()
157{
158 /* default values for a newly created machine */
159 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
160 mMemorySize = 128;
161 mCPUCount = 1;
162 mCPUHotPlugEnabled = false;
163 mMemoryBalloonSize = 0;
164 mPageFusionEnabled = false;
165 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
166 mVRAMSize = 8;
167 mAccelerate3DEnabled = false;
168 mAccelerate2DVideoEnabled = false;
169 mMonitorCount = 1;
170 mVideoCaptureFile = "Test.webm";
171 mVideoCaptureWidth = 1024;
172 mVideoCaptureHeight = 768;
173 mVideoCaptureRate = 512;
174 mVideoCaptureFps = 25;
175 mVideoCaptureEnabled = false;
176 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
177 maVideoCaptureScreens[i] = true;
178
179 mHWVirtExEnabled = true;
180 mHWVirtExNestedPagingEnabled = true;
181#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
182 mHWVirtExLargePagesEnabled = true;
183#else
184 /* Not supported on 32 bits hosts. */
185 mHWVirtExLargePagesEnabled = false;
186#endif
187 mHWVirtExVPIDEnabled = true;
188 mHWVirtExUXEnabled = true;
189 mHWVirtExForceEnabled = false;
190#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
191 mHWVirtExExclusive = false;
192#else
193 mHWVirtExExclusive = true;
194#endif
195#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
196 mPAEEnabled = true;
197#else
198 mPAEEnabled = false;
199#endif
200 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
201 mSyntheticCpu = false;
202 mHPETEnabled = false;
203
204 /* default boot order: floppy - DVD - HDD */
205 mBootOrder[0] = DeviceType_Floppy;
206 mBootOrder[1] = DeviceType_DVD;
207 mBootOrder[2] = DeviceType_HardDisk;
208 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
209 mBootOrder[i] = DeviceType_Null;
210
211 mClipboardMode = ClipboardMode_Disabled;
212 mDragAndDropMode = DragAndDropMode_Disabled;
213 mGuestPropertyNotificationPatterns = "";
214
215 mFirmwareType = FirmwareType_BIOS;
216 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
217 mPointingHIDType = PointingHIDType_PS2Mouse;
218 mChipsetType = ChipsetType_PIIX3;
219 mEmulatedUSBWebcamEnabled = FALSE;
220 mEmulatedUSBCardReaderEnabled = FALSE;
221
222 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
223 mCPUAttached[i] = false;
224
225 mIOCacheEnabled = true;
226 mIOCacheSize = 5; /* 5MB */
227
228 /* Maximum CPU execution cap by default. */
229 mCpuExecutionCap = 100;
230}
231
232Machine::HWData::~HWData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine::HDData structure
238/////////////////////////////////////////////////////////////////////////////
239
240Machine::MediaData::MediaData()
241{
242}
243
244Machine::MediaData::~MediaData()
245{
246}
247
248/////////////////////////////////////////////////////////////////////////////
249// Machine class
250/////////////////////////////////////////////////////////////////////////////
251
252// constructor / destructor
253/////////////////////////////////////////////////////////////////////////////
254
255Machine::Machine()
256 : mCollectorGuest(NULL),
257 mPeer(NULL),
258 mParent(NULL),
259 mSerialPorts(),
260 mParallelPorts(),
261 uRegistryNeedsSaving(0)
262{}
263
264Machine::~Machine()
265{}
266
267HRESULT Machine::FinalConstruct()
268{
269 LogFlowThisFunc(("\n"));
270 return BaseFinalConstruct();
271}
272
273void Machine::FinalRelease()
274{
275 LogFlowThisFunc(("\n"));
276 uninit();
277 BaseFinalRelease();
278}
279
280/**
281 * Initializes a new machine instance; this init() variant creates a new, empty machine.
282 * This gets called from VirtualBox::CreateMachine().
283 *
284 * @param aParent Associated parent object
285 * @param strConfigFile Local file system path to the VM settings file (can
286 * be relative to the VirtualBox config directory).
287 * @param strName name for the machine
288 * @param llGroups list of groups for the machine
289 * @param aOsType OS Type of this machine or NULL.
290 * @param aId UUID for the new machine.
291 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
292 *
293 * @return Success indicator. if not S_OK, the machine object is invalid
294 */
295HRESULT Machine::init(VirtualBox *aParent,
296 const Utf8Str &strConfigFile,
297 const Utf8Str &strName,
298 const StringsList &llGroups,
299 GuestOSType *aOsType,
300 const Guid &aId,
301 bool fForceOverwrite,
302 bool fDirectoryIncludesUUID)
303{
304 LogFlowThisFuncEnter();
305 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
306
307 /* Enclose the state transition NotReady->InInit->Ready */
308 AutoInitSpan autoInitSpan(this);
309 AssertReturn(autoInitSpan.isOk(), E_FAIL);
310
311 HRESULT rc = initImpl(aParent, strConfigFile);
312 if (FAILED(rc)) return rc;
313
314 rc = tryCreateMachineConfigFile(fForceOverwrite);
315 if (FAILED(rc)) return rc;
316
317 if (SUCCEEDED(rc))
318 {
319 // create an empty machine config
320 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
321
322 rc = initDataAndChildObjects();
323 }
324
325 if (SUCCEEDED(rc))
326 {
327 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
328 mData->mAccessible = TRUE;
329
330 unconst(mData->mUuid) = aId;
331
332 mUserData->s.strName = strName;
333
334 mUserData->s.llGroups = llGroups;
335
336 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
337 // the "name sync" flag determines whether the machine directory gets renamed along
338 // with the machine file; say so if the settings file name is the same as the
339 // settings file parent directory (machine directory)
340 mUserData->s.fNameSync = isInOwnDir();
341
342 // initialize the default snapshots folder
343 rc = COMSETTER(SnapshotFolder)(NULL);
344 AssertComRC(rc);
345
346 if (aOsType)
347 {
348 /* Store OS type */
349 mUserData->s.strOsType = aOsType->id();
350
351 /* Apply BIOS defaults */
352 mBIOSSettings->applyDefaults(aOsType);
353
354 /* Apply network adapters defaults */
355 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
356 mNetworkAdapters[slot]->applyDefaults(aOsType);
357
358 /* Apply serial port defaults */
359 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
360 mSerialPorts[slot]->applyDefaults(aOsType);
361
362 /* Let the OS type select 64-bit ness. */
363 mHWData->mLongMode = aOsType->is64Bit()
364 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
365 }
366
367 /* At this point the changing of the current state modification
368 * flag is allowed. */
369 allowStateModification();
370
371 /* commit all changes made during the initialization */
372 commit();
373 }
374
375 /* Confirm a successful initialization when it's the case */
376 if (SUCCEEDED(rc))
377 {
378 if (mData->mAccessible)
379 autoInitSpan.setSucceeded();
380 else
381 autoInitSpan.setLimited();
382 }
383
384 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
385 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
386 mData->mRegistered,
387 mData->mAccessible,
388 rc));
389
390 LogFlowThisFuncLeave();
391
392 return rc;
393}
394
395/**
396 * Initializes a new instance with data from machine XML (formerly Init_Registered).
397 * Gets called in two modes:
398 *
399 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
400 * UUID is specified and we mark the machine as "registered";
401 *
402 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
403 * and the machine remains unregistered until RegisterMachine() is called.
404 *
405 * @param aParent Associated parent object
406 * @param aConfigFile Local file system path to the VM settings file (can
407 * be relative to the VirtualBox config directory).
408 * @param aId UUID of the machine or NULL (see above).
409 *
410 * @return Success indicator. if not S_OK, the machine object is invalid
411 */
412HRESULT Machine::initFromSettings(VirtualBox *aParent,
413 const Utf8Str &strConfigFile,
414 const Guid *aId)
415{
416 LogFlowThisFuncEnter();
417 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
418
419 /* Enclose the state transition NotReady->InInit->Ready */
420 AutoInitSpan autoInitSpan(this);
421 AssertReturn(autoInitSpan.isOk(), E_FAIL);
422
423 HRESULT rc = initImpl(aParent, strConfigFile);
424 if (FAILED(rc)) return rc;
425
426 if (aId)
427 {
428 // loading a registered VM:
429 unconst(mData->mUuid) = *aId;
430 mData->mRegistered = TRUE;
431 // now load the settings from XML:
432 rc = registeredInit();
433 // this calls initDataAndChildObjects() and loadSettings()
434 }
435 else
436 {
437 // opening an unregistered VM (VirtualBox::OpenMachine()):
438 rc = initDataAndChildObjects();
439
440 if (SUCCEEDED(rc))
441 {
442 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
443 mData->mAccessible = TRUE;
444
445 try
446 {
447 // load and parse machine XML; this will throw on XML or logic errors
448 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
449
450 // reject VM UUID duplicates, they can happen if someone
451 // tries to register an already known VM config again
452 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
453 true /* fPermitInaccessible */,
454 false /* aDoSetError */,
455 NULL) != VBOX_E_OBJECT_NOT_FOUND)
456 {
457 throw setError(E_FAIL,
458 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
459 mData->m_strConfigFile.c_str());
460 }
461
462 // use UUID from machine config
463 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
464
465 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
466 NULL /* puuidRegistry */);
467 if (FAILED(rc)) throw rc;
468
469 /* At this point the changing of the current state modification
470 * flag is allowed. */
471 allowStateModification();
472
473 commit();
474 }
475 catch (HRESULT err)
476 {
477 /* we assume that error info is set by the thrower */
478 rc = err;
479 }
480 catch (...)
481 {
482 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
483 }
484 }
485 }
486
487 /* Confirm a successful initialization when it's the case */
488 if (SUCCEEDED(rc))
489 {
490 if (mData->mAccessible)
491 autoInitSpan.setSucceeded();
492 else
493 {
494 autoInitSpan.setLimited();
495
496 // uninit media from this machine's media registry, or else
497 // reloading the settings will fail
498 mParent->unregisterMachineMedia(getId());
499 }
500 }
501
502 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
503 "rc=%08X\n",
504 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
505 mData->mRegistered, mData->mAccessible, rc));
506
507 LogFlowThisFuncLeave();
508
509 return rc;
510}
511
512/**
513 * Initializes a new instance from a machine config that is already in memory
514 * (import OVF case). Since we are importing, the UUID in the machine
515 * config is ignored and we always generate a fresh one.
516 *
517 * @param strName Name for the new machine; this overrides what is specified in config and is used
518 * for the settings file as well.
519 * @param config Machine configuration loaded and parsed from XML.
520 *
521 * @return Success indicator. if not S_OK, the machine object is invalid
522 */
523HRESULT Machine::init(VirtualBox *aParent,
524 const Utf8Str &strName,
525 const settings::MachineConfigFile &config)
526{
527 LogFlowThisFuncEnter();
528
529 /* Enclose the state transition NotReady->InInit->Ready */
530 AutoInitSpan autoInitSpan(this);
531 AssertReturn(autoInitSpan.isOk(), E_FAIL);
532
533 Utf8Str strConfigFile;
534 aParent->getDefaultMachineFolder(strConfigFile);
535 strConfigFile.append(RTPATH_DELIMITER);
536 strConfigFile.append(strName);
537 strConfigFile.append(RTPATH_DELIMITER);
538 strConfigFile.append(strName);
539 strConfigFile.append(".vbox");
540
541 HRESULT rc = initImpl(aParent, strConfigFile);
542 if (FAILED(rc)) return rc;
543
544 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
545 if (FAILED(rc)) return rc;
546
547 rc = initDataAndChildObjects();
548
549 if (SUCCEEDED(rc))
550 {
551 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
552 mData->mAccessible = TRUE;
553
554 // create empty machine config for instance data
555 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
556
557 // generate fresh UUID, ignore machine config
558 unconst(mData->mUuid).create();
559
560 rc = loadMachineDataFromSettings(config,
561 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
562
563 // override VM name as well, it may be different
564 mUserData->s.strName = strName;
565
566 if (SUCCEEDED(rc))
567 {
568 /* At this point the changing of the current state modification
569 * flag is allowed. */
570 allowStateModification();
571
572 /* commit all changes made during the initialization */
573 commit();
574 }
575 }
576
577 /* Confirm a successful initialization when it's the case */
578 if (SUCCEEDED(rc))
579 {
580 if (mData->mAccessible)
581 autoInitSpan.setSucceeded();
582 else
583 {
584 autoInitSpan.setLimited();
585
586 // uninit media from this machine's media registry, or else
587 // reloading the settings will fail
588 mParent->unregisterMachineMedia(getId());
589 }
590 }
591
592 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
593 "rc=%08X\n",
594 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
595 mData->mRegistered, mData->mAccessible, rc));
596
597 LogFlowThisFuncLeave();
598
599 return rc;
600}
601
602/**
603 * Shared code between the various init() implementations.
604 * @param aParent
605 * @return
606 */
607HRESULT Machine::initImpl(VirtualBox *aParent,
608 const Utf8Str &strConfigFile)
609{
610 LogFlowThisFuncEnter();
611
612 AssertReturn(aParent, E_INVALIDARG);
613 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
614
615 HRESULT rc = S_OK;
616
617 /* share the parent weakly */
618 unconst(mParent) = aParent;
619
620 /* allocate the essential machine data structure (the rest will be
621 * allocated later by initDataAndChildObjects() */
622 mData.allocate();
623
624 /* memorize the config file name (as provided) */
625 mData->m_strConfigFile = strConfigFile;
626
627 /* get the full file name */
628 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
629 if (RT_FAILURE(vrc1))
630 return setError(VBOX_E_FILE_ERROR,
631 tr("Invalid machine settings file name '%s' (%Rrc)"),
632 strConfigFile.c_str(),
633 vrc1);
634
635 LogFlowThisFuncLeave();
636
637 return rc;
638}
639
640/**
641 * Tries to create a machine settings file in the path stored in the machine
642 * instance data. Used when a new machine is created to fail gracefully if
643 * the settings file could not be written (e.g. because machine dir is read-only).
644 * @return
645 */
646HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
647{
648 HRESULT rc = S_OK;
649
650 // when we create a new machine, we must be able to create the settings file
651 RTFILE f = NIL_RTFILE;
652 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
653 if ( RT_SUCCESS(vrc)
654 || vrc == VERR_SHARING_VIOLATION
655 )
656 {
657 if (RT_SUCCESS(vrc))
658 RTFileClose(f);
659 if (!fForceOverwrite)
660 rc = setError(VBOX_E_FILE_ERROR,
661 tr("Machine settings file '%s' already exists"),
662 mData->m_strConfigFileFull.c_str());
663 else
664 {
665 /* try to delete the config file, as otherwise the creation
666 * of a new settings file will fail. */
667 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
668 if (RT_FAILURE(vrc2))
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Could not delete the existing settings file '%s' (%Rrc)"),
671 mData->m_strConfigFileFull.c_str(), vrc2);
672 }
673 }
674 else if ( vrc != VERR_FILE_NOT_FOUND
675 && vrc != VERR_PATH_NOT_FOUND
676 )
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Invalid machine settings file name '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(),
680 vrc);
681 return rc;
682}
683
684/**
685 * Initializes the registered machine by loading the settings file.
686 * This method is separated from #init() in order to make it possible to
687 * retry the operation after VirtualBox startup instead of refusing to
688 * startup the whole VirtualBox server in case if the settings file of some
689 * registered VM is invalid or inaccessible.
690 *
691 * @note Must be always called from this object's write lock
692 * (unless called from #init() that doesn't need any locking).
693 * @note Locks the mUSBController method for writing.
694 * @note Subclasses must not call this method.
695 */
696HRESULT Machine::registeredInit()
697{
698 AssertReturn(!isSessionMachine(), E_FAIL);
699 AssertReturn(!isSnapshotMachine(), E_FAIL);
700 AssertReturn(mData->mUuid.isValid(), E_FAIL);
701 AssertReturn(!mData->mAccessible, E_FAIL);
702
703 HRESULT rc = initDataAndChildObjects();
704
705 if (SUCCEEDED(rc))
706 {
707 /* Temporarily reset the registered flag in order to let setters
708 * potentially called from loadSettings() succeed (isMutable() used in
709 * all setters will return FALSE for a Machine instance if mRegistered
710 * is TRUE). */
711 mData->mRegistered = FALSE;
712
713 try
714 {
715 // load and parse machine XML; this will throw on XML or logic errors
716 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
717
718 if (mData->mUuid != mData->pMachineConfigFile->uuid)
719 throw setError(E_FAIL,
720 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
721 mData->pMachineConfigFile->uuid.raw(),
722 mData->m_strConfigFileFull.c_str(),
723 mData->mUuid.toString().c_str(),
724 mParent->settingsFilePath().c_str());
725
726 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
727 NULL /* const Guid *puuidRegistry */);
728 if (FAILED(rc)) throw rc;
729 }
730 catch (HRESULT err)
731 {
732 /* we assume that error info is set by the thrower */
733 rc = err;
734 }
735 catch (...)
736 {
737 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
738 }
739
740 /* Restore the registered flag (even on failure) */
741 mData->mRegistered = TRUE;
742 }
743
744 if (SUCCEEDED(rc))
745 {
746 /* Set mAccessible to TRUE only if we successfully locked and loaded
747 * the settings file */
748 mData->mAccessible = TRUE;
749
750 /* commit all changes made during loading the settings file */
751 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
752 /// @todo r=klaus for some reason the settings loading logic backs up
753 // the settings, and therefore a commit is needed. Should probably be changed.
754 }
755 else
756 {
757 /* If the machine is registered, then, instead of returning a
758 * failure, we mark it as inaccessible and set the result to
759 * success to give it a try later */
760
761 /* fetch the current error info */
762 mData->mAccessError = com::ErrorInfo();
763 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
764 mData->mUuid.raw(),
765 mData->mAccessError.getText().raw()));
766
767 /* rollback all changes */
768 rollback(false /* aNotify */);
769
770 // uninit media from this machine's media registry, or else
771 // reloading the settings will fail
772 mParent->unregisterMachineMedia(getId());
773
774 /* uninitialize the common part to make sure all data is reset to
775 * default (null) values */
776 uninitDataAndChildObjects();
777
778 rc = S_OK;
779 }
780
781 return rc;
782}
783
784/**
785 * Uninitializes the instance.
786 * Called either from FinalRelease() or by the parent when it gets destroyed.
787 *
788 * @note The caller of this method must make sure that this object
789 * a) doesn't have active callers on the current thread and b) is not locked
790 * by the current thread; otherwise uninit() will hang either a) due to
791 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
792 * a dead-lock caused by this thread waiting for all callers on the other
793 * threads are done but preventing them from doing so by holding a lock.
794 */
795void Machine::uninit()
796{
797 LogFlowThisFuncEnter();
798
799 Assert(!isWriteLockOnCurrentThread());
800
801 Assert(!uRegistryNeedsSaving);
802 if (uRegistryNeedsSaving)
803 {
804 AutoCaller autoCaller(this);
805 if (SUCCEEDED(autoCaller.rc()))
806 {
807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
808 saveSettings(NULL, Machine::SaveS_Force);
809 }
810 }
811
812 /* Enclose the state transition Ready->InUninit->NotReady */
813 AutoUninitSpan autoUninitSpan(this);
814 if (autoUninitSpan.uninitDone())
815 return;
816
817 Assert(!isSnapshotMachine());
818 Assert(!isSessionMachine());
819 Assert(!!mData);
820
821 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
822 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
823
824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
825
826 if (!mData->mSession.mMachine.isNull())
827 {
828 /* Theoretically, this can only happen if the VirtualBox server has been
829 * terminated while there were clients running that owned open direct
830 * sessions. Since in this case we are definitely called by
831 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
832 * won't happen on the client watcher thread (because it does
833 * VirtualBox::addCaller() for the duration of the
834 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
835 * cannot happen until the VirtualBox caller is released). This is
836 * important, because SessionMachine::uninit() cannot correctly operate
837 * after we return from this method (it expects the Machine instance is
838 * still valid). We'll call it ourselves below.
839 */
840 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
841 (SessionMachine*)mData->mSession.mMachine));
842
843 if (Global::IsOnlineOrTransient(mData->mMachineState))
844 {
845 LogWarningThisFunc(("Setting state to Aborted!\n"));
846 /* set machine state using SessionMachine reimplementation */
847 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
848 }
849
850 /*
851 * Uninitialize SessionMachine using public uninit() to indicate
852 * an unexpected uninitialization.
853 */
854 mData->mSession.mMachine->uninit();
855 /* SessionMachine::uninit() must set mSession.mMachine to null */
856 Assert(mData->mSession.mMachine.isNull());
857 }
858
859 // uninit media from this machine's media registry, if they're still there
860 Guid uuidMachine(getId());
861
862 /* the lock is no more necessary (SessionMachine is uninitialized) */
863 alock.release();
864
865 /* XXX This will fail with
866 * "cannot be closed because it is still attached to 1 virtual machines"
867 * because at this point we did not call uninitDataAndChildObjects() yet
868 * and therefore also removeBackReference() for all these mediums was not called! */
869
870 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
871 mParent->unregisterMachineMedia(uuidMachine);
872
873 // has machine been modified?
874 if (mData->flModifications)
875 {
876 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
877 rollback(false /* aNotify */);
878 }
879
880 if (mData->mAccessible)
881 uninitDataAndChildObjects();
882
883 /* free the essential data structure last */
884 mData.free();
885
886 LogFlowThisFuncLeave();
887}
888
889// IMachine properties
890/////////////////////////////////////////////////////////////////////////////
891
892STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
893{
894 CheckComArgOutPointerValid(aParent);
895
896 AutoLimitedCaller autoCaller(this);
897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
898
899 /* mParent is constant during life time, no need to lock */
900 ComObjPtr<VirtualBox> pVirtualBox(mParent);
901 pVirtualBox.queryInterfaceTo(aParent);
902
903 return S_OK;
904}
905
906STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
907{
908 CheckComArgOutPointerValid(aAccessible);
909
910 AutoLimitedCaller autoCaller(this);
911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
912
913 LogFlowThisFunc(("ENTER\n"));
914
915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
916
917 HRESULT rc = S_OK;
918
919 if (!mData->mAccessible)
920 {
921 /* try to initialize the VM once more if not accessible */
922
923 AutoReinitSpan autoReinitSpan(this);
924 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
925
926#ifdef DEBUG
927 LogFlowThisFunc(("Dumping media backreferences\n"));
928 mParent->dumpAllBackRefs();
929#endif
930
931 if (mData->pMachineConfigFile)
932 {
933 // reset the XML file to force loadSettings() (called from registeredInit())
934 // to parse it again; the file might have changed
935 delete mData->pMachineConfigFile;
936 mData->pMachineConfigFile = NULL;
937 }
938
939 rc = registeredInit();
940
941 if (SUCCEEDED(rc) && mData->mAccessible)
942 {
943 autoReinitSpan.setSucceeded();
944
945 /* make sure interesting parties will notice the accessibility
946 * state change */
947 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
948 mParent->onMachineDataChange(mData->mUuid);
949 }
950 }
951
952 if (SUCCEEDED(rc))
953 *aAccessible = mData->mAccessible;
954
955 LogFlowThisFuncLeave();
956
957 return rc;
958}
959
960STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
961{
962 CheckComArgOutPointerValid(aAccessError);
963
964 AutoLimitedCaller autoCaller(this);
965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
966
967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
968
969 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
970 {
971 /* return shortly */
972 aAccessError = NULL;
973 return S_OK;
974 }
975
976 HRESULT rc = S_OK;
977
978 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
979 rc = errorInfo.createObject();
980 if (SUCCEEDED(rc))
981 {
982 errorInfo->init(mData->mAccessError.getResultCode(),
983 mData->mAccessError.getInterfaceID().ref(),
984 Utf8Str(mData->mAccessError.getComponent()).c_str(),
985 Utf8Str(mData->mAccessError.getText()));
986 rc = errorInfo.queryInterfaceTo(aAccessError);
987 }
988
989 return rc;
990}
991
992STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
993{
994 CheckComArgOutPointerValid(aName);
995
996 AutoCaller autoCaller(this);
997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
998
999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 mUserData->s.strName.cloneTo(aName);
1002
1003 return S_OK;
1004}
1005
1006STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1007{
1008 CheckComArgStrNotEmptyOrNull(aName);
1009
1010 AutoCaller autoCaller(this);
1011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1012
1013 // prohibit setting a UUID only as the machine name, or else it can
1014 // never be found by findMachine()
1015 Guid test(aName);
1016
1017 if (test.isValid())
1018 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1019
1020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 HRESULT rc = checkStateDependency(MutableStateDep);
1023 if (FAILED(rc)) return rc;
1024
1025 setModified(IsModified_MachineData);
1026 mUserData.backup();
1027 mUserData->s.strName = aName;
1028
1029 return S_OK;
1030}
1031
1032STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1033{
1034 CheckComArgOutPointerValid(aDescription);
1035
1036 AutoCaller autoCaller(this);
1037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1038
1039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 mUserData->s.strDescription.cloneTo(aDescription);
1042
1043 return S_OK;
1044}
1045
1046STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1047{
1048 AutoCaller autoCaller(this);
1049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1050
1051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1052
1053 // this can be done in principle in any state as it doesn't affect the VM
1054 // significantly, but play safe by not messing around while complex
1055 // activities are going on
1056 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1057 if (FAILED(rc)) return rc;
1058
1059 setModified(IsModified_MachineData);
1060 mUserData.backup();
1061 mUserData->s.strDescription = aDescription;
1062
1063 return S_OK;
1064}
1065
1066STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1067{
1068 CheckComArgOutPointerValid(aId);
1069
1070 AutoLimitedCaller autoCaller(this);
1071 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1072
1073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1074
1075 mData->mUuid.toUtf16().cloneTo(aId);
1076
1077 return S_OK;
1078}
1079
1080STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1081{
1082 CheckComArgOutSafeArrayPointerValid(aGroups);
1083
1084 AutoCaller autoCaller(this);
1085 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1086
1087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1088 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1089 size_t i = 0;
1090 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1091 it != mUserData->s.llGroups.end();
1092 ++it, i++)
1093 {
1094 Bstr tmp = *it;
1095 tmp.cloneTo(&groups[i]);
1096 }
1097 groups.detachTo(ComSafeArrayOutArg(aGroups));
1098
1099 return S_OK;
1100}
1101
1102STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1103{
1104 AutoCaller autoCaller(this);
1105 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1106
1107 StringsList llGroups;
1108 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1109 if (FAILED(rc))
1110 return rc;
1111
1112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1113
1114 // changing machine groups is possible while the VM is offline
1115 rc = checkStateDependency(OfflineStateDep);
1116 if (FAILED(rc)) return rc;
1117
1118 setModified(IsModified_MachineData);
1119 mUserData.backup();
1120 mUserData->s.llGroups = llGroups;
1121
1122 return S_OK;
1123}
1124
1125STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1126{
1127 CheckComArgOutPointerValid(aOSTypeId);
1128
1129 AutoCaller autoCaller(this);
1130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1131
1132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1133
1134 mUserData->s.strOsType.cloneTo(aOSTypeId);
1135
1136 return S_OK;
1137}
1138
1139STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1140{
1141 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1142
1143 AutoCaller autoCaller(this);
1144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1145
1146 /* look up the object by Id to check it is valid */
1147 ComPtr<IGuestOSType> guestOSType;
1148 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1149 if (FAILED(rc)) return rc;
1150
1151 /* when setting, always use the "etalon" value for consistency -- lookup
1152 * by ID is case-insensitive and the input value may have different case */
1153 Bstr osTypeId;
1154 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1155 if (FAILED(rc)) return rc;
1156
1157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1158
1159 rc = checkStateDependency(MutableStateDep);
1160 if (FAILED(rc)) return rc;
1161
1162 setModified(IsModified_MachineData);
1163 mUserData.backup();
1164 mUserData->s.strOsType = osTypeId;
1165
1166 return S_OK;
1167}
1168
1169
1170STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1171{
1172 CheckComArgOutPointerValid(aFirmwareType);
1173
1174 AutoCaller autoCaller(this);
1175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1176
1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 *aFirmwareType = mHWData->mFirmwareType;
1180
1181 return S_OK;
1182}
1183
1184STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1185{
1186 AutoCaller autoCaller(this);
1187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 setModified(IsModified_MachineData);
1194 mHWData.backup();
1195 mHWData->mFirmwareType = aFirmwareType;
1196
1197 return S_OK;
1198}
1199
1200STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1201{
1202 CheckComArgOutPointerValid(aKeyboardHIDType);
1203
1204 AutoCaller autoCaller(this);
1205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1206
1207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1208
1209 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1210
1211 return S_OK;
1212}
1213
1214STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1215{
1216 AutoCaller autoCaller(this);
1217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1219
1220 HRESULT rc = checkStateDependency(MutableStateDep);
1221 if (FAILED(rc)) return rc;
1222
1223 setModified(IsModified_MachineData);
1224 mHWData.backup();
1225 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1226
1227 return S_OK;
1228}
1229
1230STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1231{
1232 CheckComArgOutPointerValid(aPointingHIDType);
1233
1234 AutoCaller autoCaller(this);
1235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1236
1237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1238
1239 *aPointingHIDType = mHWData->mPointingHIDType;
1240
1241 return S_OK;
1242}
1243
1244STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1245{
1246 AutoCaller autoCaller(this);
1247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1249
1250 HRESULT rc = checkStateDependency(MutableStateDep);
1251 if (FAILED(rc)) return rc;
1252
1253 setModified(IsModified_MachineData);
1254 mHWData.backup();
1255 mHWData->mPointingHIDType = aPointingHIDType;
1256
1257 return S_OK;
1258}
1259
1260STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1261{
1262 CheckComArgOutPointerValid(aChipsetType);
1263
1264 AutoCaller autoCaller(this);
1265 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1266
1267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1268
1269 *aChipsetType = mHWData->mChipsetType;
1270
1271 return S_OK;
1272}
1273
1274STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1275{
1276 AutoCaller autoCaller(this);
1277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1279
1280 HRESULT rc = checkStateDependency(MutableStateDep);
1281 if (FAILED(rc)) return rc;
1282
1283 if (aChipsetType != mHWData->mChipsetType)
1284 {
1285 setModified(IsModified_MachineData);
1286 mHWData.backup();
1287 mHWData->mChipsetType = aChipsetType;
1288
1289 // Resize network adapter array, to be finalized on commit/rollback.
1290 // We must not throw away entries yet, otherwise settings are lost
1291 // without a way to roll back.
1292 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1293 size_t oldCount = mNetworkAdapters.size();
1294 if (newCount > oldCount)
1295 {
1296 mNetworkAdapters.resize(newCount);
1297 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1298 {
1299 unconst(mNetworkAdapters[slot]).createObject();
1300 mNetworkAdapters[slot]->init(this, slot);
1301 }
1302 }
1303 }
1304
1305 return S_OK;
1306}
1307
1308STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1309{
1310 CheckComArgOutPointerValid(aHWVersion);
1311
1312 AutoCaller autoCaller(this);
1313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1314
1315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1316
1317 mHWData->mHWVersion.cloneTo(aHWVersion);
1318
1319 return S_OK;
1320}
1321
1322STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1323{
1324 /* check known version */
1325 Utf8Str hwVersion = aHWVersion;
1326 if ( hwVersion.compare("1") != 0
1327 && hwVersion.compare("2") != 0)
1328 return setError(E_INVALIDARG,
1329 tr("Invalid hardware version: %ls\n"), aHWVersion);
1330
1331 AutoCaller autoCaller(this);
1332 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1333
1334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1335
1336 HRESULT rc = checkStateDependency(MutableStateDep);
1337 if (FAILED(rc)) return rc;
1338
1339 setModified(IsModified_MachineData);
1340 mHWData.backup();
1341 mHWData->mHWVersion = hwVersion;
1342
1343 return S_OK;
1344}
1345
1346STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1347{
1348 CheckComArgOutPointerValid(aUUID);
1349
1350 AutoCaller autoCaller(this);
1351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1352
1353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1354
1355 if (mHWData->mHardwareUUID.isValid())
1356 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1357 else
1358 mData->mUuid.toUtf16().cloneTo(aUUID);
1359
1360 return S_OK;
1361}
1362
1363STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1364{
1365 Guid hardwareUUID(aUUID);
1366 if (!hardwareUUID.isValid())
1367 return E_INVALIDARG;
1368
1369 AutoCaller autoCaller(this);
1370 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1371
1372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1373
1374 HRESULT rc = checkStateDependency(MutableStateDep);
1375 if (FAILED(rc)) return rc;
1376
1377 setModified(IsModified_MachineData);
1378 mHWData.backup();
1379 if (hardwareUUID == mData->mUuid)
1380 mHWData->mHardwareUUID.clear();
1381 else
1382 mHWData->mHardwareUUID = hardwareUUID;
1383
1384 return S_OK;
1385}
1386
1387STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1388{
1389 CheckComArgOutPointerValid(memorySize);
1390
1391 AutoCaller autoCaller(this);
1392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1393
1394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 *memorySize = mHWData->mMemorySize;
1397
1398 return S_OK;
1399}
1400
1401STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1402{
1403 /* check RAM limits */
1404 if ( memorySize < MM_RAM_MIN_IN_MB
1405 || memorySize > MM_RAM_MAX_IN_MB
1406 )
1407 return setError(E_INVALIDARG,
1408 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1409 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1410
1411 AutoCaller autoCaller(this);
1412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1413
1414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 HRESULT rc = checkStateDependency(MutableStateDep);
1417 if (FAILED(rc)) return rc;
1418
1419 setModified(IsModified_MachineData);
1420 mHWData.backup();
1421 mHWData->mMemorySize = memorySize;
1422
1423 return S_OK;
1424}
1425
1426STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1427{
1428 CheckComArgOutPointerValid(CPUCount);
1429
1430 AutoCaller autoCaller(this);
1431 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1432
1433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1434
1435 *CPUCount = mHWData->mCPUCount;
1436
1437 return S_OK;
1438}
1439
1440STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1441{
1442 /* check CPU limits */
1443 if ( CPUCount < SchemaDefs::MinCPUCount
1444 || CPUCount > SchemaDefs::MaxCPUCount
1445 )
1446 return setError(E_INVALIDARG,
1447 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1448 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1449
1450 AutoCaller autoCaller(this);
1451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1452
1453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1454
1455 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1456 if (mHWData->mCPUHotPlugEnabled)
1457 {
1458 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1459 {
1460 if (mHWData->mCPUAttached[idx])
1461 return setError(E_INVALIDARG,
1462 tr("There is still a CPU attached to socket %lu."
1463 "Detach the CPU before removing the socket"),
1464 CPUCount, idx+1);
1465 }
1466 }
1467
1468 HRESULT rc = checkStateDependency(MutableStateDep);
1469 if (FAILED(rc)) return rc;
1470
1471 setModified(IsModified_MachineData);
1472 mHWData.backup();
1473 mHWData->mCPUCount = CPUCount;
1474
1475 return S_OK;
1476}
1477
1478STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1479{
1480 CheckComArgOutPointerValid(aExecutionCap);
1481
1482 AutoCaller autoCaller(this);
1483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1484
1485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 *aExecutionCap = mHWData->mCpuExecutionCap;
1488
1489 return S_OK;
1490}
1491
1492STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1493{
1494 HRESULT rc = S_OK;
1495
1496 /* check throttle limits */
1497 if ( aExecutionCap < 1
1498 || aExecutionCap > 100
1499 )
1500 return setError(E_INVALIDARG,
1501 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1502 aExecutionCap, 1, 100);
1503
1504 AutoCaller autoCaller(this);
1505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1506
1507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1508
1509 alock.release();
1510 rc = onCPUExecutionCapChange(aExecutionCap);
1511 alock.acquire();
1512 if (FAILED(rc)) return rc;
1513
1514 setModified(IsModified_MachineData);
1515 mHWData.backup();
1516 mHWData->mCpuExecutionCap = aExecutionCap;
1517
1518 /* Save settings if online - todo why is this required?? */
1519 if (Global::IsOnline(mData->mMachineState))
1520 saveSettings(NULL);
1521
1522 return S_OK;
1523}
1524
1525
1526STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1527{
1528 CheckComArgOutPointerValid(aEnabled);
1529
1530 AutoCaller autoCaller(this);
1531 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1532
1533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1534
1535 *aEnabled = mHWData->mCPUHotPlugEnabled;
1536
1537 return S_OK;
1538}
1539
1540STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1541{
1542 HRESULT rc = S_OK;
1543
1544 AutoCaller autoCaller(this);
1545 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1546
1547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1548
1549 rc = checkStateDependency(MutableStateDep);
1550 if (FAILED(rc)) return rc;
1551
1552 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1553 {
1554 if (aEnabled)
1555 {
1556 setModified(IsModified_MachineData);
1557 mHWData.backup();
1558
1559 /* Add the amount of CPUs currently attached */
1560 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1561 {
1562 mHWData->mCPUAttached[i] = true;
1563 }
1564 }
1565 else
1566 {
1567 /*
1568 * We can disable hotplug only if the amount of maximum CPUs is equal
1569 * to the amount of attached CPUs
1570 */
1571 unsigned cCpusAttached = 0;
1572 unsigned iHighestId = 0;
1573
1574 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1575 {
1576 if (mHWData->mCPUAttached[i])
1577 {
1578 cCpusAttached++;
1579 iHighestId = i;
1580 }
1581 }
1582
1583 if ( (cCpusAttached != mHWData->mCPUCount)
1584 || (iHighestId >= mHWData->mCPUCount))
1585 return setError(E_INVALIDARG,
1586 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1587
1588 setModified(IsModified_MachineData);
1589 mHWData.backup();
1590 }
1591 }
1592
1593 mHWData->mCPUHotPlugEnabled = aEnabled;
1594
1595 return rc;
1596}
1597
1598STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1599{
1600#ifdef VBOX_WITH_USB_CARDREADER
1601 CheckComArgOutPointerValid(aEnabled);
1602
1603 AutoCaller autoCaller(this);
1604 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1605
1606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1607
1608 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1609
1610 return S_OK;
1611#else
1612 NOREF(aEnabled);
1613 return E_NOTIMPL;
1614#endif
1615}
1616
1617STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1618{
1619#ifdef VBOX_WITH_USB_CARDREADER
1620 AutoCaller autoCaller(this);
1621 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 HRESULT rc = checkStateDependency(MutableStateDep);
1625 if (FAILED(rc)) return rc;
1626
1627 setModified(IsModified_MachineData);
1628 mHWData.backup();
1629 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1630
1631 return S_OK;
1632#else
1633 NOREF(aEnabled);
1634 return E_NOTIMPL;
1635#endif
1636}
1637
1638STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1639{
1640#ifdef VBOX_WITH_USB_VIDEO
1641 CheckComArgOutPointerValid(aEnabled);
1642
1643 AutoCaller autoCaller(this);
1644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1645
1646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1647
1648 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1649
1650 return S_OK;
1651#else
1652 NOREF(aEnabled);
1653 return E_NOTIMPL;
1654#endif
1655}
1656
1657STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1658{
1659#ifdef VBOX_WITH_USB_VIDEO
1660 AutoCaller autoCaller(this);
1661 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1663
1664 HRESULT rc = checkStateDependency(MutableStateDep);
1665 if (FAILED(rc)) return rc;
1666
1667 setModified(IsModified_MachineData);
1668 mHWData.backup();
1669 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1670
1671 return S_OK;
1672#else
1673 NOREF(aEnabled);
1674 return E_NOTIMPL;
1675#endif
1676}
1677
1678STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1679{
1680 CheckComArgOutPointerValid(aEnabled);
1681
1682 AutoCaller autoCaller(this);
1683 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1685
1686 *aEnabled = mHWData->mHPETEnabled;
1687
1688 return S_OK;
1689}
1690
1691STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1692{
1693 HRESULT rc = S_OK;
1694
1695 AutoCaller autoCaller(this);
1696 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1698
1699 rc = checkStateDependency(MutableStateDep);
1700 if (FAILED(rc)) return rc;
1701
1702 setModified(IsModified_MachineData);
1703 mHWData.backup();
1704
1705 mHWData->mHPETEnabled = aEnabled;
1706
1707 return rc;
1708}
1709
1710STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1711{
1712 AutoCaller autoCaller(this);
1713 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1714
1715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 *fEnabled = mHWData->mVideoCaptureEnabled;
1718 return S_OK;
1719}
1720
1721STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1722{
1723 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 // deep copy extradata
9982 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9983
9984 config.uuid = mData->mUuid;
9985
9986 // copy name, description, OS type, teleport, UTC etc.
9987 config.machineUserData = mUserData->s;
9988
9989 // Encode the Icon Override data from Machine and store on config userdata.
9990 com::SafeArray<BYTE> iconByte;
9991 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
9992 ssize_t cbData = iconByte.size();
9993 if (cbData > 0)
9994 {
9995 ssize_t cchOut = RTBase64EncodedLength(cbData);
9996 Utf8Str strIconData;
9997 strIconData.reserve(cchOut+1);
9998 int vrc = RTBase64Encode(iconByte.raw(), cbData,
9999 strIconData.mutableRaw(), strIconData.capacity(),
10000 NULL);
10001 if (RT_FAILURE(vrc))
10002 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10003 strIconData.jolt();
10004 config.machineUserData.ovIcon = strIconData;
10005 }
10006 else
10007 config.machineUserData.ovIcon.setNull();
10008
10009 if ( mData->mMachineState == MachineState_Saved
10010 || mData->mMachineState == MachineState_Restoring
10011 // when deleting a snapshot we may or may not have a saved state in the current state,
10012 // so let's not assert here please
10013 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10014 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10015 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10016 && (!mSSData->strStateFilePath.isEmpty())
10017 )
10018 )
10019 {
10020 Assert(!mSSData->strStateFilePath.isEmpty());
10021 /* try to make the file name relative to the settings file dir */
10022 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10023 }
10024 else
10025 {
10026 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10027 config.strStateFile.setNull();
10028 }
10029
10030 if (mData->mCurrentSnapshot)
10031 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10032 else
10033 config.uuidCurrentSnapshot.clear();
10034
10035 config.timeLastStateChange = mData->mLastStateChange;
10036 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10037 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10038
10039 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10040 if (FAILED(rc)) throw rc;
10041
10042 rc = saveStorageControllers(config.storageMachine);
10043 if (FAILED(rc)) throw rc;
10044
10045 // save machine's media registry if this is VirtualBox 4.0 or later
10046 if (config.canHaveOwnMediaRegistry())
10047 {
10048 // determine machine folder
10049 Utf8Str strMachineFolder = getSettingsFileFull();
10050 strMachineFolder.stripFilename();
10051 mParent->saveMediaRegistry(config.mediaRegistry,
10052 getId(), // only media with registry ID == machine UUID
10053 strMachineFolder);
10054 // this throws HRESULT
10055 }
10056
10057 // save snapshots
10058 rc = saveAllSnapshots(config);
10059 if (FAILED(rc)) throw rc;
10060}
10061
10062/**
10063 * Saves all snapshots of the machine into the given machine config file. Called
10064 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10065 * @param config
10066 * @return
10067 */
10068HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10069{
10070 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10071
10072 HRESULT rc = S_OK;
10073
10074 try
10075 {
10076 config.llFirstSnapshot.clear();
10077
10078 if (mData->mFirstSnapshot)
10079 {
10080 settings::Snapshot snapNew;
10081 config.llFirstSnapshot.push_back(snapNew);
10082
10083 // get reference to the fresh copy of the snapshot on the list and
10084 // work on that copy directly to avoid excessive copying later
10085 settings::Snapshot &snap = config.llFirstSnapshot.front();
10086
10087 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10088 if (FAILED(rc)) throw rc;
10089 }
10090
10091// if (mType == IsSessionMachine)
10092// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10093
10094 }
10095 catch (HRESULT err)
10096 {
10097 /* we assume that error info is set by the thrower */
10098 rc = err;
10099 }
10100 catch (...)
10101 {
10102 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10103 }
10104
10105 return rc;
10106}
10107
10108/**
10109 * Saves the VM hardware configuration. It is assumed that the
10110 * given node is empty.
10111 *
10112 * @param data Reference to the settings object for the hardware config.
10113 * @param pDbg Pointer to the settings object for the debugging config
10114 * which happens to live in mHWData.
10115 * @param pAutostart Pointer to the settings object for the autostart config
10116 * which happens to live in mHWData.
10117 */
10118HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10119 settings::Autostart *pAutostart)
10120{
10121 HRESULT rc = S_OK;
10122
10123 try
10124 {
10125 /* The hardware version attribute (optional).
10126 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10127 if ( mHWData->mHWVersion == "1"
10128 && mSSData->strStateFilePath.isEmpty()
10129 )
10130 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. */
10131
10132 data.strVersion = mHWData->mHWVersion;
10133 data.uuid = mHWData->mHardwareUUID;
10134
10135 // CPU
10136 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10137 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10138 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10139 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10140 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10141 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10142 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10143 data.fPAE = !!mHWData->mPAEEnabled;
10144 data.enmLongMode = mHWData->mLongMode;
10145 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10146
10147 /* Standard and Extended CPUID leafs. */
10148 data.llCpuIdLeafs.clear();
10149 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10150 {
10151 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10152 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10153 }
10154 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10155 {
10156 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10157 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10158 }
10159
10160 data.cCPUs = mHWData->mCPUCount;
10161 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10162 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10163
10164 data.llCpus.clear();
10165 if (data.fCpuHotPlug)
10166 {
10167 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10168 {
10169 if (mHWData->mCPUAttached[idx])
10170 {
10171 settings::Cpu cpu;
10172 cpu.ulId = idx;
10173 data.llCpus.push_back(cpu);
10174 }
10175 }
10176 }
10177
10178 // memory
10179 data.ulMemorySizeMB = mHWData->mMemorySize;
10180 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10181
10182 // firmware
10183 data.firmwareType = mHWData->mFirmwareType;
10184
10185 // HID
10186 data.pointingHIDType = mHWData->mPointingHIDType;
10187 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10188
10189 // chipset
10190 data.chipsetType = mHWData->mChipsetType;
10191
10192 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10193 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10194
10195 // HPET
10196 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10197
10198 // boot order
10199 data.mapBootOrder.clear();
10200 for (size_t i = 0;
10201 i < RT_ELEMENTS(mHWData->mBootOrder);
10202 ++i)
10203 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10204
10205 // display
10206 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10207 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10208 data.cMonitors = mHWData->mMonitorCount;
10209 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10210 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10211 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10212 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10213 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10214 data.ulVideoCaptureFps = mHWData->mVideoCaptureFps;
10215 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10216 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10217 {
10218 if (mHWData->maVideoCaptureScreens[i])
10219 ASMBitSet(&data.u64VideoCaptureScreens, i);
10220 else
10221 ASMBitClear(&data.u64VideoCaptureScreens, i);
10222 }
10223 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
10224
10225 /* VRDEServer settings (optional) */
10226 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10227 if (FAILED(rc)) throw rc;
10228
10229 /* BIOS (required) */
10230 rc = mBIOSSettings->saveSettings(data.biosSettings);
10231 if (FAILED(rc)) throw rc;
10232
10233 /* USB Controller (required) */
10234 rc = mUSBController->saveSettings(data.usbController);
10235 if (FAILED(rc)) throw rc;
10236
10237 /* Network adapters (required) */
10238 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10239 data.llNetworkAdapters.clear();
10240 /* Write out only the nominal number of network adapters for this
10241 * chipset type. Since Machine::commit() hasn't been called there
10242 * may be extra NIC settings in the vector. */
10243 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10244 {
10245 settings::NetworkAdapter nic;
10246 nic.ulSlot = slot;
10247 /* paranoia check... must not be NULL, but must not crash either. */
10248 if (mNetworkAdapters[slot])
10249 {
10250 rc = mNetworkAdapters[slot]->saveSettings(nic);
10251 if (FAILED(rc)) throw rc;
10252
10253 data.llNetworkAdapters.push_back(nic);
10254 }
10255 }
10256
10257 /* Serial ports */
10258 data.llSerialPorts.clear();
10259 for (ULONG slot = 0;
10260 slot < RT_ELEMENTS(mSerialPorts);
10261 ++slot)
10262 {
10263 settings::SerialPort s;
10264 s.ulSlot = slot;
10265 rc = mSerialPorts[slot]->saveSettings(s);
10266 if (FAILED(rc)) return rc;
10267
10268 data.llSerialPorts.push_back(s);
10269 }
10270
10271 /* Parallel ports */
10272 data.llParallelPorts.clear();
10273 for (ULONG slot = 0;
10274 slot < RT_ELEMENTS(mParallelPorts);
10275 ++slot)
10276 {
10277 settings::ParallelPort p;
10278 p.ulSlot = slot;
10279 rc = mParallelPorts[slot]->saveSettings(p);
10280 if (FAILED(rc)) return rc;
10281
10282 data.llParallelPorts.push_back(p);
10283 }
10284
10285 /* Audio adapter */
10286 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10287 if (FAILED(rc)) return rc;
10288
10289 /* Shared folders */
10290 data.llSharedFolders.clear();
10291 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10292 it != mHWData->mSharedFolders.end();
10293 ++it)
10294 {
10295 SharedFolder *pSF = *it;
10296 AutoCaller sfCaller(pSF);
10297 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10298 settings::SharedFolder sf;
10299 sf.strName = pSF->getName();
10300 sf.strHostPath = pSF->getHostPath();
10301 sf.fWritable = !!pSF->isWritable();
10302 sf.fAutoMount = !!pSF->isAutoMounted();
10303
10304 data.llSharedFolders.push_back(sf);
10305 }
10306
10307 // clipboard
10308 data.clipboardMode = mHWData->mClipboardMode;
10309
10310 // drag'n'drop
10311 data.dragAndDropMode = mHWData->mDragAndDropMode;
10312
10313 /* Guest */
10314 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10315
10316 // IO settings
10317 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10318 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10319
10320 /* BandwidthControl (required) */
10321 rc = mBandwidthControl->saveSettings(data.ioSettings);
10322 if (FAILED(rc)) throw rc;
10323
10324 /* Host PCI devices */
10325 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10326 it != mHWData->mPCIDeviceAssignments.end();
10327 ++it)
10328 {
10329 ComObjPtr<PCIDeviceAttachment> pda = *it;
10330 settings::HostPCIDeviceAttachment hpda;
10331
10332 rc = pda->saveSettings(hpda);
10333 if (FAILED(rc)) throw rc;
10334
10335 data.pciAttachments.push_back(hpda);
10336 }
10337
10338
10339 // guest properties
10340 data.llGuestProperties.clear();
10341#ifdef VBOX_WITH_GUEST_PROPS
10342 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10343 it != mHWData->mGuestProperties.end();
10344 ++it)
10345 {
10346 HWData::GuestProperty property = it->second;
10347
10348 /* Remove transient guest properties at shutdown unless we
10349 * are saving state */
10350 if ( ( mData->mMachineState == MachineState_PoweredOff
10351 || mData->mMachineState == MachineState_Aborted
10352 || mData->mMachineState == MachineState_Teleported)
10353 && ( property.mFlags & guestProp::TRANSIENT
10354 || property.mFlags & guestProp::TRANSRESET))
10355 continue;
10356 settings::GuestProperty prop;
10357 prop.strName = it->first;
10358 prop.strValue = property.strValue;
10359 prop.timestamp = property.mTimestamp;
10360 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10361 guestProp::writeFlags(property.mFlags, szFlags);
10362 prop.strFlags = szFlags;
10363
10364 data.llGuestProperties.push_back(prop);
10365 }
10366
10367 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10368 /* I presume this doesn't require a backup(). */
10369 mData->mGuestPropertiesModified = FALSE;
10370#endif /* VBOX_WITH_GUEST_PROPS defined */
10371
10372 *pDbg = mHWData->mDebugging;
10373 *pAutostart = mHWData->mAutostart;
10374
10375 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10376 }
10377 catch(std::bad_alloc &)
10378 {
10379 return E_OUTOFMEMORY;
10380 }
10381
10382 AssertComRC(rc);
10383 return rc;
10384}
10385
10386/**
10387 * Saves the storage controller configuration.
10388 *
10389 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10390 */
10391HRESULT Machine::saveStorageControllers(settings::Storage &data)
10392{
10393 data.llStorageControllers.clear();
10394
10395 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10396 it != mStorageControllers->end();
10397 ++it)
10398 {
10399 HRESULT rc;
10400 ComObjPtr<StorageController> pCtl = *it;
10401
10402 settings::StorageController ctl;
10403 ctl.strName = pCtl->getName();
10404 ctl.controllerType = pCtl->getControllerType();
10405 ctl.storageBus = pCtl->getStorageBus();
10406 ctl.ulInstance = pCtl->getInstance();
10407 ctl.fBootable = pCtl->getBootable();
10408
10409 /* Save the port count. */
10410 ULONG portCount;
10411 rc = pCtl->COMGETTER(PortCount)(&portCount);
10412 ComAssertComRCRet(rc, rc);
10413 ctl.ulPortCount = portCount;
10414
10415 /* Save fUseHostIOCache */
10416 BOOL fUseHostIOCache;
10417 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10418 ComAssertComRCRet(rc, rc);
10419 ctl.fUseHostIOCache = !!fUseHostIOCache;
10420
10421 /* Save IDE emulation settings. */
10422 if (ctl.controllerType == StorageControllerType_IntelAhci)
10423 {
10424 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10425 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10426 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10427 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10428 )
10429 ComAssertComRCRet(rc, rc);
10430 }
10431
10432 /* save the devices now. */
10433 rc = saveStorageDevices(pCtl, ctl);
10434 ComAssertComRCRet(rc, rc);
10435
10436 data.llStorageControllers.push_back(ctl);
10437 }
10438
10439 return S_OK;
10440}
10441
10442/**
10443 * Saves the hard disk configuration.
10444 */
10445HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10446 settings::StorageController &data)
10447{
10448 MediaData::AttachmentList atts;
10449
10450 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10451 if (FAILED(rc)) return rc;
10452
10453 data.llAttachedDevices.clear();
10454 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10455 it != atts.end();
10456 ++it)
10457 {
10458 settings::AttachedDevice dev;
10459
10460 MediumAttachment *pAttach = *it;
10461 Medium *pMedium = pAttach->getMedium();
10462
10463 dev.deviceType = pAttach->getType();
10464 dev.lPort = pAttach->getPort();
10465 dev.lDevice = pAttach->getDevice();
10466 if (pMedium)
10467 {
10468 if (pMedium->isHostDrive())
10469 dev.strHostDriveSrc = pMedium->getLocationFull();
10470 else
10471 dev.uuid = pMedium->getId();
10472 dev.fPassThrough = pAttach->getPassthrough();
10473 dev.fTempEject = pAttach->getTempEject();
10474 dev.fNonRotational = pAttach->getNonRotational();
10475 dev.fDiscard = pAttach->getDiscard();
10476 }
10477
10478 dev.strBwGroup = pAttach->getBandwidthGroup();
10479
10480 data.llAttachedDevices.push_back(dev);
10481 }
10482
10483 return S_OK;
10484}
10485
10486/**
10487 * Saves machine state settings as defined by aFlags
10488 * (SaveSTS_* values).
10489 *
10490 * @param aFlags Combination of SaveSTS_* flags.
10491 *
10492 * @note Locks objects for writing.
10493 */
10494HRESULT Machine::saveStateSettings(int aFlags)
10495{
10496 if (aFlags == 0)
10497 return S_OK;
10498
10499 AutoCaller autoCaller(this);
10500 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10501
10502 /* This object's write lock is also necessary to serialize file access
10503 * (prevent concurrent reads and writes) */
10504 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10505
10506 HRESULT rc = S_OK;
10507
10508 Assert(mData->pMachineConfigFile);
10509
10510 try
10511 {
10512 if (aFlags & SaveSTS_CurStateModified)
10513 mData->pMachineConfigFile->fCurrentStateModified = true;
10514
10515 if (aFlags & SaveSTS_StateFilePath)
10516 {
10517 if (!mSSData->strStateFilePath.isEmpty())
10518 /* try to make the file name relative to the settings file dir */
10519 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10520 else
10521 mData->pMachineConfigFile->strStateFile.setNull();
10522 }
10523
10524 if (aFlags & SaveSTS_StateTimeStamp)
10525 {
10526 Assert( mData->mMachineState != MachineState_Aborted
10527 || mSSData->strStateFilePath.isEmpty());
10528
10529 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10530
10531 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10532//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10533 }
10534
10535 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10536 }
10537 catch (...)
10538 {
10539 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10540 }
10541
10542 return rc;
10543}
10544
10545/**
10546 * Ensures that the given medium is added to a media registry. If this machine
10547 * was created with 4.0 or later, then the machine registry is used. Otherwise
10548 * the global VirtualBox media registry is used.
10549 *
10550 * Caller must NOT hold machine lock, media tree or any medium locks!
10551 *
10552 * @param pMedium
10553 */
10554void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10555{
10556 /* Paranoia checks: do not hold machine or media tree locks. */
10557 AssertReturnVoid(!isWriteLockOnCurrentThread());
10558 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10559
10560 ComObjPtr<Medium> pBase;
10561 {
10562 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10563 pBase = pMedium->getBase();
10564 }
10565
10566 /* Paranoia checks: do not hold medium locks. */
10567 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10568 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10569
10570 // decide which medium registry to use now that the medium is attached:
10571 Guid uuid;
10572 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10573 // machine XML is VirtualBox 4.0 or higher:
10574 uuid = getId(); // machine UUID
10575 else
10576 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10577
10578 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10579 mParent->markRegistryModified(uuid);
10580
10581 /* For more complex hard disk structures it can happen that the base
10582 * medium isn't yet associated with any medium registry. Do that now. */
10583 if (pMedium != pBase)
10584 {
10585 if (pBase->addRegistry(uuid, true /* fRecurse */))
10586 mParent->markRegistryModified(uuid);
10587 }
10588}
10589
10590/**
10591 * Creates differencing hard disks for all normal hard disks attached to this
10592 * machine and a new set of attachments to refer to created disks.
10593 *
10594 * Used when taking a snapshot or when deleting the current state. Gets called
10595 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10596 *
10597 * This method assumes that mMediaData contains the original hard disk attachments
10598 * it needs to create diffs for. On success, these attachments will be replaced
10599 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10600 * called to delete created diffs which will also rollback mMediaData and restore
10601 * whatever was backed up before calling this method.
10602 *
10603 * Attachments with non-normal hard disks are left as is.
10604 *
10605 * If @a aOnline is @c false then the original hard disks that require implicit
10606 * diffs will be locked for reading. Otherwise it is assumed that they are
10607 * already locked for writing (when the VM was started). Note that in the latter
10608 * case it is responsibility of the caller to lock the newly created diffs for
10609 * writing if this method succeeds.
10610 *
10611 * @param aProgress Progress object to run (must contain at least as
10612 * many operations left as the number of hard disks
10613 * attached).
10614 * @param aOnline Whether the VM was online prior to this operation.
10615 *
10616 * @note The progress object is not marked as completed, neither on success nor
10617 * on failure. This is a responsibility of the caller.
10618 *
10619 * @note Locks this object and the media tree for writing.
10620 */
10621HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10622 ULONG aWeight,
10623 bool aOnline)
10624{
10625 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10626
10627 AutoCaller autoCaller(this);
10628 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10629
10630 AutoMultiWriteLock2 alock(this->lockHandle(),
10631 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10632
10633 /* must be in a protective state because we release the lock below */
10634 AssertReturn( mData->mMachineState == MachineState_Saving
10635 || mData->mMachineState == MachineState_LiveSnapshotting
10636 || mData->mMachineState == MachineState_RestoringSnapshot
10637 || mData->mMachineState == MachineState_DeletingSnapshot
10638 , E_FAIL);
10639
10640 HRESULT rc = S_OK;
10641
10642 // use appropriate locked media map (online or offline)
10643 MediumLockListMap lockedMediaOffline;
10644 MediumLockListMap *lockedMediaMap;
10645 if (aOnline)
10646 lockedMediaMap = &mData->mSession.mLockedMedia;
10647 else
10648 lockedMediaMap = &lockedMediaOffline;
10649
10650 try
10651 {
10652 if (!aOnline)
10653 {
10654 /* lock all attached hard disks early to detect "in use"
10655 * situations before creating actual diffs */
10656 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10657 it != mMediaData->mAttachments.end();
10658 ++it)
10659 {
10660 MediumAttachment* pAtt = *it;
10661 if (pAtt->getType() == DeviceType_HardDisk)
10662 {
10663 Medium* pMedium = pAtt->getMedium();
10664 Assert(pMedium);
10665
10666 MediumLockList *pMediumLockList(new MediumLockList());
10667 alock.release();
10668 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10669 false /* fMediumLockWrite */,
10670 NULL,
10671 *pMediumLockList);
10672 alock.acquire();
10673 if (FAILED(rc))
10674 {
10675 delete pMediumLockList;
10676 throw rc;
10677 }
10678 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10679 if (FAILED(rc))
10680 {
10681 throw setError(rc,
10682 tr("Collecting locking information for all attached media failed"));
10683 }
10684 }
10685 }
10686
10687 /* Now lock all media. If this fails, nothing is locked. */
10688 alock.release();
10689 rc = lockedMediaMap->Lock();
10690 alock.acquire();
10691 if (FAILED(rc))
10692 {
10693 throw setError(rc,
10694 tr("Locking of attached media failed"));
10695 }
10696 }
10697
10698 /* remember the current list (note that we don't use backup() since
10699 * mMediaData may be already backed up) */
10700 MediaData::AttachmentList atts = mMediaData->mAttachments;
10701
10702 /* start from scratch */
10703 mMediaData->mAttachments.clear();
10704
10705 /* go through remembered attachments and create diffs for normal hard
10706 * disks and attach them */
10707 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10708 it != atts.end();
10709 ++it)
10710 {
10711 MediumAttachment* pAtt = *it;
10712
10713 DeviceType_T devType = pAtt->getType();
10714 Medium* pMedium = pAtt->getMedium();
10715
10716 if ( devType != DeviceType_HardDisk
10717 || pMedium == NULL
10718 || pMedium->getType() != MediumType_Normal)
10719 {
10720 /* copy the attachment as is */
10721
10722 /** @todo the progress object created in Console::TakeSnaphot
10723 * only expects operations for hard disks. Later other
10724 * device types need to show up in the progress as well. */
10725 if (devType == DeviceType_HardDisk)
10726 {
10727 if (pMedium == NULL)
10728 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10729 aWeight); // weight
10730 else
10731 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10732 pMedium->getBase()->getName().c_str()).raw(),
10733 aWeight); // weight
10734 }
10735
10736 mMediaData->mAttachments.push_back(pAtt);
10737 continue;
10738 }
10739
10740 /* need a diff */
10741 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10742 pMedium->getBase()->getName().c_str()).raw(),
10743 aWeight); // weight
10744
10745 Utf8Str strFullSnapshotFolder;
10746 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10747
10748 ComObjPtr<Medium> diff;
10749 diff.createObject();
10750 // store the diff in the same registry as the parent
10751 // (this cannot fail here because we can't create implicit diffs for
10752 // unregistered images)
10753 Guid uuidRegistryParent;
10754 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10755 Assert(fInRegistry); NOREF(fInRegistry);
10756 rc = diff->init(mParent,
10757 pMedium->getPreferredDiffFormat(),
10758 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10759 uuidRegistryParent);
10760 if (FAILED(rc)) throw rc;
10761
10762 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10763 * the push_back? Looks like we're going to release medium with the
10764 * wrong kind of lock (general issue with if we fail anywhere at all)
10765 * and an orphaned VDI in the snapshots folder. */
10766
10767 /* update the appropriate lock list */
10768 MediumLockList *pMediumLockList;
10769 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10770 AssertComRCThrowRC(rc);
10771 if (aOnline)
10772 {
10773 alock.release();
10774 /* The currently attached medium will be read-only, change
10775 * the lock type to read. */
10776 rc = pMediumLockList->Update(pMedium, false);
10777 alock.acquire();
10778 AssertComRCThrowRC(rc);
10779 }
10780
10781 /* release the locks before the potentially lengthy operation */
10782 alock.release();
10783 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10784 pMediumLockList,
10785 NULL /* aProgress */,
10786 true /* aWait */);
10787 alock.acquire();
10788 if (FAILED(rc)) throw rc;
10789
10790 /* actual lock list update is done in Medium::commitMedia */
10791
10792 rc = diff->addBackReference(mData->mUuid);
10793 AssertComRCThrowRC(rc);
10794
10795 /* add a new attachment */
10796 ComObjPtr<MediumAttachment> attachment;
10797 attachment.createObject();
10798 rc = attachment->init(this,
10799 diff,
10800 pAtt->getControllerName(),
10801 pAtt->getPort(),
10802 pAtt->getDevice(),
10803 DeviceType_HardDisk,
10804 true /* aImplicit */,
10805 false /* aPassthrough */,
10806 false /* aTempEject */,
10807 pAtt->getNonRotational(),
10808 pAtt->getDiscard(),
10809 pAtt->getBandwidthGroup());
10810 if (FAILED(rc)) throw rc;
10811
10812 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10813 AssertComRCThrowRC(rc);
10814 mMediaData->mAttachments.push_back(attachment);
10815 }
10816 }
10817 catch (HRESULT aRC) { rc = aRC; }
10818
10819 /* unlock all hard disks we locked when there is no VM */
10820 if (!aOnline)
10821 {
10822 ErrorInfoKeeper eik;
10823
10824 HRESULT rc1 = lockedMediaMap->Clear();
10825 AssertComRC(rc1);
10826 }
10827
10828 return rc;
10829}
10830
10831/**
10832 * Deletes implicit differencing hard disks created either by
10833 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10834 *
10835 * Note that to delete hard disks created by #AttachDevice() this method is
10836 * called from #fixupMedia() when the changes are rolled back.
10837 *
10838 * @note Locks this object and the media tree for writing.
10839 */
10840HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10841{
10842 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10843
10844 AutoCaller autoCaller(this);
10845 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10846
10847 AutoMultiWriteLock2 alock(this->lockHandle(),
10848 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10849
10850 /* We absolutely must have backed up state. */
10851 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10852
10853 /* Check if there are any implicitly created diff images. */
10854 bool fImplicitDiffs = false;
10855 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10856 it != mMediaData->mAttachments.end();
10857 ++it)
10858 {
10859 const ComObjPtr<MediumAttachment> &pAtt = *it;
10860 if (pAtt->isImplicit())
10861 {
10862 fImplicitDiffs = true;
10863 break;
10864 }
10865 }
10866 /* If there is nothing to do, leave early. This saves lots of image locking
10867 * effort. It also avoids a MachineStateChanged event without real reason.
10868 * This is important e.g. when loading a VM config, because there should be
10869 * no events. Otherwise API clients can become thoroughly confused for
10870 * inaccessible VMs (the code for loading VM configs uses this method for
10871 * cleanup if the config makes no sense), as they take such events as an
10872 * indication that the VM is alive, and they would force the VM config to
10873 * be reread, leading to an endless loop. */
10874 if (!fImplicitDiffs)
10875 return S_OK;
10876
10877 HRESULT rc = S_OK;
10878 MachineState_T oldState = mData->mMachineState;
10879
10880 /* will release the lock before the potentially lengthy operation,
10881 * so protect with the special state (unless already protected) */
10882 if ( oldState != MachineState_Saving
10883 && oldState != MachineState_LiveSnapshotting
10884 && oldState != MachineState_RestoringSnapshot
10885 && oldState != MachineState_DeletingSnapshot
10886 && oldState != MachineState_DeletingSnapshotOnline
10887 && oldState != MachineState_DeletingSnapshotPaused
10888 )
10889 setMachineState(MachineState_SettingUp);
10890
10891 // use appropriate locked media map (online or offline)
10892 MediumLockListMap lockedMediaOffline;
10893 MediumLockListMap *lockedMediaMap;
10894 if (aOnline)
10895 lockedMediaMap = &mData->mSession.mLockedMedia;
10896 else
10897 lockedMediaMap = &lockedMediaOffline;
10898
10899 try
10900 {
10901 if (!aOnline)
10902 {
10903 /* lock all attached hard disks early to detect "in use"
10904 * situations before deleting actual diffs */
10905 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10906 it != mMediaData->mAttachments.end();
10907 ++it)
10908 {
10909 MediumAttachment* pAtt = *it;
10910 if (pAtt->getType() == DeviceType_HardDisk)
10911 {
10912 Medium* pMedium = pAtt->getMedium();
10913 Assert(pMedium);
10914
10915 MediumLockList *pMediumLockList(new MediumLockList());
10916 alock.release();
10917 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10918 false /* fMediumLockWrite */,
10919 NULL,
10920 *pMediumLockList);
10921 alock.acquire();
10922
10923 if (FAILED(rc))
10924 {
10925 delete pMediumLockList;
10926 throw rc;
10927 }
10928
10929 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10930 if (FAILED(rc))
10931 throw rc;
10932 }
10933 }
10934
10935 if (FAILED(rc))
10936 throw rc;
10937 } // end of offline
10938
10939 /* Lock lists are now up to date and include implicitly created media */
10940
10941 /* Go through remembered attachments and delete all implicitly created
10942 * diffs and fix up the attachment information */
10943 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10944 MediaData::AttachmentList implicitAtts;
10945 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10946 it != mMediaData->mAttachments.end();
10947 ++it)
10948 {
10949 ComObjPtr<MediumAttachment> pAtt = *it;
10950 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10951 if (pMedium.isNull())
10952 continue;
10953
10954 // Implicit attachments go on the list for deletion and back references are removed.
10955 if (pAtt->isImplicit())
10956 {
10957 /* Deassociate and mark for deletion */
10958 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10959 rc = pMedium->removeBackReference(mData->mUuid);
10960 if (FAILED(rc))
10961 throw rc;
10962 implicitAtts.push_back(pAtt);
10963 continue;
10964 }
10965
10966 /* Was this medium attached before? */
10967 if (!findAttachment(oldAtts, pMedium))
10968 {
10969 /* no: de-associate */
10970 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10971 rc = pMedium->removeBackReference(mData->mUuid);
10972 if (FAILED(rc))
10973 throw rc;
10974 continue;
10975 }
10976 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10977 }
10978
10979 /* If there are implicit attachments to delete, throw away the lock
10980 * map contents (which will unlock all media) since the medium
10981 * attachments will be rolled back. Below we need to completely
10982 * recreate the lock map anyway since it is infinitely complex to
10983 * do this incrementally (would need reconstructing each attachment
10984 * change, which would be extremely hairy). */
10985 if (implicitAtts.size() != 0)
10986 {
10987 ErrorInfoKeeper eik;
10988
10989 HRESULT rc1 = lockedMediaMap->Clear();
10990 AssertComRC(rc1);
10991 }
10992
10993 /* rollback hard disk changes */
10994 mMediaData.rollback();
10995
10996 MultiResult mrc(S_OK);
10997
10998 // Delete unused implicit diffs.
10999 if (implicitAtts.size() != 0)
11000 {
11001 alock.release();
11002
11003 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11004 it != implicitAtts.end();
11005 ++it)
11006 {
11007 // Remove medium associated with this attachment.
11008 ComObjPtr<MediumAttachment> pAtt = *it;
11009 Assert(pAtt);
11010 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11011 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11012 Assert(pMedium);
11013
11014 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11015 // continue on delete failure, just collect error messages
11016 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11017 mrc = rc;
11018 }
11019
11020 alock.acquire();
11021
11022 /* if there is a VM recreate media lock map as mentioned above,
11023 * otherwise it is a waste of time and we leave things unlocked */
11024 if (aOnline)
11025 {
11026 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11027 /* must never be NULL, but better safe than sorry */
11028 if (!pMachine.isNull())
11029 {
11030 alock.release();
11031 rc = mData->mSession.mMachine->lockMedia();
11032 alock.acquire();
11033 if (FAILED(rc))
11034 throw rc;
11035 }
11036 }
11037 }
11038 }
11039 catch (HRESULT aRC) {rc = aRC;}
11040
11041 if (mData->mMachineState == MachineState_SettingUp)
11042 setMachineState(oldState);
11043
11044 /* unlock all hard disks we locked when there is no VM */
11045 if (!aOnline)
11046 {
11047 ErrorInfoKeeper eik;
11048
11049 HRESULT rc1 = lockedMediaMap->Clear();
11050 AssertComRC(rc1);
11051 }
11052
11053 return rc;
11054}
11055
11056
11057/**
11058 * Looks through the given list of media attachments for one with the given parameters
11059 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11060 * can be searched as well if needed.
11061 *
11062 * @param list
11063 * @param aControllerName
11064 * @param aControllerPort
11065 * @param aDevice
11066 * @return
11067 */
11068MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11069 IN_BSTR aControllerName,
11070 LONG aControllerPort,
11071 LONG aDevice)
11072{
11073 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11074 it != ll.end();
11075 ++it)
11076 {
11077 MediumAttachment *pAttach = *it;
11078 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11079 return pAttach;
11080 }
11081
11082 return NULL;
11083}
11084
11085/**
11086 * Looks through the given list of media attachments for one with the given parameters
11087 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11088 * can be searched as well if needed.
11089 *
11090 * @param list
11091 * @param aControllerName
11092 * @param aControllerPort
11093 * @param aDevice
11094 * @return
11095 */
11096MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11097 ComObjPtr<Medium> pMedium)
11098{
11099 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11100 it != ll.end();
11101 ++it)
11102 {
11103 MediumAttachment *pAttach = *it;
11104 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11105 if (pMediumThis == pMedium)
11106 return pAttach;
11107 }
11108
11109 return NULL;
11110}
11111
11112/**
11113 * Looks through the given list of media attachments for one with the given parameters
11114 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11115 * can be searched as well if needed.
11116 *
11117 * @param list
11118 * @param aControllerName
11119 * @param aControllerPort
11120 * @param aDevice
11121 * @return
11122 */
11123MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11124 Guid &id)
11125{
11126 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11127 it != ll.end();
11128 ++it)
11129 {
11130 MediumAttachment *pAttach = *it;
11131 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11132 if (pMediumThis->getId() == id)
11133 return pAttach;
11134 }
11135
11136 return NULL;
11137}
11138
11139/**
11140 * Main implementation for Machine::DetachDevice. This also gets called
11141 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11142 *
11143 * @param pAttach Medium attachment to detach.
11144 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11145 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11146 * @return
11147 */
11148HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11149 AutoWriteLock &writeLock,
11150 Snapshot *pSnapshot)
11151{
11152 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11153 DeviceType_T mediumType = pAttach->getType();
11154
11155 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11156
11157 if (pAttach->isImplicit())
11158 {
11159 /* attempt to implicitly delete the implicitly created diff */
11160
11161 /// @todo move the implicit flag from MediumAttachment to Medium
11162 /// and forbid any hard disk operation when it is implicit. Or maybe
11163 /// a special media state for it to make it even more simple.
11164
11165 Assert(mMediaData.isBackedUp());
11166
11167 /* will release the lock before the potentially lengthy operation, so
11168 * protect with the special state */
11169 MachineState_T oldState = mData->mMachineState;
11170 setMachineState(MachineState_SettingUp);
11171
11172 writeLock.release();
11173
11174 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11175 true /*aWait*/);
11176
11177 writeLock.acquire();
11178
11179 setMachineState(oldState);
11180
11181 if (FAILED(rc)) return rc;
11182 }
11183
11184 setModified(IsModified_Storage);
11185 mMediaData.backup();
11186 mMediaData->mAttachments.remove(pAttach);
11187
11188 if (!oldmedium.isNull())
11189 {
11190 // if this is from a snapshot, do not defer detachment to commitMedia()
11191 if (pSnapshot)
11192 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11193 // else if non-hard disk media, do not defer detachment to commitMedia() either
11194 else if (mediumType != DeviceType_HardDisk)
11195 oldmedium->removeBackReference(mData->mUuid);
11196 }
11197
11198 return S_OK;
11199}
11200
11201/**
11202 * Goes thru all media of the given list and
11203 *
11204 * 1) calls detachDevice() on each of them for this machine and
11205 * 2) adds all Medium objects found in the process to the given list,
11206 * depending on cleanupMode.
11207 *
11208 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11209 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11210 * media to the list.
11211 *
11212 * This gets called from Machine::Unregister, both for the actual Machine and
11213 * the SnapshotMachine objects that might be found in the snapshots.
11214 *
11215 * Requires caller and locking. The machine lock must be passed in because it
11216 * will be passed on to detachDevice which needs it for temporary unlocking.
11217 *
11218 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11219 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11220 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11221 * otherwise no media get added.
11222 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11223 * @return
11224 */
11225HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11226 Snapshot *pSnapshot,
11227 CleanupMode_T cleanupMode,
11228 MediaList &llMedia)
11229{
11230 Assert(isWriteLockOnCurrentThread());
11231
11232 HRESULT rc;
11233
11234 // make a temporary list because detachDevice invalidates iterators into
11235 // mMediaData->mAttachments
11236 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11237
11238 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11239 it != llAttachments2.end();
11240 ++it)
11241 {
11242 ComObjPtr<MediumAttachment> &pAttach = *it;
11243 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11244
11245 if (!pMedium.isNull())
11246 {
11247 AutoCaller mac(pMedium);
11248 if (FAILED(mac.rc())) return mac.rc();
11249 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11250 DeviceType_T devType = pMedium->getDeviceType();
11251 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11252 && devType == DeviceType_HardDisk)
11253 || (cleanupMode == CleanupMode_Full)
11254 )
11255 {
11256 llMedia.push_back(pMedium);
11257 ComObjPtr<Medium> pParent = pMedium->getParent();
11258 /*
11259 * Search for medias which are not attached to any machine, but
11260 * in the chain to an attached disk. Mediums are only consided
11261 * if they are:
11262 * - have only one child
11263 * - no references to any machines
11264 * - are of normal medium type
11265 */
11266 while (!pParent.isNull())
11267 {
11268 AutoCaller mac1(pParent);
11269 if (FAILED(mac1.rc())) return mac1.rc();
11270 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11271 if (pParent->getChildren().size() == 1)
11272 {
11273 if ( pParent->getMachineBackRefCount() == 0
11274 && pParent->getType() == MediumType_Normal
11275 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11276 llMedia.push_back(pParent);
11277 }
11278 else
11279 break;
11280 pParent = pParent->getParent();
11281 }
11282 }
11283 }
11284
11285 // real machine: then we need to use the proper method
11286 rc = detachDevice(pAttach, writeLock, pSnapshot);
11287
11288 if (FAILED(rc))
11289 return rc;
11290 }
11291
11292 return S_OK;
11293}
11294
11295/**
11296 * Perform deferred hard disk detachments.
11297 *
11298 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11299 * backed up).
11300 *
11301 * If @a aOnline is @c true then this method will also unlock the old hard disks
11302 * for which the new implicit diffs were created and will lock these new diffs for
11303 * writing.
11304 *
11305 * @param aOnline Whether the VM was online prior to this operation.
11306 *
11307 * @note Locks this object for writing!
11308 */
11309void Machine::commitMedia(bool aOnline /*= false*/)
11310{
11311 AutoCaller autoCaller(this);
11312 AssertComRCReturnVoid(autoCaller.rc());
11313
11314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11315
11316 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11317
11318 HRESULT rc = S_OK;
11319
11320 /* no attach/detach operations -- nothing to do */
11321 if (!mMediaData.isBackedUp())
11322 return;
11323
11324 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11325 bool fMediaNeedsLocking = false;
11326
11327 /* enumerate new attachments */
11328 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11329 it != mMediaData->mAttachments.end();
11330 ++it)
11331 {
11332 MediumAttachment *pAttach = *it;
11333
11334 pAttach->commit();
11335
11336 Medium* pMedium = pAttach->getMedium();
11337 bool fImplicit = pAttach->isImplicit();
11338
11339 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11340 (pMedium) ? pMedium->getName().c_str() : "NULL",
11341 fImplicit));
11342
11343 /** @todo convert all this Machine-based voodoo to MediumAttachment
11344 * based commit logic. */
11345 if (fImplicit)
11346 {
11347 /* convert implicit attachment to normal */
11348 pAttach->setImplicit(false);
11349
11350 if ( aOnline
11351 && pMedium
11352 && pAttach->getType() == DeviceType_HardDisk
11353 )
11354 {
11355 ComObjPtr<Medium> parent = pMedium->getParent();
11356 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11357
11358 /* update the appropriate lock list */
11359 MediumLockList *pMediumLockList;
11360 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11361 AssertComRC(rc);
11362 if (pMediumLockList)
11363 {
11364 /* unlock if there's a need to change the locking */
11365 if (!fMediaNeedsLocking)
11366 {
11367 rc = mData->mSession.mLockedMedia.Unlock();
11368 AssertComRC(rc);
11369 fMediaNeedsLocking = true;
11370 }
11371 rc = pMediumLockList->Update(parent, false);
11372 AssertComRC(rc);
11373 rc = pMediumLockList->Append(pMedium, true);
11374 AssertComRC(rc);
11375 }
11376 }
11377
11378 continue;
11379 }
11380
11381 if (pMedium)
11382 {
11383 /* was this medium attached before? */
11384 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11385 oldIt != oldAtts.end();
11386 ++oldIt)
11387 {
11388 MediumAttachment *pOldAttach = *oldIt;
11389 if (pOldAttach->getMedium() == pMedium)
11390 {
11391 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11392
11393 /* yes: remove from old to avoid de-association */
11394 oldAtts.erase(oldIt);
11395 break;
11396 }
11397 }
11398 }
11399 }
11400
11401 /* enumerate remaining old attachments and de-associate from the
11402 * current machine state */
11403 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11404 it != oldAtts.end();
11405 ++it)
11406 {
11407 MediumAttachment *pAttach = *it;
11408 Medium* pMedium = pAttach->getMedium();
11409
11410 /* Detach only hard disks, since DVD/floppy media is detached
11411 * instantly in MountMedium. */
11412 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11413 {
11414 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11415
11416 /* now de-associate from the current machine state */
11417 rc = pMedium->removeBackReference(mData->mUuid);
11418 AssertComRC(rc);
11419
11420 if (aOnline)
11421 {
11422 /* unlock since medium is not used anymore */
11423 MediumLockList *pMediumLockList;
11424 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11425 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11426 {
11427 /* this happens for online snapshots, there the attachment
11428 * is changing, but only to a diff image created under
11429 * the old one, so there is no separate lock list */
11430 Assert(!pMediumLockList);
11431 }
11432 else
11433 {
11434 AssertComRC(rc);
11435 if (pMediumLockList)
11436 {
11437 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11438 AssertComRC(rc);
11439 }
11440 }
11441 }
11442 }
11443 }
11444
11445 /* take media locks again so that the locking state is consistent */
11446 if (fMediaNeedsLocking)
11447 {
11448 Assert(aOnline);
11449 rc = mData->mSession.mLockedMedia.Lock();
11450 AssertComRC(rc);
11451 }
11452
11453 /* commit the hard disk changes */
11454 mMediaData.commit();
11455
11456 if (isSessionMachine())
11457 {
11458 /*
11459 * Update the parent machine to point to the new owner.
11460 * This is necessary because the stored parent will point to the
11461 * session machine otherwise and cause crashes or errors later
11462 * when the session machine gets invalid.
11463 */
11464 /** @todo Change the MediumAttachment class to behave like any other
11465 * class in this regard by creating peer MediumAttachment
11466 * objects for session machines and share the data with the peer
11467 * machine.
11468 */
11469 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11470 it != mMediaData->mAttachments.end();
11471 ++it)
11472 {
11473 (*it)->updateParentMachine(mPeer);
11474 }
11475
11476 /* attach new data to the primary machine and reshare it */
11477 mPeer->mMediaData.attach(mMediaData);
11478 }
11479
11480 return;
11481}
11482
11483/**
11484 * Perform deferred deletion of implicitly created diffs.
11485 *
11486 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11487 * backed up).
11488 *
11489 * @note Locks this object for writing!
11490 */
11491void Machine::rollbackMedia()
11492{
11493 AutoCaller autoCaller(this);
11494 AssertComRCReturnVoid(autoCaller.rc());
11495
11496 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11497 LogFlowThisFunc(("Entering rollbackMedia\n"));
11498
11499 HRESULT rc = S_OK;
11500
11501 /* no attach/detach operations -- nothing to do */
11502 if (!mMediaData.isBackedUp())
11503 return;
11504
11505 /* enumerate new attachments */
11506 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11507 it != mMediaData->mAttachments.end();
11508 ++it)
11509 {
11510 MediumAttachment *pAttach = *it;
11511 /* Fix up the backrefs for DVD/floppy media. */
11512 if (pAttach->getType() != DeviceType_HardDisk)
11513 {
11514 Medium* pMedium = pAttach->getMedium();
11515 if (pMedium)
11516 {
11517 rc = pMedium->removeBackReference(mData->mUuid);
11518 AssertComRC(rc);
11519 }
11520 }
11521
11522 (*it)->rollback();
11523
11524 pAttach = *it;
11525 /* Fix up the backrefs for DVD/floppy media. */
11526 if (pAttach->getType() != DeviceType_HardDisk)
11527 {
11528 Medium* pMedium = pAttach->getMedium();
11529 if (pMedium)
11530 {
11531 rc = pMedium->addBackReference(mData->mUuid);
11532 AssertComRC(rc);
11533 }
11534 }
11535 }
11536
11537 /** @todo convert all this Machine-based voodoo to MediumAttachment
11538 * based rollback logic. */
11539 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11540
11541 return;
11542}
11543
11544/**
11545 * Returns true if the settings file is located in the directory named exactly
11546 * as the machine; this means, among other things, that the machine directory
11547 * should be auto-renamed.
11548 *
11549 * @param aSettingsDir if not NULL, the full machine settings file directory
11550 * name will be assigned there.
11551 *
11552 * @note Doesn't lock anything.
11553 * @note Not thread safe (must be called from this object's lock).
11554 */
11555bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11556{
11557 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11558 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11559 if (aSettingsDir)
11560 *aSettingsDir = strMachineDirName;
11561 strMachineDirName.stripPath(); // vmname
11562 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11563 strConfigFileOnly.stripPath() // vmname.vbox
11564 .stripExt(); // vmname
11565 /** @todo hack, make somehow use of ComposeMachineFilename */
11566 if (mUserData->s.fDirectoryIncludesUUID)
11567 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11568
11569 AssertReturn(!strMachineDirName.isEmpty(), false);
11570 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11571
11572 return strMachineDirName == strConfigFileOnly;
11573}
11574
11575/**
11576 * Discards all changes to machine settings.
11577 *
11578 * @param aNotify Whether to notify the direct session about changes or not.
11579 *
11580 * @note Locks objects for writing!
11581 */
11582void Machine::rollback(bool aNotify)
11583{
11584 AutoCaller autoCaller(this);
11585 AssertComRCReturn(autoCaller.rc(), (void)0);
11586
11587 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11588
11589 if (!mStorageControllers.isNull())
11590 {
11591 if (mStorageControllers.isBackedUp())
11592 {
11593 /* unitialize all new devices (absent in the backed up list). */
11594 StorageControllerList::const_iterator it = mStorageControllers->begin();
11595 StorageControllerList *backedList = mStorageControllers.backedUpData();
11596 while (it != mStorageControllers->end())
11597 {
11598 if ( std::find(backedList->begin(), backedList->end(), *it)
11599 == backedList->end()
11600 )
11601 {
11602 (*it)->uninit();
11603 }
11604 ++it;
11605 }
11606
11607 /* restore the list */
11608 mStorageControllers.rollback();
11609 }
11610
11611 /* rollback any changes to devices after restoring the list */
11612 if (mData->flModifications & IsModified_Storage)
11613 {
11614 StorageControllerList::const_iterator it = mStorageControllers->begin();
11615 while (it != mStorageControllers->end())
11616 {
11617 (*it)->rollback();
11618 ++it;
11619 }
11620 }
11621 }
11622
11623 mUserData.rollback();
11624
11625 mHWData.rollback();
11626
11627 if (mData->flModifications & IsModified_Storage)
11628 rollbackMedia();
11629
11630 if (mBIOSSettings)
11631 mBIOSSettings->rollback();
11632
11633 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11634 mVRDEServer->rollback();
11635
11636 if (mAudioAdapter)
11637 mAudioAdapter->rollback();
11638
11639 if (mUSBController && (mData->flModifications & IsModified_USB))
11640 mUSBController->rollback();
11641
11642 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11643 mBandwidthControl->rollback();
11644
11645 if (!mHWData.isNull())
11646 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11647 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11648 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11649 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11650
11651 if (mData->flModifications & IsModified_NetworkAdapters)
11652 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11653 if ( mNetworkAdapters[slot]
11654 && mNetworkAdapters[slot]->isModified())
11655 {
11656 mNetworkAdapters[slot]->rollback();
11657 networkAdapters[slot] = mNetworkAdapters[slot];
11658 }
11659
11660 if (mData->flModifications & IsModified_SerialPorts)
11661 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11662 if ( mSerialPorts[slot]
11663 && mSerialPorts[slot]->isModified())
11664 {
11665 mSerialPorts[slot]->rollback();
11666 serialPorts[slot] = mSerialPorts[slot];
11667 }
11668
11669 if (mData->flModifications & IsModified_ParallelPorts)
11670 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11671 if ( mParallelPorts[slot]
11672 && mParallelPorts[slot]->isModified())
11673 {
11674 mParallelPorts[slot]->rollback();
11675 parallelPorts[slot] = mParallelPorts[slot];
11676 }
11677
11678 if (aNotify)
11679 {
11680 /* inform the direct session about changes */
11681
11682 ComObjPtr<Machine> that = this;
11683 uint32_t flModifications = mData->flModifications;
11684 alock.release();
11685
11686 if (flModifications & IsModified_SharedFolders)
11687 that->onSharedFolderChange();
11688
11689 if (flModifications & IsModified_VRDEServer)
11690 that->onVRDEServerChange(/* aRestart */ TRUE);
11691 if (flModifications & IsModified_USB)
11692 that->onUSBControllerChange();
11693
11694 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11695 if (networkAdapters[slot])
11696 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11697 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11698 if (serialPorts[slot])
11699 that->onSerialPortChange(serialPorts[slot]);
11700 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11701 if (parallelPorts[slot])
11702 that->onParallelPortChange(parallelPorts[slot]);
11703
11704 if (flModifications & IsModified_Storage)
11705 that->onStorageControllerChange();
11706
11707#if 0
11708 if (flModifications & IsModified_BandwidthControl)
11709 that->onBandwidthControlChange();
11710#endif
11711 }
11712}
11713
11714/**
11715 * Commits all the changes to machine settings.
11716 *
11717 * Note that this operation is supposed to never fail.
11718 *
11719 * @note Locks this object and children for writing.
11720 */
11721void Machine::commit()
11722{
11723 AutoCaller autoCaller(this);
11724 AssertComRCReturnVoid(autoCaller.rc());
11725
11726 AutoCaller peerCaller(mPeer);
11727 AssertComRCReturnVoid(peerCaller.rc());
11728
11729 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11730
11731 /*
11732 * use safe commit to ensure Snapshot machines (that share mUserData)
11733 * will still refer to a valid memory location
11734 */
11735 mUserData.commitCopy();
11736
11737 mHWData.commit();
11738
11739 if (mMediaData.isBackedUp())
11740 commitMedia(Global::IsOnline(mData->mMachineState));
11741
11742 mBIOSSettings->commit();
11743 mVRDEServer->commit();
11744 mAudioAdapter->commit();
11745 mUSBController->commit();
11746 mBandwidthControl->commit();
11747
11748 /* Since mNetworkAdapters is a list which might have been changed (resized)
11749 * without using the Backupable<> template we need to handle the copying
11750 * of the list entries manually, including the creation of peers for the
11751 * new objects. */
11752 bool commitNetworkAdapters = false;
11753 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11754 if (mPeer)
11755 {
11756 /* commit everything, even the ones which will go away */
11757 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11758 mNetworkAdapters[slot]->commit();
11759 /* copy over the new entries, creating a peer and uninit the original */
11760 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11761 for (size_t slot = 0; slot < newSize; slot++)
11762 {
11763 /* look if this adapter has a peer device */
11764 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11765 if (!peer)
11766 {
11767 /* no peer means the adapter is a newly created one;
11768 * create a peer owning data this data share it with */
11769 peer.createObject();
11770 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11771 }
11772 mPeer->mNetworkAdapters[slot] = peer;
11773 }
11774 /* uninit any no longer needed network adapters */
11775 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11776 mNetworkAdapters[slot]->uninit();
11777 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11778 {
11779 if (mPeer->mNetworkAdapters[slot])
11780 mPeer->mNetworkAdapters[slot]->uninit();
11781 }
11782 /* Keep the original network adapter count until this point, so that
11783 * discarding a chipset type change will not lose settings. */
11784 mNetworkAdapters.resize(newSize);
11785 mPeer->mNetworkAdapters.resize(newSize);
11786 }
11787 else
11788 {
11789 /* we have no peer (our parent is the newly created machine);
11790 * just commit changes to the network adapters */
11791 commitNetworkAdapters = true;
11792 }
11793 if (commitNetworkAdapters)
11794 {
11795 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11796 mNetworkAdapters[slot]->commit();
11797 }
11798
11799 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11800 mSerialPorts[slot]->commit();
11801 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11802 mParallelPorts[slot]->commit();
11803
11804 bool commitStorageControllers = false;
11805
11806 if (mStorageControllers.isBackedUp())
11807 {
11808 mStorageControllers.commit();
11809
11810 if (mPeer)
11811 {
11812 /* Commit all changes to new controllers (this will reshare data with
11813 * peers for those who have peers) */
11814 StorageControllerList *newList = new StorageControllerList();
11815 StorageControllerList::const_iterator it = mStorageControllers->begin();
11816 while (it != mStorageControllers->end())
11817 {
11818 (*it)->commit();
11819
11820 /* look if this controller has a peer device */
11821 ComObjPtr<StorageController> peer = (*it)->getPeer();
11822 if (!peer)
11823 {
11824 /* no peer means the device is a newly created one;
11825 * create a peer owning data this device share it with */
11826 peer.createObject();
11827 peer->init(mPeer, *it, true /* aReshare */);
11828 }
11829 else
11830 {
11831 /* remove peer from the old list */
11832 mPeer->mStorageControllers->remove(peer);
11833 }
11834 /* and add it to the new list */
11835 newList->push_back(peer);
11836
11837 ++it;
11838 }
11839
11840 /* uninit old peer's controllers that are left */
11841 it = mPeer->mStorageControllers->begin();
11842 while (it != mPeer->mStorageControllers->end())
11843 {
11844 (*it)->uninit();
11845 ++it;
11846 }
11847
11848 /* attach new list of controllers to our peer */
11849 mPeer->mStorageControllers.attach(newList);
11850 }
11851 else
11852 {
11853 /* we have no peer (our parent is the newly created machine);
11854 * just commit changes to devices */
11855 commitStorageControllers = true;
11856 }
11857 }
11858 else
11859 {
11860 /* the list of controllers itself is not changed,
11861 * just commit changes to controllers themselves */
11862 commitStorageControllers = true;
11863 }
11864
11865 if (commitStorageControllers)
11866 {
11867 StorageControllerList::const_iterator it = mStorageControllers->begin();
11868 while (it != mStorageControllers->end())
11869 {
11870 (*it)->commit();
11871 ++it;
11872 }
11873 }
11874
11875 if (isSessionMachine())
11876 {
11877 /* attach new data to the primary machine and reshare it */
11878 mPeer->mUserData.attach(mUserData);
11879 mPeer->mHWData.attach(mHWData);
11880 /* mMediaData is reshared by fixupMedia */
11881 // mPeer->mMediaData.attach(mMediaData);
11882 Assert(mPeer->mMediaData.data() == mMediaData.data());
11883 }
11884}
11885
11886/**
11887 * Copies all the hardware data from the given machine.
11888 *
11889 * Currently, only called when the VM is being restored from a snapshot. In
11890 * particular, this implies that the VM is not running during this method's
11891 * call.
11892 *
11893 * @note This method must be called from under this object's lock.
11894 *
11895 * @note This method doesn't call #commit(), so all data remains backed up and
11896 * unsaved.
11897 */
11898void Machine::copyFrom(Machine *aThat)
11899{
11900 AssertReturnVoid(!isSnapshotMachine());
11901 AssertReturnVoid(aThat->isSnapshotMachine());
11902
11903 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11904
11905 mHWData.assignCopy(aThat->mHWData);
11906
11907 // create copies of all shared folders (mHWData after attaching a copy
11908 // contains just references to original objects)
11909 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11910 it != mHWData->mSharedFolders.end();
11911 ++it)
11912 {
11913 ComObjPtr<SharedFolder> folder;
11914 folder.createObject();
11915 HRESULT rc = folder->initCopy(getMachine(), *it);
11916 AssertComRC(rc);
11917 *it = folder;
11918 }
11919
11920 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11921 mVRDEServer->copyFrom(aThat->mVRDEServer);
11922 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11923 mUSBController->copyFrom(aThat->mUSBController);
11924 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11925
11926 /* create private copies of all controllers */
11927 mStorageControllers.backup();
11928 mStorageControllers->clear();
11929 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11930 it != aThat->mStorageControllers->end();
11931 ++it)
11932 {
11933 ComObjPtr<StorageController> ctrl;
11934 ctrl.createObject();
11935 ctrl->initCopy(this, *it);
11936 mStorageControllers->push_back(ctrl);
11937 }
11938
11939 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11940 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11941 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11942 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11943 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11944 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11945 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11946}
11947
11948/**
11949 * Returns whether the given storage controller is hotplug capable.
11950 *
11951 * @returns true if the controller supports hotplugging
11952 * false otherwise.
11953 * @param enmCtrlType The controller type to check for.
11954 */
11955bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11956{
11957 switch (enmCtrlType)
11958 {
11959 case StorageControllerType_IntelAhci:
11960 return true;
11961 case StorageControllerType_LsiLogic:
11962 case StorageControllerType_LsiLogicSas:
11963 case StorageControllerType_BusLogic:
11964 case StorageControllerType_PIIX3:
11965 case StorageControllerType_PIIX4:
11966 case StorageControllerType_ICH6:
11967 case StorageControllerType_I82078:
11968 default:
11969 return false;
11970 }
11971}
11972
11973#ifdef VBOX_WITH_RESOURCE_USAGE_API
11974
11975void Machine::getDiskList(MediaList &list)
11976{
11977 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11978 it != mMediaData->mAttachments.end();
11979 ++it)
11980 {
11981 MediumAttachment* pAttach = *it;
11982 /* just in case */
11983 AssertStmt(pAttach, continue);
11984
11985 AutoCaller localAutoCallerA(pAttach);
11986 if (FAILED(localAutoCallerA.rc())) continue;
11987
11988 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11989
11990 if (pAttach->getType() == DeviceType_HardDisk)
11991 list.push_back(pAttach->getMedium());
11992 }
11993}
11994
11995void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11996{
11997 AssertReturnVoid(isWriteLockOnCurrentThread());
11998 AssertPtrReturnVoid(aCollector);
11999
12000 pm::CollectorHAL *hal = aCollector->getHAL();
12001 /* Create sub metrics */
12002 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12003 "Percentage of processor time spent in user mode by the VM process.");
12004 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12005 "Percentage of processor time spent in kernel mode by the VM process.");
12006 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12007 "Size of resident portion of VM process in memory.");
12008 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12009 "Actual size of all VM disks combined.");
12010 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12011 "Network receive rate.");
12012 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12013 "Network transmit rate.");
12014 /* Create and register base metrics */
12015 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12016 cpuLoadUser, cpuLoadKernel);
12017 aCollector->registerBaseMetric(cpuLoad);
12018 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12019 ramUsageUsed);
12020 aCollector->registerBaseMetric(ramUsage);
12021 MediaList disks;
12022 getDiskList(disks);
12023 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12024 diskUsageUsed);
12025 aCollector->registerBaseMetric(diskUsage);
12026
12027 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12028 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12029 new pm::AggregateAvg()));
12030 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12031 new pm::AggregateMin()));
12032 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12033 new pm::AggregateMax()));
12034 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12035 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12036 new pm::AggregateAvg()));
12037 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12038 new pm::AggregateMin()));
12039 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12040 new pm::AggregateMax()));
12041
12042 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12043 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12044 new pm::AggregateAvg()));
12045 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12046 new pm::AggregateMin()));
12047 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12048 new pm::AggregateMax()));
12049
12050 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12051 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12052 new pm::AggregateAvg()));
12053 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12054 new pm::AggregateMin()));
12055 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12056 new pm::AggregateMax()));
12057
12058
12059 /* Guest metrics collector */
12060 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12061 aCollector->registerGuest(mCollectorGuest);
12062 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12063 this, __PRETTY_FUNCTION__, mCollectorGuest));
12064
12065 /* Create sub metrics */
12066 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12067 "Percentage of processor time spent in user mode as seen by the guest.");
12068 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12069 "Percentage of processor time spent in kernel mode as seen by the guest.");
12070 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12071 "Percentage of processor time spent idling as seen by the guest.");
12072
12073 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12074 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12075 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12076 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12077 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12078 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12079
12080 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12081
12082 /* Create and register base metrics */
12083 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12084 machineNetRx, machineNetTx);
12085 aCollector->registerBaseMetric(machineNetRate);
12086
12087 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12088 guestLoadUser, guestLoadKernel, guestLoadIdle);
12089 aCollector->registerBaseMetric(guestCpuLoad);
12090
12091 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12092 guestMemTotal, guestMemFree,
12093 guestMemBalloon, guestMemShared,
12094 guestMemCache, guestPagedTotal);
12095 aCollector->registerBaseMetric(guestCpuMem);
12096
12097 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12098 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12099 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12100 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12101
12102 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12103 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12104 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12105 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12106
12107 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12108 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12109 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12110 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12111
12112 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12113 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12114 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12115 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12116
12117 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12118 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12119 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12120 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12121
12122 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12123 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12124 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12125 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12126
12127 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12128 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12129 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12130 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12131
12132 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12133 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12134 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12135 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12136
12137 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12138 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12139 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12140 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12141
12142 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12143 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12144 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12145 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12146
12147 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12148 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12149 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12150 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12151}
12152
12153void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12154{
12155 AssertReturnVoid(isWriteLockOnCurrentThread());
12156
12157 if (aCollector)
12158 {
12159 aCollector->unregisterMetricsFor(aMachine);
12160 aCollector->unregisterBaseMetricsFor(aMachine);
12161 }
12162}
12163
12164#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12165
12166
12167////////////////////////////////////////////////////////////////////////////////
12168
12169DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12170
12171HRESULT SessionMachine::FinalConstruct()
12172{
12173 LogFlowThisFunc(("\n"));
12174
12175#if defined(RT_OS_WINDOWS)
12176 mIPCSem = NULL;
12177#elif defined(RT_OS_OS2)
12178 mIPCSem = NULLHANDLE;
12179#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12180 mIPCSem = -1;
12181#else
12182# error "Port me!"
12183#endif
12184
12185 return BaseFinalConstruct();
12186}
12187
12188void SessionMachine::FinalRelease()
12189{
12190 LogFlowThisFunc(("\n"));
12191
12192 uninit(Uninit::Unexpected);
12193
12194 BaseFinalRelease();
12195}
12196
12197/**
12198 * @note Must be called only by Machine::openSession() from its own write lock.
12199 */
12200HRESULT SessionMachine::init(Machine *aMachine)
12201{
12202 LogFlowThisFuncEnter();
12203 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12204
12205 AssertReturn(aMachine, E_INVALIDARG);
12206
12207 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12208
12209 /* Enclose the state transition NotReady->InInit->Ready */
12210 AutoInitSpan autoInitSpan(this);
12211 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12212
12213 /* create the interprocess semaphore */
12214#if defined(RT_OS_WINDOWS)
12215 mIPCSemName = aMachine->mData->m_strConfigFileFull;
12216 for (size_t i = 0; i < mIPCSemName.length(); i++)
12217 if (mIPCSemName.raw()[i] == '\\')
12218 mIPCSemName.raw()[i] = '/';
12219 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12220 ComAssertMsgRet(mIPCSem,
12221 ("Cannot create IPC mutex '%ls', err=%d",
12222 mIPCSemName.raw(), ::GetLastError()),
12223 E_FAIL);
12224#elif defined(RT_OS_OS2)
12225 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12226 aMachine->mData->mUuid.raw());
12227 mIPCSemName = ipcSem;
12228 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12229 ComAssertMsgRet(arc == NO_ERROR,
12230 ("Cannot create IPC mutex '%s', arc=%ld",
12231 ipcSem.c_str(), arc),
12232 E_FAIL);
12233#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12234# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12235# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12236 /** @todo Check that this still works correctly. */
12237 AssertCompileSize(key_t, 8);
12238# else
12239 AssertCompileSize(key_t, 4);
12240# endif
12241 key_t key;
12242 mIPCSem = -1;
12243 mIPCKey = "0";
12244 for (uint32_t i = 0; i < 1 << 24; i++)
12245 {
12246 key = ((uint32_t)'V' << 24) | i;
12247 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12248 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12249 {
12250 mIPCSem = sem;
12251 if (sem >= 0)
12252 mIPCKey = BstrFmt("%u", key);
12253 break;
12254 }
12255 }
12256# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12257 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12258 char *pszSemName = NULL;
12259 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12260 key_t key = ::ftok(pszSemName, 'V');
12261 RTStrFree(pszSemName);
12262
12263 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12264# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12265
12266 int errnoSave = errno;
12267 if (mIPCSem < 0 && errnoSave == ENOSYS)
12268 {
12269 setError(E_FAIL,
12270 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12271 "support for SysV IPC. Check the host kernel configuration for "
12272 "CONFIG_SYSVIPC=y"));
12273 return E_FAIL;
12274 }
12275 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12276 * the IPC semaphores */
12277 if (mIPCSem < 0 && errnoSave == ENOSPC)
12278 {
12279#ifdef RT_OS_LINUX
12280 setError(E_FAIL,
12281 tr("Cannot create IPC semaphore because the system limit for the "
12282 "maximum number of semaphore sets (SEMMNI), or the system wide "
12283 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12284 "current set of SysV IPC semaphores can be determined from "
12285 "the file /proc/sysvipc/sem"));
12286#else
12287 setError(E_FAIL,
12288 tr("Cannot create IPC semaphore because the system-imposed limit "
12289 "on the maximum number of allowed semaphores or semaphore "
12290 "identifiers system-wide would be exceeded"));
12291#endif
12292 return E_FAIL;
12293 }
12294 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12295 E_FAIL);
12296 /* set the initial value to 1 */
12297 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12298 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12299 E_FAIL);
12300#else
12301# error "Port me!"
12302#endif
12303
12304 /* memorize the peer Machine */
12305 unconst(mPeer) = aMachine;
12306 /* share the parent pointer */
12307 unconst(mParent) = aMachine->mParent;
12308
12309 /* take the pointers to data to share */
12310 mData.share(aMachine->mData);
12311 mSSData.share(aMachine->mSSData);
12312
12313 mUserData.share(aMachine->mUserData);
12314 mHWData.share(aMachine->mHWData);
12315 mMediaData.share(aMachine->mMediaData);
12316
12317 mStorageControllers.allocate();
12318 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12319 it != aMachine->mStorageControllers->end();
12320 ++it)
12321 {
12322 ComObjPtr<StorageController> ctl;
12323 ctl.createObject();
12324 ctl->init(this, *it);
12325 mStorageControllers->push_back(ctl);
12326 }
12327
12328 unconst(mBIOSSettings).createObject();
12329 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12330 /* create another VRDEServer object that will be mutable */
12331 unconst(mVRDEServer).createObject();
12332 mVRDEServer->init(this, aMachine->mVRDEServer);
12333 /* create another audio adapter object that will be mutable */
12334 unconst(mAudioAdapter).createObject();
12335 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12336 /* create a list of serial ports that will be mutable */
12337 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12338 {
12339 unconst(mSerialPorts[slot]).createObject();
12340 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12341 }
12342 /* create a list of parallel ports that will be mutable */
12343 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12344 {
12345 unconst(mParallelPorts[slot]).createObject();
12346 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12347 }
12348 /* create another USB controller object that will be mutable */
12349 unconst(mUSBController).createObject();
12350 mUSBController->init(this, aMachine->mUSBController);
12351
12352 /* create a list of network adapters that will be mutable */
12353 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12354 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12355 {
12356 unconst(mNetworkAdapters[slot]).createObject();
12357 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12358 }
12359
12360 /* create another bandwidth control object that will be mutable */
12361 unconst(mBandwidthControl).createObject();
12362 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12363
12364 /* default is to delete saved state on Saved -> PoweredOff transition */
12365 mRemoveSavedState = true;
12366
12367 /* Confirm a successful initialization when it's the case */
12368 autoInitSpan.setSucceeded();
12369
12370 LogFlowThisFuncLeave();
12371 return S_OK;
12372}
12373
12374/**
12375 * Uninitializes this session object. If the reason is other than
12376 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12377 *
12378 * @param aReason uninitialization reason
12379 *
12380 * @note Locks mParent + this object for writing.
12381 */
12382void SessionMachine::uninit(Uninit::Reason aReason)
12383{
12384 LogFlowThisFuncEnter();
12385 LogFlowThisFunc(("reason=%d\n", aReason));
12386
12387 /*
12388 * Strongly reference ourselves to prevent this object deletion after
12389 * mData->mSession.mMachine.setNull() below (which can release the last
12390 * reference and call the destructor). Important: this must be done before
12391 * accessing any members (and before AutoUninitSpan that does it as well).
12392 * This self reference will be released as the very last step on return.
12393 */
12394 ComObjPtr<SessionMachine> selfRef = this;
12395
12396 /* Enclose the state transition Ready->InUninit->NotReady */
12397 AutoUninitSpan autoUninitSpan(this);
12398 if (autoUninitSpan.uninitDone())
12399 {
12400 LogFlowThisFunc(("Already uninitialized\n"));
12401 LogFlowThisFuncLeave();
12402 return;
12403 }
12404
12405 if (autoUninitSpan.initFailed())
12406 {
12407 /* We've been called by init() because it's failed. It's not really
12408 * necessary (nor it's safe) to perform the regular uninit sequence
12409 * below, the following is enough.
12410 */
12411 LogFlowThisFunc(("Initialization failed.\n"));
12412#if defined(RT_OS_WINDOWS)
12413 if (mIPCSem)
12414 ::CloseHandle(mIPCSem);
12415 mIPCSem = NULL;
12416#elif defined(RT_OS_OS2)
12417 if (mIPCSem != NULLHANDLE)
12418 ::DosCloseMutexSem(mIPCSem);
12419 mIPCSem = NULLHANDLE;
12420#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12421 if (mIPCSem >= 0)
12422 ::semctl(mIPCSem, 0, IPC_RMID);
12423 mIPCSem = -1;
12424# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12425 mIPCKey = "0";
12426# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12427#else
12428# error "Port me!"
12429#endif
12430 uninitDataAndChildObjects();
12431 mData.free();
12432 unconst(mParent) = NULL;
12433 unconst(mPeer) = NULL;
12434 LogFlowThisFuncLeave();
12435 return;
12436 }
12437
12438 MachineState_T lastState;
12439 {
12440 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12441 lastState = mData->mMachineState;
12442 }
12443 NOREF(lastState);
12444
12445#ifdef VBOX_WITH_USB
12446 // release all captured USB devices, but do this before requesting the locks below
12447 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12448 {
12449 /* Console::captureUSBDevices() is called in the VM process only after
12450 * setting the machine state to Starting or Restoring.
12451 * Console::detachAllUSBDevices() will be called upon successful
12452 * termination. So, we need to release USB devices only if there was
12453 * an abnormal termination of a running VM.
12454 *
12455 * This is identical to SessionMachine::DetachAllUSBDevices except
12456 * for the aAbnormal argument. */
12457 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12458 AssertComRC(rc);
12459 NOREF(rc);
12460
12461 USBProxyService *service = mParent->host()->usbProxyService();
12462 if (service)
12463 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12464 }
12465#endif /* VBOX_WITH_USB */
12466
12467 // we need to lock this object in uninit() because the lock is shared
12468 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12469 // and others need mParent lock, and USB needs host lock.
12470 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12471
12472#if 0
12473 // Trigger async cleanup tasks, avoid doing things here which are not
12474 // vital to be done immediately and maybe need more locks. This calls
12475 // Machine::unregisterMetrics().
12476 mParent->onMachineUninit(mPeer);
12477#else
12478 /*
12479 * It is safe to call Machine::unregisterMetrics() here because
12480 * PerformanceCollector::samplerCallback no longer accesses guest methods
12481 * holding the lock.
12482 */
12483 unregisterMetrics(mParent->performanceCollector(), mPeer);
12484#endif
12485 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12486 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12487 this, __PRETTY_FUNCTION__, mCollectorGuest));
12488 if (mCollectorGuest)
12489 {
12490 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12491 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12492 mCollectorGuest = NULL;
12493 }
12494
12495 if (aReason == Uninit::Abnormal)
12496 {
12497 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12498 Global::IsOnlineOrTransient(lastState)));
12499
12500 /* reset the state to Aborted */
12501 if (mData->mMachineState != MachineState_Aborted)
12502 setMachineState(MachineState_Aborted);
12503 }
12504
12505 // any machine settings modified?
12506 if (mData->flModifications)
12507 {
12508 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12509 rollback(false /* aNotify */);
12510 }
12511
12512 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12513 || !mConsoleTaskData.mSnapshot);
12514 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12515 {
12516 LogWarningThisFunc(("canceling failed save state request!\n"));
12517 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12518 }
12519 else if (!mConsoleTaskData.mSnapshot.isNull())
12520 {
12521 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12522
12523 /* delete all differencing hard disks created (this will also attach
12524 * their parents back by rolling back mMediaData) */
12525 rollbackMedia();
12526
12527 // delete the saved state file (it might have been already created)
12528 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12529 // think it's still in use
12530 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12531 mConsoleTaskData.mSnapshot->uninit();
12532 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12533 }
12534
12535 if (!mData->mSession.mType.isEmpty())
12536 {
12537 /* mType is not null when this machine's process has been started by
12538 * Machine::LaunchVMProcess(), therefore it is our child. We
12539 * need to queue the PID to reap the process (and avoid zombies on
12540 * Linux). */
12541 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12542 mParent->addProcessToReap(mData->mSession.mPID);
12543 }
12544
12545 mData->mSession.mPID = NIL_RTPROCESS;
12546
12547 if (aReason == Uninit::Unexpected)
12548 {
12549 /* Uninitialization didn't come from #checkForDeath(), so tell the
12550 * client watcher thread to update the set of machines that have open
12551 * sessions. */
12552 mParent->updateClientWatcher();
12553 }
12554
12555 /* uninitialize all remote controls */
12556 if (mData->mSession.mRemoteControls.size())
12557 {
12558 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12559 mData->mSession.mRemoteControls.size()));
12560
12561 Data::Session::RemoteControlList::iterator it =
12562 mData->mSession.mRemoteControls.begin();
12563 while (it != mData->mSession.mRemoteControls.end())
12564 {
12565 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12566 HRESULT rc = (*it)->Uninitialize();
12567 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12568 if (FAILED(rc))
12569 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12570 ++it;
12571 }
12572 mData->mSession.mRemoteControls.clear();
12573 }
12574
12575 /*
12576 * An expected uninitialization can come only from #checkForDeath().
12577 * Otherwise it means that something's gone really wrong (for example,
12578 * the Session implementation has released the VirtualBox reference
12579 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12580 * etc). However, it's also possible, that the client releases the IPC
12581 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12582 * but the VirtualBox release event comes first to the server process.
12583 * This case is practically possible, so we should not assert on an
12584 * unexpected uninit, just log a warning.
12585 */
12586
12587 if ((aReason == Uninit::Unexpected))
12588 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12589
12590 if (aReason != Uninit::Normal)
12591 {
12592 mData->mSession.mDirectControl.setNull();
12593 }
12594 else
12595 {
12596 /* this must be null here (see #OnSessionEnd()) */
12597 Assert(mData->mSession.mDirectControl.isNull());
12598 Assert(mData->mSession.mState == SessionState_Unlocking);
12599 Assert(!mData->mSession.mProgress.isNull());
12600 }
12601 if (mData->mSession.mProgress)
12602 {
12603 if (aReason == Uninit::Normal)
12604 mData->mSession.mProgress->notifyComplete(S_OK);
12605 else
12606 mData->mSession.mProgress->notifyComplete(E_FAIL,
12607 COM_IIDOF(ISession),
12608 getComponentName(),
12609 tr("The VM session was aborted"));
12610 mData->mSession.mProgress.setNull();
12611 }
12612
12613 /* remove the association between the peer machine and this session machine */
12614 Assert( (SessionMachine*)mData->mSession.mMachine == this
12615 || aReason == Uninit::Unexpected);
12616
12617 /* reset the rest of session data */
12618 mData->mSession.mMachine.setNull();
12619 mData->mSession.mState = SessionState_Unlocked;
12620 mData->mSession.mType.setNull();
12621
12622 /* close the interprocess semaphore before leaving the exclusive lock */
12623#if defined(RT_OS_WINDOWS)
12624 if (mIPCSem)
12625 ::CloseHandle(mIPCSem);
12626 mIPCSem = NULL;
12627#elif defined(RT_OS_OS2)
12628 if (mIPCSem != NULLHANDLE)
12629 ::DosCloseMutexSem(mIPCSem);
12630 mIPCSem = NULLHANDLE;
12631#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12632 if (mIPCSem >= 0)
12633 ::semctl(mIPCSem, 0, IPC_RMID);
12634 mIPCSem = -1;
12635# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12636 mIPCKey = "0";
12637# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12638#else
12639# error "Port me!"
12640#endif
12641
12642 /* fire an event */
12643 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12644
12645 uninitDataAndChildObjects();
12646
12647 /* free the essential data structure last */
12648 mData.free();
12649
12650 /* release the exclusive lock before setting the below two to NULL */
12651 multilock.release();
12652
12653 unconst(mParent) = NULL;
12654 unconst(mPeer) = NULL;
12655
12656 LogFlowThisFuncLeave();
12657}
12658
12659// util::Lockable interface
12660////////////////////////////////////////////////////////////////////////////////
12661
12662/**
12663 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12664 * with the primary Machine instance (mPeer).
12665 */
12666RWLockHandle *SessionMachine::lockHandle() const
12667{
12668 AssertReturn(mPeer != NULL, NULL);
12669 return mPeer->lockHandle();
12670}
12671
12672// IInternalMachineControl methods
12673////////////////////////////////////////////////////////////////////////////////
12674
12675/**
12676 * Passes collected guest statistics to performance collector object
12677 */
12678STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12679 ULONG aCpuKernel, ULONG aCpuIdle,
12680 ULONG aMemTotal, ULONG aMemFree,
12681 ULONG aMemBalloon, ULONG aMemShared,
12682 ULONG aMemCache, ULONG aPageTotal,
12683 ULONG aAllocVMM, ULONG aFreeVMM,
12684 ULONG aBalloonedVMM, ULONG aSharedVMM,
12685 ULONG aVmNetRx, ULONG aVmNetTx)
12686{
12687 if (mCollectorGuest)
12688 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12689 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12690 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12691 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12692
12693 return S_OK;
12694}
12695
12696/**
12697 * @note Locks this object for writing.
12698 */
12699STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12700{
12701 AutoCaller autoCaller(this);
12702 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12703
12704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12705
12706 mRemoveSavedState = aRemove;
12707
12708 return S_OK;
12709}
12710
12711/**
12712 * @note Locks the same as #setMachineState() does.
12713 */
12714STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12715{
12716 return setMachineState(aMachineState);
12717}
12718
12719/**
12720 * @note Locks this object for reading.
12721 */
12722STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12723{
12724 AutoCaller autoCaller(this);
12725 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12726
12727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12728
12729#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12730 mIPCSemName.cloneTo(aId);
12731 return S_OK;
12732#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12733# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12734 mIPCKey.cloneTo(aId);
12735# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12736 mData->m_strConfigFileFull.cloneTo(aId);
12737# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12738 return S_OK;
12739#else
12740# error "Port me!"
12741#endif
12742}
12743
12744/**
12745 * @note Locks this object for writing.
12746 */
12747STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12748{
12749 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12750 AutoCaller autoCaller(this);
12751 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12752
12753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12754
12755 if (mData->mSession.mState != SessionState_Locked)
12756 return VBOX_E_INVALID_OBJECT_STATE;
12757
12758 if (!mData->mSession.mProgress.isNull())
12759 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12760
12761 LogFlowThisFunc(("returns S_OK.\n"));
12762 return S_OK;
12763}
12764
12765/**
12766 * @note Locks this object for writing.
12767 */
12768STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12769{
12770 AutoCaller autoCaller(this);
12771 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12772
12773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12774
12775 if (mData->mSession.mState != SessionState_Locked)
12776 return VBOX_E_INVALID_OBJECT_STATE;
12777
12778 /* Finalize the LaunchVMProcess progress object. */
12779 if (mData->mSession.mProgress)
12780 {
12781 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12782 mData->mSession.mProgress.setNull();
12783 }
12784
12785 if (SUCCEEDED((HRESULT)iResult))
12786 {
12787#ifdef VBOX_WITH_RESOURCE_USAGE_API
12788 /* The VM has been powered up successfully, so it makes sense
12789 * now to offer the performance metrics for a running machine
12790 * object. Doing it earlier wouldn't be safe. */
12791 registerMetrics(mParent->performanceCollector(), mPeer,
12792 mData->mSession.mPID);
12793#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12794 }
12795
12796 return S_OK;
12797}
12798
12799/**
12800 * @note Locks this object for writing.
12801 */
12802STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12803{
12804 LogFlowThisFuncEnter();
12805
12806 CheckComArgOutPointerValid(aProgress);
12807
12808 AutoCaller autoCaller(this);
12809 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12810
12811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12812
12813 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12814 E_FAIL);
12815
12816 /* create a progress object to track operation completion */
12817 ComObjPtr<Progress> pProgress;
12818 pProgress.createObject();
12819 pProgress->init(getVirtualBox(),
12820 static_cast<IMachine *>(this) /* aInitiator */,
12821 Bstr(tr("Stopping the virtual machine")).raw(),
12822 FALSE /* aCancelable */);
12823
12824 /* fill in the console task data */
12825 mConsoleTaskData.mLastState = mData->mMachineState;
12826 mConsoleTaskData.mProgress = pProgress;
12827
12828 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12829 setMachineState(MachineState_Stopping);
12830
12831 pProgress.queryInterfaceTo(aProgress);
12832
12833 return S_OK;
12834}
12835
12836/**
12837 * @note Locks this object for writing.
12838 */
12839STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12840{
12841 LogFlowThisFuncEnter();
12842
12843 AutoCaller autoCaller(this);
12844 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12845
12846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12847
12848 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12849 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12850 && mConsoleTaskData.mLastState != MachineState_Null,
12851 E_FAIL);
12852
12853 /*
12854 * On failure, set the state to the state we had when BeginPoweringDown()
12855 * was called (this is expected by Console::PowerDown() and the associated
12856 * task). On success the VM process already changed the state to
12857 * MachineState_PoweredOff, so no need to do anything.
12858 */
12859 if (FAILED(iResult))
12860 setMachineState(mConsoleTaskData.mLastState);
12861
12862 /* notify the progress object about operation completion */
12863 Assert(mConsoleTaskData.mProgress);
12864 if (SUCCEEDED(iResult))
12865 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12866 else
12867 {
12868 Utf8Str strErrMsg(aErrMsg);
12869 if (strErrMsg.length())
12870 mConsoleTaskData.mProgress->notifyComplete(iResult,
12871 COM_IIDOF(ISession),
12872 getComponentName(),
12873 strErrMsg.c_str());
12874 else
12875 mConsoleTaskData.mProgress->notifyComplete(iResult);
12876 }
12877
12878 /* clear out the temporary saved state data */
12879 mConsoleTaskData.mLastState = MachineState_Null;
12880 mConsoleTaskData.mProgress.setNull();
12881
12882 LogFlowThisFuncLeave();
12883 return S_OK;
12884}
12885
12886
12887/**
12888 * Goes through the USB filters of the given machine to see if the given
12889 * device matches any filter or not.
12890 *
12891 * @note Locks the same as USBController::hasMatchingFilter() does.
12892 */
12893STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12894 BOOL *aMatched,
12895 ULONG *aMaskedIfs)
12896{
12897 LogFlowThisFunc(("\n"));
12898
12899 CheckComArgNotNull(aUSBDevice);
12900 CheckComArgOutPointerValid(aMatched);
12901
12902 AutoCaller autoCaller(this);
12903 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12904
12905#ifdef VBOX_WITH_USB
12906 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12907#else
12908 NOREF(aUSBDevice);
12909 NOREF(aMaskedIfs);
12910 *aMatched = FALSE;
12911#endif
12912
12913 return S_OK;
12914}
12915
12916/**
12917 * @note Locks the same as Host::captureUSBDevice() does.
12918 */
12919STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12920{
12921 LogFlowThisFunc(("\n"));
12922
12923 AutoCaller autoCaller(this);
12924 AssertComRCReturnRC(autoCaller.rc());
12925
12926#ifdef VBOX_WITH_USB
12927 /* if captureDeviceForVM() fails, it must have set extended error info */
12928 clearError();
12929 MultiResult rc = mParent->host()->checkUSBProxyService();
12930 if (FAILED(rc)) return rc;
12931
12932 USBProxyService *service = mParent->host()->usbProxyService();
12933 AssertReturn(service, E_FAIL);
12934 return service->captureDeviceForVM(this, Guid(aId).ref());
12935#else
12936 NOREF(aId);
12937 return E_NOTIMPL;
12938#endif
12939}
12940
12941/**
12942 * @note Locks the same as Host::detachUSBDevice() does.
12943 */
12944STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12945{
12946 LogFlowThisFunc(("\n"));
12947
12948 AutoCaller autoCaller(this);
12949 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12950
12951#ifdef VBOX_WITH_USB
12952 USBProxyService *service = mParent->host()->usbProxyService();
12953 AssertReturn(service, E_FAIL);
12954 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12955#else
12956 NOREF(aId);
12957 NOREF(aDone);
12958 return E_NOTIMPL;
12959#endif
12960}
12961
12962/**
12963 * Inserts all machine filters to the USB proxy service and then calls
12964 * Host::autoCaptureUSBDevices().
12965 *
12966 * Called by Console from the VM process upon VM startup.
12967 *
12968 * @note Locks what called methods lock.
12969 */
12970STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12971{
12972 LogFlowThisFunc(("\n"));
12973
12974 AutoCaller autoCaller(this);
12975 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12976
12977#ifdef VBOX_WITH_USB
12978 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12979 AssertComRC(rc);
12980 NOREF(rc);
12981
12982 USBProxyService *service = mParent->host()->usbProxyService();
12983 AssertReturn(service, E_FAIL);
12984 return service->autoCaptureDevicesForVM(this);
12985#else
12986 return S_OK;
12987#endif
12988}
12989
12990/**
12991 * Removes all machine filters from the USB proxy service and then calls
12992 * Host::detachAllUSBDevices().
12993 *
12994 * Called by Console from the VM process upon normal VM termination or by
12995 * SessionMachine::uninit() upon abnormal VM termination (from under the
12996 * Machine/SessionMachine lock).
12997 *
12998 * @note Locks what called methods lock.
12999 */
13000STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13001{
13002 LogFlowThisFunc(("\n"));
13003
13004 AutoCaller autoCaller(this);
13005 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13006
13007#ifdef VBOX_WITH_USB
13008 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
13009 AssertComRC(rc);
13010 NOREF(rc);
13011
13012 USBProxyService *service = mParent->host()->usbProxyService();
13013 AssertReturn(service, E_FAIL);
13014 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13015#else
13016 NOREF(aDone);
13017 return S_OK;
13018#endif
13019}
13020
13021/**
13022 * @note Locks this object for writing.
13023 */
13024STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13025 IProgress **aProgress)
13026{
13027 LogFlowThisFuncEnter();
13028
13029 AssertReturn(aSession, E_INVALIDARG);
13030 AssertReturn(aProgress, E_INVALIDARG);
13031
13032 AutoCaller autoCaller(this);
13033
13034 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13035 /*
13036 * We don't assert below because it might happen that a non-direct session
13037 * informs us it is closed right after we've been uninitialized -- it's ok.
13038 */
13039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13040
13041 /* get IInternalSessionControl interface */
13042 ComPtr<IInternalSessionControl> control(aSession);
13043
13044 ComAssertRet(!control.isNull(), E_INVALIDARG);
13045
13046 /* Creating a Progress object requires the VirtualBox lock, and
13047 * thus locking it here is required by the lock order rules. */
13048 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13049
13050 if (control == mData->mSession.mDirectControl)
13051 {
13052 ComAssertRet(aProgress, E_POINTER);
13053
13054 /* The direct session is being normally closed by the client process
13055 * ----------------------------------------------------------------- */
13056
13057 /* go to the closing state (essential for all open*Session() calls and
13058 * for #checkForDeath()) */
13059 Assert(mData->mSession.mState == SessionState_Locked);
13060 mData->mSession.mState = SessionState_Unlocking;
13061
13062 /* set direct control to NULL to release the remote instance */
13063 mData->mSession.mDirectControl.setNull();
13064 LogFlowThisFunc(("Direct control is set to NULL\n"));
13065
13066 if (mData->mSession.mProgress)
13067 {
13068 /* finalize the progress, someone might wait if a frontend
13069 * closes the session before powering on the VM. */
13070 mData->mSession.mProgress->notifyComplete(E_FAIL,
13071 COM_IIDOF(ISession),
13072 getComponentName(),
13073 tr("The VM session was closed before any attempt to power it on"));
13074 mData->mSession.mProgress.setNull();
13075 }
13076
13077 /* Create the progress object the client will use to wait until
13078 * #checkForDeath() is called to uninitialize this session object after
13079 * it releases the IPC semaphore.
13080 * Note! Because we're "reusing" mProgress here, this must be a proxy
13081 * object just like for LaunchVMProcess. */
13082 Assert(mData->mSession.mProgress.isNull());
13083 ComObjPtr<ProgressProxy> progress;
13084 progress.createObject();
13085 ComPtr<IUnknown> pPeer(mPeer);
13086 progress->init(mParent, pPeer,
13087 Bstr(tr("Closing session")).raw(),
13088 FALSE /* aCancelable */);
13089 progress.queryInterfaceTo(aProgress);
13090 mData->mSession.mProgress = progress;
13091 }
13092 else
13093 {
13094 /* the remote session is being normally closed */
13095 Data::Session::RemoteControlList::iterator it =
13096 mData->mSession.mRemoteControls.begin();
13097 while (it != mData->mSession.mRemoteControls.end())
13098 {
13099 if (control == *it)
13100 break;
13101 ++it;
13102 }
13103 BOOL found = it != mData->mSession.mRemoteControls.end();
13104 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13105 E_INVALIDARG);
13106 // This MUST be erase(it), not remove(*it) as the latter triggers a
13107 // very nasty use after free due to the place where the value "lives".
13108 mData->mSession.mRemoteControls.erase(it);
13109 }
13110
13111 /* signal the client watcher thread, because the client is going away */
13112 mParent->updateClientWatcher();
13113
13114 LogFlowThisFuncLeave();
13115 return S_OK;
13116}
13117
13118/**
13119 * @note Locks this object for writing.
13120 */
13121STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13122{
13123 LogFlowThisFuncEnter();
13124
13125 CheckComArgOutPointerValid(aProgress);
13126 CheckComArgOutPointerValid(aStateFilePath);
13127
13128 AutoCaller autoCaller(this);
13129 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13130
13131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13132
13133 AssertReturn( mData->mMachineState == MachineState_Paused
13134 && mConsoleTaskData.mLastState == MachineState_Null
13135 && mConsoleTaskData.strStateFilePath.isEmpty(),
13136 E_FAIL);
13137
13138 /* create a progress object to track operation completion */
13139 ComObjPtr<Progress> pProgress;
13140 pProgress.createObject();
13141 pProgress->init(getVirtualBox(),
13142 static_cast<IMachine *>(this) /* aInitiator */,
13143 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13144 FALSE /* aCancelable */);
13145
13146 Utf8Str strStateFilePath;
13147 /* stateFilePath is null when the machine is not running */
13148 if (mData->mMachineState == MachineState_Paused)
13149 composeSavedStateFilename(strStateFilePath);
13150
13151 /* fill in the console task data */
13152 mConsoleTaskData.mLastState = mData->mMachineState;
13153 mConsoleTaskData.strStateFilePath = strStateFilePath;
13154 mConsoleTaskData.mProgress = pProgress;
13155
13156 /* set the state to Saving (this is expected by Console::SaveState()) */
13157 setMachineState(MachineState_Saving);
13158
13159 strStateFilePath.cloneTo(aStateFilePath);
13160 pProgress.queryInterfaceTo(aProgress);
13161
13162 return S_OK;
13163}
13164
13165/**
13166 * @note Locks mParent + this object for writing.
13167 */
13168STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13169{
13170 LogFlowThisFunc(("\n"));
13171
13172 AutoCaller autoCaller(this);
13173 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13174
13175 /* endSavingState() need mParent lock */
13176 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13177
13178 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13179 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13180 && mConsoleTaskData.mLastState != MachineState_Null
13181 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13182 E_FAIL);
13183
13184 /*
13185 * On failure, set the state to the state we had when BeginSavingState()
13186 * was called (this is expected by Console::SaveState() and the associated
13187 * task). On success the VM process already changed the state to
13188 * MachineState_Saved, so no need to do anything.
13189 */
13190 if (FAILED(iResult))
13191 setMachineState(mConsoleTaskData.mLastState);
13192
13193 return endSavingState(iResult, aErrMsg);
13194}
13195
13196/**
13197 * @note Locks this object for writing.
13198 */
13199STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13200{
13201 LogFlowThisFunc(("\n"));
13202
13203 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13204
13205 AutoCaller autoCaller(this);
13206 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13207
13208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13209
13210 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13211 || mData->mMachineState == MachineState_Teleported
13212 || mData->mMachineState == MachineState_Aborted
13213 , E_FAIL); /** @todo setError. */
13214
13215 Utf8Str stateFilePathFull = aSavedStateFile;
13216 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13217 if (RT_FAILURE(vrc))
13218 return setError(VBOX_E_FILE_ERROR,
13219 tr("Invalid saved state file path '%ls' (%Rrc)"),
13220 aSavedStateFile,
13221 vrc);
13222
13223 mSSData->strStateFilePath = stateFilePathFull;
13224
13225 /* The below setMachineState() will detect the state transition and will
13226 * update the settings file */
13227
13228 return setMachineState(MachineState_Saved);
13229}
13230
13231STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13232 ComSafeArrayOut(BSTR, aValues),
13233 ComSafeArrayOut(LONG64, aTimestamps),
13234 ComSafeArrayOut(BSTR, aFlags))
13235{
13236 LogFlowThisFunc(("\n"));
13237
13238#ifdef VBOX_WITH_GUEST_PROPS
13239 using namespace guestProp;
13240
13241 AutoCaller autoCaller(this);
13242 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13243
13244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13245
13246 CheckComArgOutSafeArrayPointerValid(aNames);
13247 CheckComArgOutSafeArrayPointerValid(aValues);
13248 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13249 CheckComArgOutSafeArrayPointerValid(aFlags);
13250
13251 size_t cEntries = mHWData->mGuestProperties.size();
13252 com::SafeArray<BSTR> names(cEntries);
13253 com::SafeArray<BSTR> values(cEntries);
13254 com::SafeArray<LONG64> timestamps(cEntries);
13255 com::SafeArray<BSTR> flags(cEntries);
13256 unsigned i = 0;
13257 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13258 it != mHWData->mGuestProperties.end();
13259 ++it)
13260 {
13261 char szFlags[MAX_FLAGS_LEN + 1];
13262 it->first.cloneTo(&names[i]);
13263 it->second.strValue.cloneTo(&values[i]);
13264 timestamps[i] = it->second.mTimestamp;
13265 /* If it is NULL, keep it NULL. */
13266 if (it->second.mFlags)
13267 {
13268 writeFlags(it->second.mFlags, szFlags);
13269 Bstr(szFlags).cloneTo(&flags[i]);
13270 }
13271 else
13272 flags[i] = NULL;
13273 ++i;
13274 }
13275 names.detachTo(ComSafeArrayOutArg(aNames));
13276 values.detachTo(ComSafeArrayOutArg(aValues));
13277 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13278 flags.detachTo(ComSafeArrayOutArg(aFlags));
13279 return S_OK;
13280#else
13281 ReturnComNotImplemented();
13282#endif
13283}
13284
13285STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13286 IN_BSTR aValue,
13287 LONG64 aTimestamp,
13288 IN_BSTR aFlags)
13289{
13290 LogFlowThisFunc(("\n"));
13291
13292#ifdef VBOX_WITH_GUEST_PROPS
13293 using namespace guestProp;
13294
13295 CheckComArgStrNotEmptyOrNull(aName);
13296 CheckComArgNotNull(aValue);
13297 CheckComArgNotNull(aFlags);
13298
13299 try
13300 {
13301 /*
13302 * Convert input up front.
13303 */
13304 Utf8Str utf8Name(aName);
13305 uint32_t fFlags = NILFLAG;
13306 if (aFlags)
13307 {
13308 Utf8Str utf8Flags(aFlags);
13309 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13310 AssertRCReturn(vrc, E_INVALIDARG);
13311 }
13312
13313 /*
13314 * Now grab the object lock, validate the state and do the update.
13315 */
13316 AutoCaller autoCaller(this);
13317 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13318
13319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13320
13321 switch (mData->mMachineState)
13322 {
13323 case MachineState_Paused:
13324 case MachineState_Running:
13325 case MachineState_Teleporting:
13326 case MachineState_TeleportingPausedVM:
13327 case MachineState_LiveSnapshotting:
13328 case MachineState_DeletingSnapshotOnline:
13329 case MachineState_DeletingSnapshotPaused:
13330 case MachineState_Saving:
13331 case MachineState_Stopping:
13332 break;
13333
13334 default:
13335 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13336 VBOX_E_INVALID_VM_STATE);
13337 }
13338
13339 setModified(IsModified_MachineData);
13340 mHWData.backup();
13341
13342 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13343 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13344 if (it != mHWData->mGuestProperties.end())
13345 {
13346 if (!fDelete)
13347 {
13348 it->second.strValue = aValue;
13349 it->second.mTimestamp = aTimestamp;
13350 it->second.mFlags = fFlags;
13351 }
13352 else
13353 mHWData->mGuestProperties.erase(it);
13354
13355 mData->mGuestPropertiesModified = TRUE;
13356 }
13357 else if (!fDelete)
13358 {
13359 HWData::GuestProperty prop;
13360 prop.strValue = aValue;
13361 prop.mTimestamp = aTimestamp;
13362 prop.mFlags = fFlags;
13363
13364 mHWData->mGuestProperties[utf8Name] = prop;
13365 mData->mGuestPropertiesModified = TRUE;
13366 }
13367
13368 /*
13369 * Send a callback notification if appropriate
13370 */
13371 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13372 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13373 RTSTR_MAX,
13374 utf8Name.c_str(),
13375 RTSTR_MAX, NULL)
13376 )
13377 {
13378 alock.release();
13379
13380 mParent->onGuestPropertyChange(mData->mUuid,
13381 aName,
13382 aValue,
13383 aFlags);
13384 }
13385 }
13386 catch (...)
13387 {
13388 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13389 }
13390 return S_OK;
13391#else
13392 ReturnComNotImplemented();
13393#endif
13394}
13395
13396STDMETHODIMP SessionMachine::LockMedia()
13397{
13398 AutoCaller autoCaller(this);
13399 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13400
13401 AutoMultiWriteLock2 alock(this->lockHandle(),
13402 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13403
13404 AssertReturn( mData->mMachineState == MachineState_Starting
13405 || mData->mMachineState == MachineState_Restoring
13406 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13407
13408 clearError();
13409 alock.release();
13410 return lockMedia();
13411}
13412
13413STDMETHODIMP SessionMachine::UnlockMedia()
13414{
13415 unlockMedia();
13416 return S_OK;
13417}
13418
13419STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13420 IMediumAttachment **aNewAttachment)
13421{
13422 CheckComArgNotNull(aAttachment);
13423 CheckComArgOutPointerValid(aNewAttachment);
13424
13425 AutoCaller autoCaller(this);
13426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13427
13428 // request the host lock first, since might be calling Host methods for getting host drives;
13429 // next, protect the media tree all the while we're in here, as well as our member variables
13430 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13431 this->lockHandle(),
13432 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13433
13434 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13435
13436 Bstr ctrlName;
13437 LONG lPort;
13438 LONG lDevice;
13439 bool fTempEject;
13440 {
13441 AutoCaller autoAttachCaller(this);
13442 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13443
13444 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13445
13446 /* Need to query the details first, as the IMediumAttachment reference
13447 * might be to the original settings, which we are going to change. */
13448 ctrlName = pAttach->getControllerName();
13449 lPort = pAttach->getPort();
13450 lDevice = pAttach->getDevice();
13451 fTempEject = pAttach->getTempEject();
13452 }
13453
13454 if (!fTempEject)
13455 {
13456 /* Remember previously mounted medium. The medium before taking the
13457 * backup is not necessarily the same thing. */
13458 ComObjPtr<Medium> oldmedium;
13459 oldmedium = pAttach->getMedium();
13460
13461 setModified(IsModified_Storage);
13462 mMediaData.backup();
13463
13464 // The backup operation makes the pAttach reference point to the
13465 // old settings. Re-get the correct reference.
13466 pAttach = findAttachment(mMediaData->mAttachments,
13467 ctrlName.raw(),
13468 lPort,
13469 lDevice);
13470
13471 {
13472 AutoCaller autoAttachCaller(this);
13473 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13474
13475 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13476 if (!oldmedium.isNull())
13477 oldmedium->removeBackReference(mData->mUuid);
13478
13479 pAttach->updateMedium(NULL);
13480 pAttach->updateEjected();
13481 }
13482
13483 setModified(IsModified_Storage);
13484 }
13485 else
13486 {
13487 {
13488 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13489 pAttach->updateEjected();
13490 }
13491 }
13492
13493 pAttach.queryInterfaceTo(aNewAttachment);
13494
13495 return S_OK;
13496}
13497
13498// public methods only for internal purposes
13499/////////////////////////////////////////////////////////////////////////////
13500
13501/**
13502 * Called from the client watcher thread to check for expected or unexpected
13503 * death of the client process that has a direct session to this machine.
13504 *
13505 * On Win32 and on OS/2, this method is called only when we've got the
13506 * mutex (i.e. the client has either died or terminated normally) so it always
13507 * returns @c true (the client is terminated, the session machine is
13508 * uninitialized).
13509 *
13510 * On other platforms, the method returns @c true if the client process has
13511 * terminated normally or abnormally and the session machine was uninitialized,
13512 * and @c false if the client process is still alive.
13513 *
13514 * @note Locks this object for writing.
13515 */
13516bool SessionMachine::checkForDeath()
13517{
13518 Uninit::Reason reason;
13519 bool terminated = false;
13520
13521 /* Enclose autoCaller with a block because calling uninit() from under it
13522 * will deadlock. */
13523 {
13524 AutoCaller autoCaller(this);
13525 if (!autoCaller.isOk())
13526 {
13527 /* return true if not ready, to cause the client watcher to exclude
13528 * the corresponding session from watching */
13529 LogFlowThisFunc(("Already uninitialized!\n"));
13530 return true;
13531 }
13532
13533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13534
13535 /* Determine the reason of death: if the session state is Closing here,
13536 * everything is fine. Otherwise it means that the client did not call
13537 * OnSessionEnd() before it released the IPC semaphore. This may happen
13538 * either because the client process has abnormally terminated, or
13539 * because it simply forgot to call ISession::Close() before exiting. We
13540 * threat the latter also as an abnormal termination (see
13541 * Session::uninit() for details). */
13542 reason = mData->mSession.mState == SessionState_Unlocking ?
13543 Uninit::Normal :
13544 Uninit::Abnormal;
13545
13546#if defined(RT_OS_WINDOWS)
13547
13548 AssertMsg(mIPCSem, ("semaphore must be created"));
13549
13550 /* release the IPC mutex */
13551 ::ReleaseMutex(mIPCSem);
13552
13553 terminated = true;
13554
13555#elif defined(RT_OS_OS2)
13556
13557 AssertMsg(mIPCSem, ("semaphore must be created"));
13558
13559 /* release the IPC mutex */
13560 ::DosReleaseMutexSem(mIPCSem);
13561
13562 terminated = true;
13563
13564#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13565
13566 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13567
13568 int val = ::semctl(mIPCSem, 0, GETVAL);
13569 if (val > 0)
13570 {
13571 /* the semaphore is signaled, meaning the session is terminated */
13572 terminated = true;
13573 }
13574
13575#else
13576# error "Port me!"
13577#endif
13578
13579 } /* AutoCaller block */
13580
13581 if (terminated)
13582 uninit(reason);
13583
13584 return terminated;
13585}
13586
13587/**
13588 * @note Locks this object for reading.
13589 */
13590HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13591{
13592 LogFlowThisFunc(("\n"));
13593
13594 AutoCaller autoCaller(this);
13595 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13596
13597 ComPtr<IInternalSessionControl> directControl;
13598 {
13599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13600 directControl = mData->mSession.mDirectControl;
13601 }
13602
13603 /* ignore notifications sent after #OnSessionEnd() is called */
13604 if (!directControl)
13605 return S_OK;
13606
13607 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13608}
13609
13610/**
13611 * @note Locks this object for reading.
13612 */
13613HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13614 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13615{
13616 LogFlowThisFunc(("\n"));
13617
13618 AutoCaller autoCaller(this);
13619 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13620
13621 ComPtr<IInternalSessionControl> directControl;
13622 {
13623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13624 directControl = mData->mSession.mDirectControl;
13625 }
13626
13627 /* ignore notifications sent after #OnSessionEnd() is called */
13628 if (!directControl)
13629 return S_OK;
13630 /*
13631 * instead acting like callback we ask IVirtualBox deliver corresponding event
13632 */
13633
13634 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13635 return S_OK;
13636}
13637
13638/**
13639 * @note Locks this object for reading.
13640 */
13641HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13642{
13643 LogFlowThisFunc(("\n"));
13644
13645 AutoCaller autoCaller(this);
13646 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13647
13648 ComPtr<IInternalSessionControl> directControl;
13649 {
13650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13651 directControl = mData->mSession.mDirectControl;
13652 }
13653
13654 /* ignore notifications sent after #OnSessionEnd() is called */
13655 if (!directControl)
13656 return S_OK;
13657
13658 return directControl->OnSerialPortChange(serialPort);
13659}
13660
13661/**
13662 * @note Locks this object for reading.
13663 */
13664HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13665{
13666 LogFlowThisFunc(("\n"));
13667
13668 AutoCaller autoCaller(this);
13669 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13670
13671 ComPtr<IInternalSessionControl> directControl;
13672 {
13673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13674 directControl = mData->mSession.mDirectControl;
13675 }
13676
13677 /* ignore notifications sent after #OnSessionEnd() is called */
13678 if (!directControl)
13679 return S_OK;
13680
13681 return directControl->OnParallelPortChange(parallelPort);
13682}
13683
13684/**
13685 * @note Locks this object for reading.
13686 */
13687HRESULT SessionMachine::onStorageControllerChange()
13688{
13689 LogFlowThisFunc(("\n"));
13690
13691 AutoCaller autoCaller(this);
13692 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13693
13694 ComPtr<IInternalSessionControl> directControl;
13695 {
13696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13697 directControl = mData->mSession.mDirectControl;
13698 }
13699
13700 /* ignore notifications sent after #OnSessionEnd() is called */
13701 if (!directControl)
13702 return S_OK;
13703
13704 return directControl->OnStorageControllerChange();
13705}
13706
13707/**
13708 * @note Locks this object for reading.
13709 */
13710HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13711{
13712 LogFlowThisFunc(("\n"));
13713
13714 AutoCaller autoCaller(this);
13715 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13716
13717 ComPtr<IInternalSessionControl> directControl;
13718 {
13719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13720 directControl = mData->mSession.mDirectControl;
13721 }
13722
13723 /* ignore notifications sent after #OnSessionEnd() is called */
13724 if (!directControl)
13725 return S_OK;
13726
13727 return directControl->OnMediumChange(aAttachment, aForce);
13728}
13729
13730/**
13731 * @note Locks this object for reading.
13732 */
13733HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13734{
13735 LogFlowThisFunc(("\n"));
13736
13737 AutoCaller autoCaller(this);
13738 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13739
13740 ComPtr<IInternalSessionControl> directControl;
13741 {
13742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13743 directControl = mData->mSession.mDirectControl;
13744 }
13745
13746 /* ignore notifications sent after #OnSessionEnd() is called */
13747 if (!directControl)
13748 return S_OK;
13749
13750 return directControl->OnCPUChange(aCPU, aRemove);
13751}
13752
13753HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13754{
13755 LogFlowThisFunc(("\n"));
13756
13757 AutoCaller autoCaller(this);
13758 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13759
13760 ComPtr<IInternalSessionControl> directControl;
13761 {
13762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13763 directControl = mData->mSession.mDirectControl;
13764 }
13765
13766 /* ignore notifications sent after #OnSessionEnd() is called */
13767 if (!directControl)
13768 return S_OK;
13769
13770 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13771}
13772
13773/**
13774 * @note Locks this object for reading.
13775 */
13776HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13777{
13778 LogFlowThisFunc(("\n"));
13779
13780 AutoCaller autoCaller(this);
13781 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13782
13783 ComPtr<IInternalSessionControl> directControl;
13784 {
13785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13786 directControl = mData->mSession.mDirectControl;
13787 }
13788
13789 /* ignore notifications sent after #OnSessionEnd() is called */
13790 if (!directControl)
13791 return S_OK;
13792
13793 return directControl->OnVRDEServerChange(aRestart);
13794}
13795
13796/**
13797 * @note Locks this object for reading.
13798 */
13799HRESULT SessionMachine::onUSBControllerChange()
13800{
13801 LogFlowThisFunc(("\n"));
13802
13803 AutoCaller autoCaller(this);
13804 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13805
13806 ComPtr<IInternalSessionControl> directControl;
13807 {
13808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13809 directControl = mData->mSession.mDirectControl;
13810 }
13811
13812 /* ignore notifications sent after #OnSessionEnd() is called */
13813 if (!directControl)
13814 return S_OK;
13815
13816 return directControl->OnUSBControllerChange();
13817}
13818
13819/**
13820 * @note Locks this object for reading.
13821 */
13822HRESULT SessionMachine::onSharedFolderChange()
13823{
13824 LogFlowThisFunc(("\n"));
13825
13826 AutoCaller autoCaller(this);
13827 AssertComRCReturnRC(autoCaller.rc());
13828
13829 ComPtr<IInternalSessionControl> directControl;
13830 {
13831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13832 directControl = mData->mSession.mDirectControl;
13833 }
13834
13835 /* ignore notifications sent after #OnSessionEnd() is called */
13836 if (!directControl)
13837 return S_OK;
13838
13839 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13840}
13841
13842/**
13843 * @note Locks this object for reading.
13844 */
13845HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13846{
13847 LogFlowThisFunc(("\n"));
13848
13849 AutoCaller autoCaller(this);
13850 AssertComRCReturnRC(autoCaller.rc());
13851
13852 ComPtr<IInternalSessionControl> directControl;
13853 {
13854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13855 directControl = mData->mSession.mDirectControl;
13856 }
13857
13858 /* ignore notifications sent after #OnSessionEnd() is called */
13859 if (!directControl)
13860 return S_OK;
13861
13862 return directControl->OnClipboardModeChange(aClipboardMode);
13863}
13864
13865/**
13866 * @note Locks this object for reading.
13867 */
13868HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13869{
13870 LogFlowThisFunc(("\n"));
13871
13872 AutoCaller autoCaller(this);
13873 AssertComRCReturnRC(autoCaller.rc());
13874
13875 ComPtr<IInternalSessionControl> directControl;
13876 {
13877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13878 directControl = mData->mSession.mDirectControl;
13879 }
13880
13881 /* ignore notifications sent after #OnSessionEnd() is called */
13882 if (!directControl)
13883 return S_OK;
13884
13885 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13886}
13887
13888/**
13889 * @note Locks this object for reading.
13890 */
13891HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13892{
13893 LogFlowThisFunc(("\n"));
13894
13895 AutoCaller autoCaller(this);
13896 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13897
13898 ComPtr<IInternalSessionControl> directControl;
13899 {
13900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13901 directControl = mData->mSession.mDirectControl;
13902 }
13903
13904 /* ignore notifications sent after #OnSessionEnd() is called */
13905 if (!directControl)
13906 return S_OK;
13907
13908 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13909}
13910
13911/**
13912 * @note Locks this object for reading.
13913 */
13914HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13915{
13916 LogFlowThisFunc(("\n"));
13917
13918 AutoCaller autoCaller(this);
13919 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13920
13921 ComPtr<IInternalSessionControl> directControl;
13922 {
13923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13924 directControl = mData->mSession.mDirectControl;
13925 }
13926
13927 /* ignore notifications sent after #OnSessionEnd() is called */
13928 if (!directControl)
13929 return S_OK;
13930
13931 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13932}
13933
13934/**
13935 * Returns @c true if this machine's USB controller reports it has a matching
13936 * filter for the given USB device and @c false otherwise.
13937 *
13938 * @note locks this object for reading.
13939 */
13940bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13941{
13942 AutoCaller autoCaller(this);
13943 /* silently return if not ready -- this method may be called after the
13944 * direct machine session has been called */
13945 if (!autoCaller.isOk())
13946 return false;
13947
13948#ifdef VBOX_WITH_USB
13949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13950
13951 switch (mData->mMachineState)
13952 {
13953 case MachineState_Starting:
13954 case MachineState_Restoring:
13955 case MachineState_TeleportingIn:
13956 case MachineState_Paused:
13957 case MachineState_Running:
13958 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13959 * elsewhere... */
13960 alock.release();
13961 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13962 default: break;
13963 }
13964#else
13965 NOREF(aDevice);
13966 NOREF(aMaskedIfs);
13967#endif
13968 return false;
13969}
13970
13971/**
13972 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13973 */
13974HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13975 IVirtualBoxErrorInfo *aError,
13976 ULONG aMaskedIfs)
13977{
13978 LogFlowThisFunc(("\n"));
13979
13980 AutoCaller autoCaller(this);
13981
13982 /* This notification may happen after the machine object has been
13983 * uninitialized (the session was closed), so don't assert. */
13984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13985
13986 ComPtr<IInternalSessionControl> directControl;
13987 {
13988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13989 directControl = mData->mSession.mDirectControl;
13990 }
13991
13992 /* fail on notifications sent after #OnSessionEnd() is called, it is
13993 * expected by the caller */
13994 if (!directControl)
13995 return E_FAIL;
13996
13997 /* No locks should be held at this point. */
13998 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13999 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14000
14001 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14002}
14003
14004/**
14005 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14006 */
14007HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14008 IVirtualBoxErrorInfo *aError)
14009{
14010 LogFlowThisFunc(("\n"));
14011
14012 AutoCaller autoCaller(this);
14013
14014 /* This notification may happen after the machine object has been
14015 * uninitialized (the session was closed), so don't assert. */
14016 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14017
14018 ComPtr<IInternalSessionControl> directControl;
14019 {
14020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14021 directControl = mData->mSession.mDirectControl;
14022 }
14023
14024 /* fail on notifications sent after #OnSessionEnd() is called, it is
14025 * expected by the caller */
14026 if (!directControl)
14027 return E_FAIL;
14028
14029 /* No locks should be held at this point. */
14030 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14031 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14032
14033 return directControl->OnUSBDeviceDetach(aId, aError);
14034}
14035
14036// protected methods
14037/////////////////////////////////////////////////////////////////////////////
14038
14039/**
14040 * Helper method to finalize saving the state.
14041 *
14042 * @note Must be called from under this object's lock.
14043 *
14044 * @param aRc S_OK if the snapshot has been taken successfully
14045 * @param aErrMsg human readable error message for failure
14046 *
14047 * @note Locks mParent + this objects for writing.
14048 */
14049HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14050{
14051 LogFlowThisFuncEnter();
14052
14053 AutoCaller autoCaller(this);
14054 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14055
14056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14057
14058 HRESULT rc = S_OK;
14059
14060 if (SUCCEEDED(aRc))
14061 {
14062 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14063
14064 /* save all VM settings */
14065 rc = saveSettings(NULL);
14066 // no need to check whether VirtualBox.xml needs saving also since
14067 // we can't have a name change pending at this point
14068 }
14069 else
14070 {
14071 // delete the saved state file (it might have been already created);
14072 // we need not check whether this is shared with a snapshot here because
14073 // we certainly created this saved state file here anew
14074 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14075 }
14076
14077 /* notify the progress object about operation completion */
14078 Assert(mConsoleTaskData.mProgress);
14079 if (SUCCEEDED(aRc))
14080 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14081 else
14082 {
14083 if (aErrMsg.length())
14084 mConsoleTaskData.mProgress->notifyComplete(aRc,
14085 COM_IIDOF(ISession),
14086 getComponentName(),
14087 aErrMsg.c_str());
14088 else
14089 mConsoleTaskData.mProgress->notifyComplete(aRc);
14090 }
14091
14092 /* clear out the temporary saved state data */
14093 mConsoleTaskData.mLastState = MachineState_Null;
14094 mConsoleTaskData.strStateFilePath.setNull();
14095 mConsoleTaskData.mProgress.setNull();
14096
14097 LogFlowThisFuncLeave();
14098 return rc;
14099}
14100
14101/**
14102 * Deletes the given file if it is no longer in use by either the current machine state
14103 * (if the machine is "saved") or any of the machine's snapshots.
14104 *
14105 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14106 * but is different for each SnapshotMachine. When calling this, the order of calling this
14107 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14108 * is therefore critical. I know, it's all rather messy.
14109 *
14110 * @param strStateFile
14111 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14112 */
14113void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14114 Snapshot *pSnapshotToIgnore)
14115{
14116 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14117 if ( (strStateFile.isNotEmpty())
14118 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14119 )
14120 // ... and it must also not be shared with other snapshots
14121 if ( !mData->mFirstSnapshot
14122 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14123 // this checks the SnapshotMachine's state file paths
14124 )
14125 RTFileDelete(strStateFile.c_str());
14126}
14127
14128/**
14129 * Locks the attached media.
14130 *
14131 * All attached hard disks are locked for writing and DVD/floppy are locked for
14132 * reading. Parents of attached hard disks (if any) are locked for reading.
14133 *
14134 * This method also performs accessibility check of all media it locks: if some
14135 * media is inaccessible, the method will return a failure and a bunch of
14136 * extended error info objects per each inaccessible medium.
14137 *
14138 * Note that this method is atomic: if it returns a success, all media are
14139 * locked as described above; on failure no media is locked at all (all
14140 * succeeded individual locks will be undone).
14141 *
14142 * The caller is responsible for doing the necessary state sanity checks.
14143 *
14144 * The locks made by this method must be undone by calling #unlockMedia() when
14145 * no more needed.
14146 */
14147HRESULT SessionMachine::lockMedia()
14148{
14149 AutoCaller autoCaller(this);
14150 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14151
14152 AutoMultiWriteLock2 alock(this->lockHandle(),
14153 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14154
14155 /* bail out if trying to lock things with already set up locking */
14156 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14157
14158 MultiResult mrc(S_OK);
14159
14160 /* Collect locking information for all medium objects attached to the VM. */
14161 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14162 it != mMediaData->mAttachments.end();
14163 ++it)
14164 {
14165 MediumAttachment* pAtt = *it;
14166 DeviceType_T devType = pAtt->getType();
14167 Medium *pMedium = pAtt->getMedium();
14168
14169 MediumLockList *pMediumLockList(new MediumLockList());
14170 // There can be attachments without a medium (floppy/dvd), and thus
14171 // it's impossible to create a medium lock list. It still makes sense
14172 // to have the empty medium lock list in the map in case a medium is
14173 // attached later.
14174 if (pMedium != NULL)
14175 {
14176 MediumType_T mediumType = pMedium->getType();
14177 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14178 || mediumType == MediumType_Shareable;
14179 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14180
14181 alock.release();
14182 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14183 !fIsReadOnlyLock /* fMediumLockWrite */,
14184 NULL,
14185 *pMediumLockList);
14186 alock.acquire();
14187 if (FAILED(mrc))
14188 {
14189 delete pMediumLockList;
14190 mData->mSession.mLockedMedia.Clear();
14191 break;
14192 }
14193 }
14194
14195 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14196 if (FAILED(rc))
14197 {
14198 mData->mSession.mLockedMedia.Clear();
14199 mrc = setError(rc,
14200 tr("Collecting locking information for all attached media failed"));
14201 break;
14202 }
14203 }
14204
14205 if (SUCCEEDED(mrc))
14206 {
14207 /* Now lock all media. If this fails, nothing is locked. */
14208 alock.release();
14209 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14210 alock.acquire();
14211 if (FAILED(rc))
14212 {
14213 mrc = setError(rc,
14214 tr("Locking of attached media failed"));
14215 }
14216 }
14217
14218 return mrc;
14219}
14220
14221/**
14222 * Undoes the locks made by by #lockMedia().
14223 */
14224void SessionMachine::unlockMedia()
14225{
14226 AutoCaller autoCaller(this);
14227 AssertComRCReturnVoid(autoCaller.rc());
14228
14229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14230
14231 /* we may be holding important error info on the current thread;
14232 * preserve it */
14233 ErrorInfoKeeper eik;
14234
14235 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14236 AssertComRC(rc);
14237}
14238
14239/**
14240 * Helper to change the machine state (reimplementation).
14241 *
14242 * @note Locks this object for writing.
14243 * @note This method must not call saveSettings or SaveSettings, otherwise
14244 * it can cause crashes in random places due to unexpectedly committing
14245 * the current settings. The caller is responsible for that. The call
14246 * to saveStateSettings is fine, because this method does not commit.
14247 */
14248HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14249{
14250 LogFlowThisFuncEnter();
14251 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14252
14253 AutoCaller autoCaller(this);
14254 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14255
14256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14257
14258 MachineState_T oldMachineState = mData->mMachineState;
14259
14260 AssertMsgReturn(oldMachineState != aMachineState,
14261 ("oldMachineState=%s, aMachineState=%s\n",
14262 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14263 E_FAIL);
14264
14265 HRESULT rc = S_OK;
14266
14267 int stsFlags = 0;
14268 bool deleteSavedState = false;
14269
14270 /* detect some state transitions */
14271
14272 if ( ( oldMachineState == MachineState_Saved
14273 && aMachineState == MachineState_Restoring)
14274 || ( ( oldMachineState == MachineState_PoweredOff
14275 || oldMachineState == MachineState_Teleported
14276 || oldMachineState == MachineState_Aborted
14277 )
14278 && ( aMachineState == MachineState_TeleportingIn
14279 || aMachineState == MachineState_Starting
14280 )
14281 )
14282 )
14283 {
14284 /* The EMT thread is about to start */
14285
14286 /* Nothing to do here for now... */
14287
14288 /// @todo NEWMEDIA don't let mDVDDrive and other children
14289 /// change anything when in the Starting/Restoring state
14290 }
14291 else if ( ( oldMachineState == MachineState_Running
14292 || oldMachineState == MachineState_Paused
14293 || oldMachineState == MachineState_Teleporting
14294 || oldMachineState == MachineState_LiveSnapshotting
14295 || oldMachineState == MachineState_Stuck
14296 || oldMachineState == MachineState_Starting
14297 || oldMachineState == MachineState_Stopping
14298 || oldMachineState == MachineState_Saving
14299 || oldMachineState == MachineState_Restoring
14300 || oldMachineState == MachineState_TeleportingPausedVM
14301 || oldMachineState == MachineState_TeleportingIn
14302 )
14303 && ( aMachineState == MachineState_PoweredOff
14304 || aMachineState == MachineState_Saved
14305 || aMachineState == MachineState_Teleported
14306 || aMachineState == MachineState_Aborted
14307 )
14308 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14309 * snapshot */
14310 && ( mConsoleTaskData.mSnapshot.isNull()
14311 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14312 )
14313 )
14314 {
14315 /* The EMT thread has just stopped, unlock attached media. Note that as
14316 * opposed to locking that is done from Console, we do unlocking here
14317 * because the VM process may have aborted before having a chance to
14318 * properly unlock all media it locked. */
14319
14320 unlockMedia();
14321 }
14322
14323 if (oldMachineState == MachineState_Restoring)
14324 {
14325 if (aMachineState != MachineState_Saved)
14326 {
14327 /*
14328 * delete the saved state file once the machine has finished
14329 * restoring from it (note that Console sets the state from
14330 * Restoring to Saved if the VM couldn't restore successfully,
14331 * to give the user an ability to fix an error and retry --
14332 * we keep the saved state file in this case)
14333 */
14334 deleteSavedState = true;
14335 }
14336 }
14337 else if ( oldMachineState == MachineState_Saved
14338 && ( aMachineState == MachineState_PoweredOff
14339 || aMachineState == MachineState_Aborted
14340 || aMachineState == MachineState_Teleported
14341 )
14342 )
14343 {
14344 /*
14345 * delete the saved state after Console::ForgetSavedState() is called
14346 * or if the VM process (owning a direct VM session) crashed while the
14347 * VM was Saved
14348 */
14349
14350 /// @todo (dmik)
14351 // Not sure that deleting the saved state file just because of the
14352 // client death before it attempted to restore the VM is a good
14353 // thing. But when it crashes we need to go to the Aborted state
14354 // which cannot have the saved state file associated... The only
14355 // way to fix this is to make the Aborted condition not a VM state
14356 // but a bool flag: i.e., when a crash occurs, set it to true and
14357 // change the state to PoweredOff or Saved depending on the
14358 // saved state presence.
14359
14360 deleteSavedState = true;
14361 mData->mCurrentStateModified = TRUE;
14362 stsFlags |= SaveSTS_CurStateModified;
14363 }
14364
14365 if ( aMachineState == MachineState_Starting
14366 || aMachineState == MachineState_Restoring
14367 || aMachineState == MachineState_TeleportingIn
14368 )
14369 {
14370 /* set the current state modified flag to indicate that the current
14371 * state is no more identical to the state in the
14372 * current snapshot */
14373 if (!mData->mCurrentSnapshot.isNull())
14374 {
14375 mData->mCurrentStateModified = TRUE;
14376 stsFlags |= SaveSTS_CurStateModified;
14377 }
14378 }
14379
14380 if (deleteSavedState)
14381 {
14382 if (mRemoveSavedState)
14383 {
14384 Assert(!mSSData->strStateFilePath.isEmpty());
14385
14386 // it is safe to delete the saved state file if ...
14387 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14388 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14389 // ... none of the snapshots share the saved state file
14390 )
14391 RTFileDelete(mSSData->strStateFilePath.c_str());
14392 }
14393
14394 mSSData->strStateFilePath.setNull();
14395 stsFlags |= SaveSTS_StateFilePath;
14396 }
14397
14398 /* redirect to the underlying peer machine */
14399 mPeer->setMachineState(aMachineState);
14400
14401 if ( aMachineState == MachineState_PoweredOff
14402 || aMachineState == MachineState_Teleported
14403 || aMachineState == MachineState_Aborted
14404 || aMachineState == MachineState_Saved)
14405 {
14406 /* the machine has stopped execution
14407 * (or the saved state file was adopted) */
14408 stsFlags |= SaveSTS_StateTimeStamp;
14409 }
14410
14411 if ( ( oldMachineState == MachineState_PoweredOff
14412 || oldMachineState == MachineState_Aborted
14413 || oldMachineState == MachineState_Teleported
14414 )
14415 && aMachineState == MachineState_Saved)
14416 {
14417 /* the saved state file was adopted */
14418 Assert(!mSSData->strStateFilePath.isEmpty());
14419 stsFlags |= SaveSTS_StateFilePath;
14420 }
14421
14422#ifdef VBOX_WITH_GUEST_PROPS
14423 if ( aMachineState == MachineState_PoweredOff
14424 || aMachineState == MachineState_Aborted
14425 || aMachineState == MachineState_Teleported)
14426 {
14427 /* Make sure any transient guest properties get removed from the
14428 * property store on shutdown. */
14429
14430 HWData::GuestPropertyMap::const_iterator it;
14431 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14432 if (!fNeedsSaving)
14433 for (it = mHWData->mGuestProperties.begin();
14434 it != mHWData->mGuestProperties.end(); ++it)
14435 if ( (it->second.mFlags & guestProp::TRANSIENT)
14436 || (it->second.mFlags & guestProp::TRANSRESET))
14437 {
14438 fNeedsSaving = true;
14439 break;
14440 }
14441 if (fNeedsSaving)
14442 {
14443 mData->mCurrentStateModified = TRUE;
14444 stsFlags |= SaveSTS_CurStateModified;
14445 }
14446 }
14447#endif
14448
14449 rc = saveStateSettings(stsFlags);
14450
14451 if ( ( oldMachineState != MachineState_PoweredOff
14452 && oldMachineState != MachineState_Aborted
14453 && oldMachineState != MachineState_Teleported
14454 )
14455 && ( aMachineState == MachineState_PoweredOff
14456 || aMachineState == MachineState_Aborted
14457 || aMachineState == MachineState_Teleported
14458 )
14459 )
14460 {
14461 /* we've been shut down for any reason */
14462 /* no special action so far */
14463 }
14464
14465 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14466 LogFlowThisFuncLeave();
14467 return rc;
14468}
14469
14470/**
14471 * Sends the current machine state value to the VM process.
14472 *
14473 * @note Locks this object for reading, then calls a client process.
14474 */
14475HRESULT SessionMachine::updateMachineStateOnClient()
14476{
14477 AutoCaller autoCaller(this);
14478 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14479
14480 ComPtr<IInternalSessionControl> directControl;
14481 {
14482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14483 AssertReturn(!!mData, E_FAIL);
14484 directControl = mData->mSession.mDirectControl;
14485
14486 /* directControl may be already set to NULL here in #OnSessionEnd()
14487 * called too early by the direct session process while there is still
14488 * some operation (like deleting the snapshot) in progress. The client
14489 * process in this case is waiting inside Session::close() for the
14490 * "end session" process object to complete, while #uninit() called by
14491 * #checkForDeath() on the Watcher thread is waiting for the pending
14492 * operation to complete. For now, we accept this inconsistent behavior
14493 * and simply do nothing here. */
14494
14495 if (mData->mSession.mState == SessionState_Unlocking)
14496 return S_OK;
14497
14498 AssertReturn(!directControl.isNull(), E_FAIL);
14499 }
14500
14501 return directControl->UpdateMachineState(mData->mMachineState);
14502}
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