VirtualBox

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

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

Main/VPX: error handling

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 493.5 KB
Line 
1/* $Id: MachineImpl.cpp 46825 2013-06-27 10:23:20Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.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 mVideoCaptureWidth = 1024;
171 mVideoCaptureHeight = 768;
172 mVideoCaptureRate = 512;
173 mVideoCaptureFPS = 25;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
190 mHWVirtExExclusive = false;
191#else
192 mHWVirtExExclusive = true;
193#endif
194#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
195 mPAEEnabled = true;
196#else
197 mPAEEnabled = false;
198#endif
199 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
200 mSyntheticCpu = false;
201 mHPETEnabled = false;
202
203 /* default boot order: floppy - DVD - HDD */
204 mBootOrder[0] = DeviceType_Floppy;
205 mBootOrder[1] = DeviceType_DVD;
206 mBootOrder[2] = DeviceType_HardDisk;
207 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
208 mBootOrder[i] = DeviceType_Null;
209
210 mClipboardMode = ClipboardMode_Disabled;
211 mDragAndDropMode = DragAndDropMode_Disabled;
212 mGuestPropertyNotificationPatterns = "";
213
214 mFirmwareType = FirmwareType_BIOS;
215 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
216 mPointingHIDType = PointingHIDType_PS2Mouse;
217 mChipsetType = ChipsetType_PIIX3;
218 mEmulatedUSBWebcamEnabled = FALSE;
219 mEmulatedUSBCardReaderEnabled = FALSE;
220
221 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
222 mCPUAttached[i] = false;
223
224 mIOCacheEnabled = true;
225 mIOCacheSize = 5; /* 5MB */
226
227 /* Maximum CPU execution cap by default. */
228 mCpuExecutionCap = 100;
229}
230
231Machine::HWData::~HWData()
232{
233}
234
235/////////////////////////////////////////////////////////////////////////////
236// Machine::HDData structure
237/////////////////////////////////////////////////////////////////////////////
238
239Machine::MediaData::MediaData()
240{
241}
242
243Machine::MediaData::~MediaData()
244{
245}
246
247/////////////////////////////////////////////////////////////////////////////
248// Machine class
249/////////////////////////////////////////////////////////////////////////////
250
251// constructor / destructor
252/////////////////////////////////////////////////////////////////////////////
253
254Machine::Machine() :
255#ifdef VBOX_WITH_RESOURCE_USAGE_API
256 mCollectorGuest(NULL),
257#endif
258 mPeer(NULL),
259 mParent(NULL),
260 mSerialPorts(),
261 mParallelPorts(),
262 uRegistryNeedsSaving(0)
263{}
264
265Machine::~Machine()
266{}
267
268HRESULT Machine::FinalConstruct()
269{
270 LogFlowThisFunc(("\n"));
271 return BaseFinalConstruct();
272}
273
274void Machine::FinalRelease()
275{
276 LogFlowThisFunc(("\n"));
277 uninit();
278 BaseFinalRelease();
279}
280
281/**
282 * Initializes a new machine instance; this init() variant creates a new, empty machine.
283 * This gets called from VirtualBox::CreateMachine().
284 *
285 * @param aParent Associated parent object
286 * @param strConfigFile Local file system path to the VM settings file (can
287 * be relative to the VirtualBox config directory).
288 * @param strName name for the machine
289 * @param llGroups list of groups for the machine
290 * @param aOsType OS Type of this machine or NULL.
291 * @param aId UUID for the new machine.
292 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
293 *
294 * @return Success indicator. if not S_OK, the machine object is invalid
295 */
296HRESULT Machine::init(VirtualBox *aParent,
297 const Utf8Str &strConfigFile,
298 const Utf8Str &strName,
299 const StringsList &llGroups,
300 GuestOSType *aOsType,
301 const Guid &aId,
302 bool fForceOverwrite,
303 bool fDirectoryIncludesUUID)
304{
305 LogFlowThisFuncEnter();
306 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
307
308 /* Enclose the state transition NotReady->InInit->Ready */
309 AutoInitSpan autoInitSpan(this);
310 AssertReturn(autoInitSpan.isOk(), E_FAIL);
311
312 HRESULT rc = initImpl(aParent, strConfigFile);
313 if (FAILED(rc)) return rc;
314
315 rc = tryCreateMachineConfigFile(fForceOverwrite);
316 if (FAILED(rc)) return rc;
317
318 if (SUCCEEDED(rc))
319 {
320 // create an empty machine config
321 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
322
323 rc = initDataAndChildObjects();
324 }
325
326 if (SUCCEEDED(rc))
327 {
328 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
329 mData->mAccessible = TRUE;
330
331 unconst(mData->mUuid) = aId;
332
333 mUserData->s.strName = strName;
334
335 mUserData->s.llGroups = llGroups;
336
337 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
338 // the "name sync" flag determines whether the machine directory gets renamed along
339 // with the machine file; say so if the settings file name is the same as the
340 // settings file parent directory (machine directory)
341 mUserData->s.fNameSync = isInOwnDir();
342
343 // initialize the default snapshots folder
344 rc = COMSETTER(SnapshotFolder)(NULL);
345 AssertComRC(rc);
346
347 if (aOsType)
348 {
349 /* Store OS type */
350 mUserData->s.strOsType = aOsType->id();
351
352 /* Apply BIOS defaults */
353 mBIOSSettings->applyDefaults(aOsType);
354
355 /* Apply network adapters defaults */
356 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
357 mNetworkAdapters[slot]->applyDefaults(aOsType);
358
359 /* Apply serial port defaults */
360 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
361 mSerialPorts[slot]->applyDefaults(aOsType);
362
363 /* Let the OS type select 64-bit ness. */
364 mHWData->mLongMode = aOsType->is64Bit()
365 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
366 }
367
368 /* At this point the changing of the current state modification
369 * flag is allowed. */
370 allowStateModification();
371
372 /* commit all changes made during the initialization */
373 commit();
374 }
375
376 /* Confirm a successful initialization when it's the case */
377 if (SUCCEEDED(rc))
378 {
379 if (mData->mAccessible)
380 autoInitSpan.setSucceeded();
381 else
382 autoInitSpan.setLimited();
383 }
384
385 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
386 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
387 mData->mRegistered,
388 mData->mAccessible,
389 rc));
390
391 LogFlowThisFuncLeave();
392
393 return rc;
394}
395
396/**
397 * Initializes a new instance with data from machine XML (formerly Init_Registered).
398 * Gets called in two modes:
399 *
400 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
401 * UUID is specified and we mark the machine as "registered";
402 *
403 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
404 * and the machine remains unregistered until RegisterMachine() is called.
405 *
406 * @param aParent Associated parent object
407 * @param aConfigFile Local file system path to the VM settings file (can
408 * be relative to the VirtualBox config directory).
409 * @param aId UUID of the machine or NULL (see above).
410 *
411 * @return Success indicator. if not S_OK, the machine object is invalid
412 */
413HRESULT Machine::initFromSettings(VirtualBox *aParent,
414 const Utf8Str &strConfigFile,
415 const Guid *aId)
416{
417 LogFlowThisFuncEnter();
418 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
419
420 /* Enclose the state transition NotReady->InInit->Ready */
421 AutoInitSpan autoInitSpan(this);
422 AssertReturn(autoInitSpan.isOk(), E_FAIL);
423
424 HRESULT rc = initImpl(aParent, strConfigFile);
425 if (FAILED(rc)) return rc;
426
427 if (aId)
428 {
429 // loading a registered VM:
430 unconst(mData->mUuid) = *aId;
431 mData->mRegistered = TRUE;
432 // now load the settings from XML:
433 rc = registeredInit();
434 // this calls initDataAndChildObjects() and loadSettings()
435 }
436 else
437 {
438 // opening an unregistered VM (VirtualBox::OpenMachine()):
439 rc = initDataAndChildObjects();
440
441 if (SUCCEEDED(rc))
442 {
443 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
444 mData->mAccessible = TRUE;
445
446 try
447 {
448 // load and parse machine XML; this will throw on XML or logic errors
449 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
450
451 // reject VM UUID duplicates, they can happen if someone
452 // tries to register an already known VM config again
453 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
454 true /* fPermitInaccessible */,
455 false /* aDoSetError */,
456 NULL) != VBOX_E_OBJECT_NOT_FOUND)
457 {
458 throw setError(E_FAIL,
459 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
460 mData->m_strConfigFile.c_str());
461 }
462
463 // use UUID from machine config
464 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
465
466 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
467 NULL /* puuidRegistry */);
468 if (FAILED(rc)) throw rc;
469
470 /* At this point the changing of the current state modification
471 * flag is allowed. */
472 allowStateModification();
473
474 commit();
475 }
476 catch (HRESULT err)
477 {
478 /* we assume that error info is set by the thrower */
479 rc = err;
480 }
481 catch (...)
482 {
483 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
484 }
485 }
486 }
487
488 /* Confirm a successful initialization when it's the case */
489 if (SUCCEEDED(rc))
490 {
491 if (mData->mAccessible)
492 autoInitSpan.setSucceeded();
493 else
494 {
495 autoInitSpan.setLimited();
496
497 // uninit media from this machine's media registry, or else
498 // reloading the settings will fail
499 mParent->unregisterMachineMedia(getId());
500 }
501 }
502
503 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
504 "rc=%08X\n",
505 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
506 mData->mRegistered, mData->mAccessible, rc));
507
508 LogFlowThisFuncLeave();
509
510 return rc;
511}
512
513/**
514 * Initializes a new instance from a machine config that is already in memory
515 * (import OVF case). Since we are importing, the UUID in the machine
516 * config is ignored and we always generate a fresh one.
517 *
518 * @param strName Name for the new machine; this overrides what is specified in config and is used
519 * for the settings file as well.
520 * @param config Machine configuration loaded and parsed from XML.
521 *
522 * @return Success indicator. if not S_OK, the machine object is invalid
523 */
524HRESULT Machine::init(VirtualBox *aParent,
525 const Utf8Str &strName,
526 const settings::MachineConfigFile &config)
527{
528 LogFlowThisFuncEnter();
529
530 /* Enclose the state transition NotReady->InInit->Ready */
531 AutoInitSpan autoInitSpan(this);
532 AssertReturn(autoInitSpan.isOk(), E_FAIL);
533
534 Utf8Str strConfigFile;
535 aParent->getDefaultMachineFolder(strConfigFile);
536 strConfigFile.append(RTPATH_DELIMITER);
537 strConfigFile.append(strName);
538 strConfigFile.append(RTPATH_DELIMITER);
539 strConfigFile.append(strName);
540 strConfigFile.append(".vbox");
541
542 HRESULT rc = initImpl(aParent, strConfigFile);
543 if (FAILED(rc)) return rc;
544
545 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
546 if (FAILED(rc)) return rc;
547
548 rc = initDataAndChildObjects();
549
550 if (SUCCEEDED(rc))
551 {
552 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
553 mData->mAccessible = TRUE;
554
555 // create empty machine config for instance data
556 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
557
558 // generate fresh UUID, ignore machine config
559 unconst(mData->mUuid).create();
560
561 rc = loadMachineDataFromSettings(config,
562 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
563
564 // override VM name as well, it may be different
565 mUserData->s.strName = strName;
566
567 if (SUCCEEDED(rc))
568 {
569 /* At this point the changing of the current state modification
570 * flag is allowed. */
571 allowStateModification();
572
573 /* commit all changes made during the initialization */
574 commit();
575 }
576 }
577
578 /* Confirm a successful initialization when it's the case */
579 if (SUCCEEDED(rc))
580 {
581 if (mData->mAccessible)
582 autoInitSpan.setSucceeded();
583 else
584 {
585 autoInitSpan.setLimited();
586
587 // uninit media from this machine's media registry, or else
588 // reloading the settings will fail
589 mParent->unregisterMachineMedia(getId());
590 }
591 }
592
593 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
594 "rc=%08X\n",
595 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
596 mData->mRegistered, mData->mAccessible, rc));
597
598 LogFlowThisFuncLeave();
599
600 return rc;
601}
602
603/**
604 * Shared code between the various init() implementations.
605 * @param aParent
606 * @return
607 */
608HRESULT Machine::initImpl(VirtualBox *aParent,
609 const Utf8Str &strConfigFile)
610{
611 LogFlowThisFuncEnter();
612
613 AssertReturn(aParent, E_INVALIDARG);
614 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
615
616 HRESULT rc = S_OK;
617
618 /* share the parent weakly */
619 unconst(mParent) = aParent;
620
621 /* allocate the essential machine data structure (the rest will be
622 * allocated later by initDataAndChildObjects() */
623 mData.allocate();
624
625 /* memorize the config file name (as provided) */
626 mData->m_strConfigFile = strConfigFile;
627
628 /* get the full file name */
629 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
630 if (RT_FAILURE(vrc1))
631 return setError(VBOX_E_FILE_ERROR,
632 tr("Invalid machine settings file name '%s' (%Rrc)"),
633 strConfigFile.c_str(),
634 vrc1);
635
636 LogFlowThisFuncLeave();
637
638 return rc;
639}
640
641/**
642 * Tries to create a machine settings file in the path stored in the machine
643 * instance data. Used when a new machine is created to fail gracefully if
644 * the settings file could not be written (e.g. because machine dir is read-only).
645 * @return
646 */
647HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
648{
649 HRESULT rc = S_OK;
650
651 // when we create a new machine, we must be able to create the settings file
652 RTFILE f = NIL_RTFILE;
653 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
654 if ( RT_SUCCESS(vrc)
655 || vrc == VERR_SHARING_VIOLATION
656 )
657 {
658 if (RT_SUCCESS(vrc))
659 RTFileClose(f);
660 if (!fForceOverwrite)
661 rc = setError(VBOX_E_FILE_ERROR,
662 tr("Machine settings file '%s' already exists"),
663 mData->m_strConfigFileFull.c_str());
664 else
665 {
666 /* try to delete the config file, as otherwise the creation
667 * of a new settings file will fail. */
668 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
669 if (RT_FAILURE(vrc2))
670 rc = setError(VBOX_E_FILE_ERROR,
671 tr("Could not delete the existing settings file '%s' (%Rrc)"),
672 mData->m_strConfigFileFull.c_str(), vrc2);
673 }
674 }
675 else if ( vrc != VERR_FILE_NOT_FOUND
676 && vrc != VERR_PATH_NOT_FOUND
677 )
678 rc = setError(VBOX_E_FILE_ERROR,
679 tr("Invalid machine settings file name '%s' (%Rrc)"),
680 mData->m_strConfigFileFull.c_str(),
681 vrc);
682 return rc;
683}
684
685/**
686 * Initializes the registered machine by loading the settings file.
687 * This method is separated from #init() in order to make it possible to
688 * retry the operation after VirtualBox startup instead of refusing to
689 * startup the whole VirtualBox server in case if the settings file of some
690 * registered VM is invalid or inaccessible.
691 *
692 * @note Must be always called from this object's write lock
693 * (unless called from #init() that doesn't need any locking).
694 * @note Locks the mUSBController method for writing.
695 * @note Subclasses must not call this method.
696 */
697HRESULT Machine::registeredInit()
698{
699 AssertReturn(!isSessionMachine(), E_FAIL);
700 AssertReturn(!isSnapshotMachine(), E_FAIL);
701 AssertReturn(mData->mUuid.isValid(), E_FAIL);
702 AssertReturn(!mData->mAccessible, E_FAIL);
703
704 HRESULT rc = initDataAndChildObjects();
705
706 if (SUCCEEDED(rc))
707 {
708 /* Temporarily reset the registered flag in order to let setters
709 * potentially called from loadSettings() succeed (isMutable() used in
710 * all setters will return FALSE for a Machine instance if mRegistered
711 * is TRUE). */
712 mData->mRegistered = FALSE;
713
714 try
715 {
716 // load and parse machine XML; this will throw on XML or logic errors
717 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
718
719 if (mData->mUuid != mData->pMachineConfigFile->uuid)
720 throw setError(E_FAIL,
721 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
722 mData->pMachineConfigFile->uuid.raw(),
723 mData->m_strConfigFileFull.c_str(),
724 mData->mUuid.toString().c_str(),
725 mParent->settingsFilePath().c_str());
726
727 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
728 NULL /* const Guid *puuidRegistry */);
729 if (FAILED(rc)) throw rc;
730 }
731 catch (HRESULT err)
732 {
733 /* we assume that error info is set by the thrower */
734 rc = err;
735 }
736 catch (...)
737 {
738 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
739 }
740
741 /* Restore the registered flag (even on failure) */
742 mData->mRegistered = TRUE;
743 }
744
745 if (SUCCEEDED(rc))
746 {
747 /* Set mAccessible to TRUE only if we successfully locked and loaded
748 * the settings file */
749 mData->mAccessible = TRUE;
750
751 /* commit all changes made during loading the settings file */
752 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
753 /// @todo r=klaus for some reason the settings loading logic backs up
754 // the settings, and therefore a commit is needed. Should probably be changed.
755 }
756 else
757 {
758 /* If the machine is registered, then, instead of returning a
759 * failure, we mark it as inaccessible and set the result to
760 * success to give it a try later */
761
762 /* fetch the current error info */
763 mData->mAccessError = com::ErrorInfo();
764 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
765 mData->mUuid.raw(),
766 mData->mAccessError.getText().raw()));
767
768 /* rollback all changes */
769 rollback(false /* aNotify */);
770
771 // uninit media from this machine's media registry, or else
772 // reloading the settings will fail
773 mParent->unregisterMachineMedia(getId());
774
775 /* uninitialize the common part to make sure all data is reset to
776 * default (null) values */
777 uninitDataAndChildObjects();
778
779 rc = S_OK;
780 }
781
782 return rc;
783}
784
785/**
786 * Uninitializes the instance.
787 * Called either from FinalRelease() or by the parent when it gets destroyed.
788 *
789 * @note The caller of this method must make sure that this object
790 * a) doesn't have active callers on the current thread and b) is not locked
791 * by the current thread; otherwise uninit() will hang either a) due to
792 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
793 * a dead-lock caused by this thread waiting for all callers on the other
794 * threads are done but preventing them from doing so by holding a lock.
795 */
796void Machine::uninit()
797{
798 LogFlowThisFuncEnter();
799
800 Assert(!isWriteLockOnCurrentThread());
801
802 Assert(!uRegistryNeedsSaving);
803 if (uRegistryNeedsSaving)
804 {
805 AutoCaller autoCaller(this);
806 if (SUCCEEDED(autoCaller.rc()))
807 {
808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
809 saveSettings(NULL, Machine::SaveS_Force);
810 }
811 }
812
813 /* Enclose the state transition Ready->InUninit->NotReady */
814 AutoUninitSpan autoUninitSpan(this);
815 if (autoUninitSpan.uninitDone())
816 return;
817
818 Assert(!isSnapshotMachine());
819 Assert(!isSessionMachine());
820 Assert(!!mData);
821
822 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
823 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
824
825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
826
827 if (!mData->mSession.mMachine.isNull())
828 {
829 /* Theoretically, this can only happen if the VirtualBox server has been
830 * terminated while there were clients running that owned open direct
831 * sessions. Since in this case we are definitely called by
832 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
833 * won't happen on the client watcher thread (because it does
834 * VirtualBox::addCaller() for the duration of the
835 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
836 * cannot happen until the VirtualBox caller is released). This is
837 * important, because SessionMachine::uninit() cannot correctly operate
838 * after we return from this method (it expects the Machine instance is
839 * still valid). We'll call it ourselves below.
840 */
841 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
842 (SessionMachine*)mData->mSession.mMachine));
843
844 if (Global::IsOnlineOrTransient(mData->mMachineState))
845 {
846 LogWarningThisFunc(("Setting state to Aborted!\n"));
847 /* set machine state using SessionMachine reimplementation */
848 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
849 }
850
851 /*
852 * Uninitialize SessionMachine using public uninit() to indicate
853 * an unexpected uninitialization.
854 */
855 mData->mSession.mMachine->uninit();
856 /* SessionMachine::uninit() must set mSession.mMachine to null */
857 Assert(mData->mSession.mMachine.isNull());
858 }
859
860 // uninit media from this machine's media registry, if they're still there
861 Guid uuidMachine(getId());
862
863 /* the lock is no more necessary (SessionMachine is uninitialized) */
864 alock.release();
865
866 /* XXX This will fail with
867 * "cannot be closed because it is still attached to 1 virtual machines"
868 * because at this point we did not call uninitDataAndChildObjects() yet
869 * and therefore also removeBackReference() for all these mediums was not called! */
870
871 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
872 mParent->unregisterMachineMedia(uuidMachine);
873
874 // has machine been modified?
875 if (mData->flModifications)
876 {
877 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
878 rollback(false /* aNotify */);
879 }
880
881 if (mData->mAccessible)
882 uninitDataAndChildObjects();
883
884 /* free the essential data structure last */
885 mData.free();
886
887 LogFlowThisFuncLeave();
888}
889
890// IMachine properties
891/////////////////////////////////////////////////////////////////////////////
892
893STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
894{
895 CheckComArgOutPointerValid(aParent);
896
897 AutoLimitedCaller autoCaller(this);
898 if (FAILED(autoCaller.rc())) return autoCaller.rc();
899
900 /* mParent is constant during life time, no need to lock */
901 ComObjPtr<VirtualBox> pVirtualBox(mParent);
902 pVirtualBox.queryInterfaceTo(aParent);
903
904 return S_OK;
905}
906
907STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
908{
909 CheckComArgOutPointerValid(aAccessible);
910
911 AutoLimitedCaller autoCaller(this);
912 if (FAILED(autoCaller.rc())) return autoCaller.rc();
913
914 LogFlowThisFunc(("ENTER\n"));
915
916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
917
918 HRESULT rc = S_OK;
919
920 if (!mData->mAccessible)
921 {
922 /* try to initialize the VM once more if not accessible */
923
924 AutoReinitSpan autoReinitSpan(this);
925 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
926
927#ifdef DEBUG
928 LogFlowThisFunc(("Dumping media backreferences\n"));
929 mParent->dumpAllBackRefs();
930#endif
931
932 if (mData->pMachineConfigFile)
933 {
934 // reset the XML file to force loadSettings() (called from registeredInit())
935 // to parse it again; the file might have changed
936 delete mData->pMachineConfigFile;
937 mData->pMachineConfigFile = NULL;
938 }
939
940 rc = registeredInit();
941
942 if (SUCCEEDED(rc) && mData->mAccessible)
943 {
944 autoReinitSpan.setSucceeded();
945
946 /* make sure interesting parties will notice the accessibility
947 * state change */
948 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
949 mParent->onMachineDataChange(mData->mUuid);
950 }
951 }
952
953 if (SUCCEEDED(rc))
954 *aAccessible = mData->mAccessible;
955
956 LogFlowThisFuncLeave();
957
958 return rc;
959}
960
961STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
962{
963 CheckComArgOutPointerValid(aAccessError);
964
965 AutoLimitedCaller autoCaller(this);
966 if (FAILED(autoCaller.rc())) return autoCaller.rc();
967
968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
969
970 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
971 {
972 /* return shortly */
973 aAccessError = NULL;
974 return S_OK;
975 }
976
977 HRESULT rc = S_OK;
978
979 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
980 rc = errorInfo.createObject();
981 if (SUCCEEDED(rc))
982 {
983 errorInfo->init(mData->mAccessError.getResultCode(),
984 mData->mAccessError.getInterfaceID().ref(),
985 Utf8Str(mData->mAccessError.getComponent()).c_str(),
986 Utf8Str(mData->mAccessError.getText()));
987 rc = errorInfo.queryInterfaceTo(aAccessError);
988 }
989
990 return rc;
991}
992
993STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
994{
995 CheckComArgOutPointerValid(aName);
996
997 AutoCaller autoCaller(this);
998 if (FAILED(autoCaller.rc())) return autoCaller.rc();
999
1000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1001
1002 mUserData->s.strName.cloneTo(aName);
1003
1004 return S_OK;
1005}
1006
1007STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1008{
1009 CheckComArgStrNotEmptyOrNull(aName);
1010
1011 AutoCaller autoCaller(this);
1012 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1013
1014 // prohibit setting a UUID only as the machine name, or else it can
1015 // never be found by findMachine()
1016 Guid test(aName);
1017
1018 if (test.isValid())
1019 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1020
1021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1022
1023 HRESULT rc = checkStateDependency(MutableStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strName = aName;
1029
1030 return S_OK;
1031}
1032
1033STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1034{
1035 CheckComArgOutPointerValid(aDescription);
1036
1037 AutoCaller autoCaller(this);
1038 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1039
1040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1041
1042 mUserData->s.strDescription.cloneTo(aDescription);
1043
1044 return S_OK;
1045}
1046
1047STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1048{
1049 AutoCaller autoCaller(this);
1050 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1051
1052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1053
1054 // this can be done in principle in any state as it doesn't affect the VM
1055 // significantly, but play safe by not messing around while complex
1056 // activities are going on
1057 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1058 if (FAILED(rc)) return rc;
1059
1060 setModified(IsModified_MachineData);
1061 mUserData.backup();
1062 mUserData->s.strDescription = aDescription;
1063
1064 return S_OK;
1065}
1066
1067STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1068{
1069 CheckComArgOutPointerValid(aId);
1070
1071 AutoLimitedCaller autoCaller(this);
1072 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1073
1074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1075
1076 mData->mUuid.toUtf16().cloneTo(aId);
1077
1078 return S_OK;
1079}
1080
1081STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1082{
1083 CheckComArgOutSafeArrayPointerValid(aGroups);
1084
1085 AutoCaller autoCaller(this);
1086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1087
1088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1089 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1090 size_t i = 0;
1091 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1092 it != mUserData->s.llGroups.end();
1093 ++it, i++)
1094 {
1095 Bstr tmp = *it;
1096 tmp.cloneTo(&groups[i]);
1097 }
1098 groups.detachTo(ComSafeArrayOutArg(aGroups));
1099
1100 return S_OK;
1101}
1102
1103STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1104{
1105 AutoCaller autoCaller(this);
1106 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1107
1108 StringsList llGroups;
1109 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1110 if (FAILED(rc))
1111 return rc;
1112
1113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 // changing machine groups is possible while the VM is offline
1116 rc = checkStateDependency(OfflineStateDep);
1117 if (FAILED(rc)) return rc;
1118
1119 setModified(IsModified_MachineData);
1120 mUserData.backup();
1121 mUserData->s.llGroups = llGroups;
1122
1123 return S_OK;
1124}
1125
1126STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1127{
1128 CheckComArgOutPointerValid(aOSTypeId);
1129
1130 AutoCaller autoCaller(this);
1131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1132
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 mUserData->s.strOsType.cloneTo(aOSTypeId);
1136
1137 return S_OK;
1138}
1139
1140STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1141{
1142 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1143
1144 AutoCaller autoCaller(this);
1145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1146
1147 /* look up the object by Id to check it is valid */
1148 ComPtr<IGuestOSType> guestOSType;
1149 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1150 if (FAILED(rc)) return rc;
1151
1152 /* when setting, always use the "etalon" value for consistency -- lookup
1153 * by ID is case-insensitive and the input value may have different case */
1154 Bstr osTypeId;
1155 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1156 if (FAILED(rc)) return rc;
1157
1158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1159
1160 rc = checkStateDependency(MutableStateDep);
1161 if (FAILED(rc)) return rc;
1162
1163 setModified(IsModified_MachineData);
1164 mUserData.backup();
1165 mUserData->s.strOsType = osTypeId;
1166
1167 return S_OK;
1168}
1169
1170
1171STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1172{
1173 CheckComArgOutPointerValid(aFirmwareType);
1174
1175 AutoCaller autoCaller(this);
1176 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1177
1178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1179
1180 *aFirmwareType = mHWData->mFirmwareType;
1181
1182 return S_OK;
1183}
1184
1185STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1186{
1187 AutoCaller autoCaller(this);
1188 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1190
1191 HRESULT rc = checkStateDependency(MutableStateDep);
1192 if (FAILED(rc)) return rc;
1193
1194 setModified(IsModified_MachineData);
1195 mHWData.backup();
1196 mHWData->mFirmwareType = aFirmwareType;
1197
1198 return S_OK;
1199}
1200
1201STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1202{
1203 CheckComArgOutPointerValid(aKeyboardHIDType);
1204
1205 AutoCaller autoCaller(this);
1206 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1207
1208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1209
1210 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1211
1212 return S_OK;
1213}
1214
1215STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1216{
1217 AutoCaller autoCaller(this);
1218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1220
1221 HRESULT rc = checkStateDependency(MutableStateDep);
1222 if (FAILED(rc)) return rc;
1223
1224 setModified(IsModified_MachineData);
1225 mHWData.backup();
1226 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1227
1228 return S_OK;
1229}
1230
1231STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1232{
1233 CheckComArgOutPointerValid(aPointingHIDType);
1234
1235 AutoCaller autoCaller(this);
1236 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1237
1238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1239
1240 *aPointingHIDType = mHWData->mPointingHIDType;
1241
1242 return S_OK;
1243}
1244
1245STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1246{
1247 AutoCaller autoCaller(this);
1248 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1250
1251 HRESULT rc = checkStateDependency(MutableStateDep);
1252 if (FAILED(rc)) return rc;
1253
1254 setModified(IsModified_MachineData);
1255 mHWData.backup();
1256 mHWData->mPointingHIDType = aPointingHIDType;
1257
1258 return S_OK;
1259}
1260
1261STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1262{
1263 CheckComArgOutPointerValid(aChipsetType);
1264
1265 AutoCaller autoCaller(this);
1266 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1267
1268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1269
1270 *aChipsetType = mHWData->mChipsetType;
1271
1272 return S_OK;
1273}
1274
1275STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1276{
1277 AutoCaller autoCaller(this);
1278 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1280
1281 HRESULT rc = checkStateDependency(MutableStateDep);
1282 if (FAILED(rc)) return rc;
1283
1284 if (aChipsetType != mHWData->mChipsetType)
1285 {
1286 setModified(IsModified_MachineData);
1287 mHWData.backup();
1288 mHWData->mChipsetType = aChipsetType;
1289
1290 // Resize network adapter array, to be finalized on commit/rollback.
1291 // We must not throw away entries yet, otherwise settings are lost
1292 // without a way to roll back.
1293 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1294 size_t oldCount = mNetworkAdapters.size();
1295 if (newCount > oldCount)
1296 {
1297 mNetworkAdapters.resize(newCount);
1298 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1299 {
1300 unconst(mNetworkAdapters[slot]).createObject();
1301 mNetworkAdapters[slot]->init(this, slot);
1302 }
1303 }
1304 }
1305
1306 return S_OK;
1307}
1308
1309STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1310{
1311 CheckComArgOutPointerValid(aHWVersion);
1312
1313 AutoCaller autoCaller(this);
1314 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1315
1316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1317
1318 mHWData->mHWVersion.cloneTo(aHWVersion);
1319
1320 return S_OK;
1321}
1322
1323STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1324{
1325 /* check known version */
1326 Utf8Str hwVersion = aHWVersion;
1327 if ( hwVersion.compare("1") != 0
1328 && hwVersion.compare("2") != 0)
1329 return setError(E_INVALIDARG,
1330 tr("Invalid hardware version: %ls\n"), aHWVersion);
1331
1332 AutoCaller autoCaller(this);
1333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1334
1335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1336
1337 HRESULT rc = checkStateDependency(MutableStateDep);
1338 if (FAILED(rc)) return rc;
1339
1340 setModified(IsModified_MachineData);
1341 mHWData.backup();
1342 mHWData->mHWVersion = hwVersion;
1343
1344 return S_OK;
1345}
1346
1347STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1348{
1349 CheckComArgOutPointerValid(aUUID);
1350
1351 AutoCaller autoCaller(this);
1352 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1353
1354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1355
1356 if (mHWData->mHardwareUUID.isValid())
1357 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1358 else
1359 mData->mUuid.toUtf16().cloneTo(aUUID);
1360
1361 return S_OK;
1362}
1363
1364STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1365{
1366 Guid hardwareUUID(aUUID);
1367 if (!hardwareUUID.isValid())
1368 return E_INVALIDARG;
1369
1370 AutoCaller autoCaller(this);
1371 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1372
1373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1374
1375 HRESULT rc = checkStateDependency(MutableStateDep);
1376 if (FAILED(rc)) return rc;
1377
1378 setModified(IsModified_MachineData);
1379 mHWData.backup();
1380 if (hardwareUUID == mData->mUuid)
1381 mHWData->mHardwareUUID.clear();
1382 else
1383 mHWData->mHardwareUUID = hardwareUUID;
1384
1385 return S_OK;
1386}
1387
1388STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1389{
1390 CheckComArgOutPointerValid(memorySize);
1391
1392 AutoCaller autoCaller(this);
1393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1394
1395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1396
1397 *memorySize = mHWData->mMemorySize;
1398
1399 return S_OK;
1400}
1401
1402STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1403{
1404 /* check RAM limits */
1405 if ( memorySize < MM_RAM_MIN_IN_MB
1406 || memorySize > MM_RAM_MAX_IN_MB
1407 )
1408 return setError(E_INVALIDARG,
1409 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1410 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1411
1412 AutoCaller autoCaller(this);
1413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1414
1415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1416
1417 HRESULT rc = checkStateDependency(MutableStateDep);
1418 if (FAILED(rc)) return rc;
1419
1420 setModified(IsModified_MachineData);
1421 mHWData.backup();
1422 mHWData->mMemorySize = memorySize;
1423
1424 return S_OK;
1425}
1426
1427STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1428{
1429 CheckComArgOutPointerValid(CPUCount);
1430
1431 AutoCaller autoCaller(this);
1432 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1433
1434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1435
1436 *CPUCount = mHWData->mCPUCount;
1437
1438 return S_OK;
1439}
1440
1441STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1442{
1443 /* check CPU limits */
1444 if ( CPUCount < SchemaDefs::MinCPUCount
1445 || CPUCount > SchemaDefs::MaxCPUCount
1446 )
1447 return setError(E_INVALIDARG,
1448 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1449 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1450
1451 AutoCaller autoCaller(this);
1452 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1453
1454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1457 if (mHWData->mCPUHotPlugEnabled)
1458 {
1459 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1460 {
1461 if (mHWData->mCPUAttached[idx])
1462 return setError(E_INVALIDARG,
1463 tr("There is still a CPU attached to socket %lu."
1464 "Detach the CPU before removing the socket"),
1465 CPUCount, idx+1);
1466 }
1467 }
1468
1469 HRESULT rc = checkStateDependency(MutableStateDep);
1470 if (FAILED(rc)) return rc;
1471
1472 setModified(IsModified_MachineData);
1473 mHWData.backup();
1474 mHWData->mCPUCount = CPUCount;
1475
1476 return S_OK;
1477}
1478
1479STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1480{
1481 CheckComArgOutPointerValid(aExecutionCap);
1482
1483 AutoCaller autoCaller(this);
1484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1485
1486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1487
1488 *aExecutionCap = mHWData->mCpuExecutionCap;
1489
1490 return S_OK;
1491}
1492
1493STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1494{
1495 HRESULT rc = S_OK;
1496
1497 /* check throttle limits */
1498 if ( aExecutionCap < 1
1499 || aExecutionCap > 100
1500 )
1501 return setError(E_INVALIDARG,
1502 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1503 aExecutionCap, 1, 100);
1504
1505 AutoCaller autoCaller(this);
1506 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1507
1508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1509
1510 alock.release();
1511 rc = onCPUExecutionCapChange(aExecutionCap);
1512 alock.acquire();
1513 if (FAILED(rc)) return rc;
1514
1515 setModified(IsModified_MachineData);
1516 mHWData.backup();
1517 mHWData->mCpuExecutionCap = aExecutionCap;
1518
1519 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1520 if (Global::IsOnline(mData->mMachineState))
1521 saveSettings(NULL);
1522
1523 return S_OK;
1524}
1525
1526
1527STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1528{
1529 CheckComArgOutPointerValid(aEnabled);
1530
1531 AutoCaller autoCaller(this);
1532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1533
1534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1535
1536 *aEnabled = mHWData->mCPUHotPlugEnabled;
1537
1538 return S_OK;
1539}
1540
1541STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1542{
1543 HRESULT rc = S_OK;
1544
1545 AutoCaller autoCaller(this);
1546 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1547
1548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1549
1550 rc = checkStateDependency(MutableStateDep);
1551 if (FAILED(rc)) return rc;
1552
1553 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1554 {
1555 if (aEnabled)
1556 {
1557 setModified(IsModified_MachineData);
1558 mHWData.backup();
1559
1560 /* Add the amount of CPUs currently attached */
1561 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1562 {
1563 mHWData->mCPUAttached[i] = true;
1564 }
1565 }
1566 else
1567 {
1568 /*
1569 * We can disable hotplug only if the amount of maximum CPUs is equal
1570 * to the amount of attached CPUs
1571 */
1572 unsigned cCpusAttached = 0;
1573 unsigned iHighestId = 0;
1574
1575 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1576 {
1577 if (mHWData->mCPUAttached[i])
1578 {
1579 cCpusAttached++;
1580 iHighestId = i;
1581 }
1582 }
1583
1584 if ( (cCpusAttached != mHWData->mCPUCount)
1585 || (iHighestId >= mHWData->mCPUCount))
1586 return setError(E_INVALIDARG,
1587 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1588
1589 setModified(IsModified_MachineData);
1590 mHWData.backup();
1591 }
1592 }
1593
1594 mHWData->mCPUHotPlugEnabled = aEnabled;
1595
1596 return rc;
1597}
1598
1599STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1600{
1601#ifdef VBOX_WITH_USB_CARDREADER
1602 CheckComArgOutPointerValid(aEnabled);
1603
1604 AutoCaller autoCaller(this);
1605 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1606
1607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1608
1609 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1610
1611 return S_OK;
1612#else
1613 NOREF(aEnabled);
1614 return E_NOTIMPL;
1615#endif
1616}
1617
1618STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1619{
1620#ifdef VBOX_WITH_USB_CARDREADER
1621 AutoCaller autoCaller(this);
1622 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 HRESULT rc = checkStateDependency(MutableStateDep);
1626 if (FAILED(rc)) return rc;
1627
1628 setModified(IsModified_MachineData);
1629 mHWData.backup();
1630 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1631
1632 return S_OK;
1633#else
1634 NOREF(aEnabled);
1635 return E_NOTIMPL;
1636#endif
1637}
1638
1639STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1640{
1641#ifdef VBOX_WITH_USB_VIDEO
1642 CheckComArgOutPointerValid(aEnabled);
1643
1644 AutoCaller autoCaller(this);
1645 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1646
1647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1648
1649 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1650
1651 return S_OK;
1652#else
1653 NOREF(aEnabled);
1654 return E_NOTIMPL;
1655#endif
1656}
1657
1658STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1659{
1660#ifdef VBOX_WITH_USB_VIDEO
1661 AutoCaller autoCaller(this);
1662 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1663 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1664
1665 HRESULT rc = checkStateDependency(MutableStateDep);
1666 if (FAILED(rc)) return rc;
1667
1668 setModified(IsModified_MachineData);
1669 mHWData.backup();
1670 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1671
1672 return S_OK;
1673#else
1674 NOREF(aEnabled);
1675 return E_NOTIMPL;
1676#endif
1677}
1678
1679STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1680{
1681 CheckComArgOutPointerValid(aEnabled);
1682
1683 AutoCaller autoCaller(this);
1684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 *aEnabled = mHWData->mHPETEnabled;
1688
1689 return S_OK;
1690}
1691
1692STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1693{
1694 HRESULT rc = S_OK;
1695
1696 AutoCaller autoCaller(this);
1697 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1699
1700 rc = checkStateDependency(MutableStateDep);
1701 if (FAILED(rc)) return rc;
1702
1703 setModified(IsModified_MachineData);
1704 mHWData.backup();
1705
1706 mHWData->mHPETEnabled = aEnabled;
1707
1708 return rc;
1709}
1710
1711STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1712{
1713 AutoCaller autoCaller(this);
1714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1715
1716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1717
1718 *fEnabled = mHWData->mVideoCaptureEnabled;
1719 return S_OK;
1720}
1721
1722STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1723{
1724 HRESULT rc = S_OK;
1725
1726 AutoCaller autoCaller(this);
1727 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1729
1730 setModified(IsModified_MachineData);
1731 mHWData.backup();
1732 mHWData->mVideoCaptureEnabled = fEnabled;
1733
1734 alock.release();
1735 rc = onVideoCaptureChange();
1736 alock.acquire();
1737 if (FAILED(rc))
1738 {
1739 /*
1740 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1741 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1742 * determine if it should start or stop capturing. Therefore we need to manually
1743 * undo change.
1744 */
1745 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1746 return rc;
1747 }
1748
1749 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1750 if (Global::IsOnline(mData->mMachineState))
1751 saveSettings(NULL);
1752
1753 return rc;
1754}
1755
1756STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1757{
1758 CheckComArgOutSafeArrayPointerValid(aScreens);
1759
1760 AutoCaller autoCaller(this);
1761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1762
1763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1764
1765 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1766 for (unsigned i = 0; i < screens.size(); i++)
1767 screens[i] = mHWData->maVideoCaptureScreens[i];
1768 screens.detachTo(ComSafeArrayOutArg(aScreens));
1769 return S_OK;
1770}
1771
1772STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1773{
1774 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1775 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1776 bool fChanged = false;
1777
1778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1779
1780 for (unsigned i = 0; i < screens.size(); i++)
1781 {
1782 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1783 {
1784 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1785 fChanged = true;
1786 }
1787 }
1788 if (fChanged)
1789 {
1790 alock.release();
1791 HRESULT rc = onVideoCaptureChange();
1792 alock.acquire();
1793 if (FAILED(rc)) return rc;
1794 setModified(IsModified_MachineData);
1795
1796 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1797 if (Global::IsOnline(mData->mMachineState))
1798 saveSettings(NULL);
1799 }
1800
1801 return S_OK;
1802}
1803
1804STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1805{
1806 AutoCaller autoCaller(this);
1807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1808
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 if (mHWData->mVideoCaptureFile.isEmpty())
1811 {
1812 Utf8Str defaultFile;
1813 getDefaultVideoCaptureFile(defaultFile);
1814 defaultFile.cloneTo(apFile);
1815 }
1816 else
1817 mHWData->mVideoCaptureFile.cloneTo(apFile);
1818 return S_OK;
1819}
1820
1821STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1822{
1823 Utf8Str strFile(aFile);
1824 AutoCaller autoCaller(this);
1825 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1826
1827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1828
1829 if ( Global::IsOnline(mData->mMachineState)
1830 && mHWData->mVideoCaptureEnabled)
1831 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1832
1833 if (!RTPathStartsWithRoot(strFile.c_str()))
1834 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1835
1836 if (!strFile.isEmpty())
1837 {
1838 Utf8Str defaultFile;
1839 getDefaultVideoCaptureFile(defaultFile);
1840 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1841 strFile.setNull();
1842 }
1843
1844 setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mVideoCaptureFile = strFile;
1847
1848 return S_OK;
1849}
1850
1851STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1852{
1853 AutoCaller autoCaller(this);
1854 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1855
1856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1857 *aHorzRes = mHWData->mVideoCaptureWidth;
1858 return S_OK;
1859}
1860
1861STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1862{
1863 AutoCaller autoCaller(this);
1864 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1865
1866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1867
1868 if ( Global::IsOnline(mData->mMachineState)
1869 && mHWData->mVideoCaptureEnabled)
1870 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1871
1872 setModified(IsModified_MachineData);
1873 mHWData.backup();
1874 mHWData->mVideoCaptureWidth = aHorzRes;
1875
1876 return S_OK;
1877}
1878
1879STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1880{
1881 AutoCaller autoCaller(this);
1882 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1883
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885 *aVertRes = mHWData->mVideoCaptureHeight;
1886 return S_OK;
1887}
1888
1889STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1890{
1891 AutoCaller autoCaller(this);
1892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1893
1894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1895
1896 if ( Global::IsOnline(mData->mMachineState)
1897 && mHWData->mVideoCaptureEnabled)
1898 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1899
1900 setModified(IsModified_MachineData);
1901 mHWData.backup();
1902 mHWData->mVideoCaptureHeight = aVertRes;
1903
1904 return S_OK;
1905}
1906
1907STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1908{
1909 AutoCaller autoCaller(this);
1910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1911
1912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1913 *aRate = mHWData->mVideoCaptureRate;
1914 return S_OK;
1915}
1916
1917STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1918{
1919 AutoCaller autoCaller(this);
1920 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1921
1922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1923
1924 if ( Global::IsOnline(mData->mMachineState)
1925 && mHWData->mVideoCaptureEnabled)
1926 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1927
1928 setModified(IsModified_MachineData);
1929 mHWData.backup();
1930 mHWData->mVideoCaptureRate = aRate;
1931
1932 return S_OK;
1933}
1934
1935STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1936{
1937 AutoCaller autoCaller(this);
1938 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1939
1940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1941 *aFPS = mHWData->mVideoCaptureFPS;
1942 return S_OK;
1943}
1944
1945STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1946{
1947 AutoCaller autoCaller(this);
1948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1949
1950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1951
1952 if ( Global::IsOnline(mData->mMachineState)
1953 && mHWData->mVideoCaptureEnabled)
1954 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1955
1956 setModified(IsModified_MachineData);
1957 mHWData.backup();
1958 mHWData->mVideoCaptureFPS = aFPS;
1959
1960 return S_OK;
1961}
1962
1963STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1964{
1965 CheckComArgOutPointerValid(aGraphicsControllerType);
1966
1967 AutoCaller autoCaller(this);
1968 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1969
1970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1971
1972 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1973
1974 return S_OK;
1975}
1976
1977STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1978{
1979 switch (aGraphicsControllerType)
1980 {
1981 case GraphicsControllerType_Null:
1982 case GraphicsControllerType_VBoxVGA:
1983 break;
1984 default:
1985 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1986 }
1987
1988 AutoCaller autoCaller(this);
1989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1990
1991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 HRESULT rc = checkStateDependency(MutableStateDep);
1994 if (FAILED(rc)) return rc;
1995
1996 setModified(IsModified_MachineData);
1997 mHWData.backup();
1998 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1999
2000 return S_OK;
2001}
2002
2003STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
2004{
2005 CheckComArgOutPointerValid(memorySize);
2006
2007 AutoCaller autoCaller(this);
2008 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2009
2010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2011
2012 *memorySize = mHWData->mVRAMSize;
2013
2014 return S_OK;
2015}
2016
2017STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
2018{
2019 /* check VRAM limits */
2020 if (memorySize < SchemaDefs::MinGuestVRAM ||
2021 memorySize > SchemaDefs::MaxGuestVRAM)
2022 return setError(E_INVALIDARG,
2023 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2024 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2025
2026 AutoCaller autoCaller(this);
2027 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2028
2029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2030
2031 HRESULT rc = checkStateDependency(MutableStateDep);
2032 if (FAILED(rc)) return rc;
2033
2034 setModified(IsModified_MachineData);
2035 mHWData.backup();
2036 mHWData->mVRAMSize = memorySize;
2037
2038 return S_OK;
2039}
2040
2041/** @todo this method should not be public */
2042STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2043{
2044 CheckComArgOutPointerValid(memoryBalloonSize);
2045
2046 AutoCaller autoCaller(this);
2047 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2048
2049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2050
2051 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2052
2053 return S_OK;
2054}
2055
2056/**
2057 * Set the memory balloon size.
2058 *
2059 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2060 * we have to make sure that we never call IGuest from here.
2061 */
2062STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2063{
2064 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2065#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2066 /* check limits */
2067 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2068 return setError(E_INVALIDARG,
2069 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2070 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2071
2072 AutoCaller autoCaller(this);
2073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2074
2075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2076
2077 setModified(IsModified_MachineData);
2078 mHWData.backup();
2079 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2080
2081 return S_OK;
2082#else
2083 NOREF(memoryBalloonSize);
2084 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2085#endif
2086}
2087
2088STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2089{
2090 CheckComArgOutPointerValid(aEnabled);
2091
2092 AutoCaller autoCaller(this);
2093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2094
2095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2096
2097 *aEnabled = mHWData->mPageFusionEnabled;
2098 return S_OK;
2099}
2100
2101STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2102{
2103#ifdef VBOX_WITH_PAGE_SHARING
2104 AutoCaller autoCaller(this);
2105 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2106
2107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2108
2109 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2110 setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mPageFusionEnabled = aEnabled;
2113 return S_OK;
2114#else
2115 NOREF(aEnabled);
2116 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2117#endif
2118}
2119
2120STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2121{
2122 CheckComArgOutPointerValid(aEnabled);
2123
2124 AutoCaller autoCaller(this);
2125 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2126
2127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2128
2129 *aEnabled = mHWData->mAccelerate3DEnabled;
2130
2131 return S_OK;
2132}
2133
2134STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2135{
2136 AutoCaller autoCaller(this);
2137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2138
2139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2140
2141 HRESULT rc = checkStateDependency(MutableStateDep);
2142 if (FAILED(rc)) return rc;
2143
2144 /** @todo check validity! */
2145
2146 setModified(IsModified_MachineData);
2147 mHWData.backup();
2148 mHWData->mAccelerate3DEnabled = enable;
2149
2150 return S_OK;
2151}
2152
2153
2154STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2155{
2156 CheckComArgOutPointerValid(aEnabled);
2157
2158 AutoCaller autoCaller(this);
2159 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2160
2161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2162
2163 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2164
2165 return S_OK;
2166}
2167
2168STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2169{
2170 AutoCaller autoCaller(this);
2171 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2172
2173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2174
2175 HRESULT rc = checkStateDependency(MutableStateDep);
2176 if (FAILED(rc)) return rc;
2177
2178 /** @todo check validity! */
2179
2180 setModified(IsModified_MachineData);
2181 mHWData.backup();
2182 mHWData->mAccelerate2DVideoEnabled = enable;
2183
2184 return S_OK;
2185}
2186
2187STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2188{
2189 CheckComArgOutPointerValid(monitorCount);
2190
2191 AutoCaller autoCaller(this);
2192 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2193
2194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2195
2196 *monitorCount = mHWData->mMonitorCount;
2197
2198 return S_OK;
2199}
2200
2201STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2202{
2203 /* make sure monitor count is a sensible number */
2204 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2205 return setError(E_INVALIDARG,
2206 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2207 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2208
2209 AutoCaller autoCaller(this);
2210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2211
2212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2213
2214 HRESULT rc = checkStateDependency(MutableStateDep);
2215 if (FAILED(rc)) return rc;
2216
2217 setModified(IsModified_MachineData);
2218 mHWData.backup();
2219 mHWData->mMonitorCount = monitorCount;
2220
2221 return S_OK;
2222}
2223
2224STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2225{
2226 CheckComArgOutPointerValid(biosSettings);
2227
2228 AutoCaller autoCaller(this);
2229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2230
2231 /* mBIOSSettings is constant during life time, no need to lock */
2232 mBIOSSettings.queryInterfaceTo(biosSettings);
2233
2234 return S_OK;
2235}
2236
2237STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2238{
2239 CheckComArgOutPointerValid(aVal);
2240
2241 AutoCaller autoCaller(this);
2242 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2243
2244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2245
2246 switch (property)
2247 {
2248 case CPUPropertyType_PAE:
2249 *aVal = mHWData->mPAEEnabled;
2250 break;
2251
2252 case CPUPropertyType_Synthetic:
2253 *aVal = mHWData->mSyntheticCpu;
2254 break;
2255
2256 case CPUPropertyType_LongMode:
2257 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2258 *aVal = TRUE;
2259 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2260 *aVal = FALSE;
2261#if HC_ARCH_BITS == 64
2262 else
2263 *aVal = TRUE;
2264#else
2265 else
2266 {
2267 *aVal = FALSE;
2268
2269 ComPtr<IGuestOSType> ptrGuestOSType;
2270 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2271 if (SUCCEEDED(hrc2))
2272 {
2273 BOOL fIs64Bit = FALSE;
2274 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2275 if (SUCCEEDED(hrc2) && fIs64Bit)
2276 {
2277 ComObjPtr<Host> ptrHost = mParent->host();
2278 alock.release();
2279
2280 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2281 if (FAILED(hrc2))
2282 *aVal = FALSE;
2283 }
2284 }
2285 }
2286#endif
2287 break;
2288
2289 default:
2290 return E_INVALIDARG;
2291 }
2292 return S_OK;
2293}
2294
2295STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2296{
2297 AutoCaller autoCaller(this);
2298 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2299
2300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2301
2302 HRESULT rc = checkStateDependency(MutableStateDep);
2303 if (FAILED(rc)) return rc;
2304
2305 switch (property)
2306 {
2307 case CPUPropertyType_PAE:
2308 setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mPAEEnabled = !!aVal;
2311 break;
2312
2313 case CPUPropertyType_Synthetic:
2314 setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mSyntheticCpu = !!aVal;
2317 break;
2318
2319 case CPUPropertyType_LongMode:
2320 setModified(IsModified_MachineData);
2321 mHWData.backup();
2322 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2323 break;
2324
2325 default:
2326 return E_INVALIDARG;
2327 }
2328 return S_OK;
2329}
2330
2331STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2332{
2333 CheckComArgOutPointerValid(aValEax);
2334 CheckComArgOutPointerValid(aValEbx);
2335 CheckComArgOutPointerValid(aValEcx);
2336 CheckComArgOutPointerValid(aValEdx);
2337
2338 AutoCaller autoCaller(this);
2339 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2340
2341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2342
2343 switch(aId)
2344 {
2345 case 0x0:
2346 case 0x1:
2347 case 0x2:
2348 case 0x3:
2349 case 0x4:
2350 case 0x5:
2351 case 0x6:
2352 case 0x7:
2353 case 0x8:
2354 case 0x9:
2355 case 0xA:
2356 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2357 return E_INVALIDARG;
2358
2359 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2360 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2361 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2362 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2363 break;
2364
2365 case 0x80000000:
2366 case 0x80000001:
2367 case 0x80000002:
2368 case 0x80000003:
2369 case 0x80000004:
2370 case 0x80000005:
2371 case 0x80000006:
2372 case 0x80000007:
2373 case 0x80000008:
2374 case 0x80000009:
2375 case 0x8000000A:
2376 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2377 return E_INVALIDARG;
2378
2379 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2380 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2381 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2382 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2383 break;
2384
2385 default:
2386 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2387 }
2388 return S_OK;
2389}
2390
2391STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2392{
2393 AutoCaller autoCaller(this);
2394 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2395
2396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2397
2398 HRESULT rc = checkStateDependency(MutableStateDep);
2399 if (FAILED(rc)) return rc;
2400
2401 switch(aId)
2402 {
2403 case 0x0:
2404 case 0x1:
2405 case 0x2:
2406 case 0x3:
2407 case 0x4:
2408 case 0x5:
2409 case 0x6:
2410 case 0x7:
2411 case 0x8:
2412 case 0x9:
2413 case 0xA:
2414 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2415 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2416 setModified(IsModified_MachineData);
2417 mHWData.backup();
2418 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2419 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2420 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2421 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2422 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2423 break;
2424
2425 case 0x80000000:
2426 case 0x80000001:
2427 case 0x80000002:
2428 case 0x80000003:
2429 case 0x80000004:
2430 case 0x80000005:
2431 case 0x80000006:
2432 case 0x80000007:
2433 case 0x80000008:
2434 case 0x80000009:
2435 case 0x8000000A:
2436 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2437 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2438 setModified(IsModified_MachineData);
2439 mHWData.backup();
2440 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2441 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2442 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2443 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2444 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2445 break;
2446
2447 default:
2448 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2449 }
2450 return S_OK;
2451}
2452
2453STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2454{
2455 AutoCaller autoCaller(this);
2456 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2457
2458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2459
2460 HRESULT rc = checkStateDependency(MutableStateDep);
2461 if (FAILED(rc)) return rc;
2462
2463 switch(aId)
2464 {
2465 case 0x0:
2466 case 0x1:
2467 case 0x2:
2468 case 0x3:
2469 case 0x4:
2470 case 0x5:
2471 case 0x6:
2472 case 0x7:
2473 case 0x8:
2474 case 0x9:
2475 case 0xA:
2476 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2477 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2478 setModified(IsModified_MachineData);
2479 mHWData.backup();
2480 /* Invalidate leaf. */
2481 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2482 break;
2483
2484 case 0x80000000:
2485 case 0x80000001:
2486 case 0x80000002:
2487 case 0x80000003:
2488 case 0x80000004:
2489 case 0x80000005:
2490 case 0x80000006:
2491 case 0x80000007:
2492 case 0x80000008:
2493 case 0x80000009:
2494 case 0x8000000A:
2495 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2496 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2497 setModified(IsModified_MachineData);
2498 mHWData.backup();
2499 /* Invalidate leaf. */
2500 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2501 break;
2502
2503 default:
2504 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2505 }
2506 return S_OK;
2507}
2508
2509STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2510{
2511 AutoCaller autoCaller(this);
2512 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2513
2514 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2515
2516 HRESULT rc = checkStateDependency(MutableStateDep);
2517 if (FAILED(rc)) return rc;
2518
2519 setModified(IsModified_MachineData);
2520 mHWData.backup();
2521
2522 /* Invalidate all standard leafs. */
2523 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2524 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2525
2526 /* Invalidate all extended leafs. */
2527 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2528 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2529
2530 return S_OK;
2531}
2532
2533STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2534{
2535 CheckComArgOutPointerValid(aVal);
2536
2537 AutoCaller autoCaller(this);
2538 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2539
2540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2541
2542 switch(property)
2543 {
2544 case HWVirtExPropertyType_Enabled:
2545 *aVal = mHWData->mHWVirtExEnabled;
2546 break;
2547
2548 case HWVirtExPropertyType_Exclusive:
2549 *aVal = mHWData->mHWVirtExExclusive;
2550 break;
2551
2552 case HWVirtExPropertyType_VPID:
2553 *aVal = mHWData->mHWVirtExVPIDEnabled;
2554 break;
2555
2556 case HWVirtExPropertyType_NestedPaging:
2557 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2558 break;
2559
2560 case HWVirtExPropertyType_UnrestrictedExecution:
2561 *aVal = mHWData->mHWVirtExUXEnabled;
2562 break;
2563
2564 case HWVirtExPropertyType_LargePages:
2565 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2566#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2567 *aVal = FALSE;
2568#endif
2569 break;
2570
2571 case HWVirtExPropertyType_Force:
2572 *aVal = mHWData->mHWVirtExForceEnabled;
2573 break;
2574
2575 default:
2576 return E_INVALIDARG;
2577 }
2578 return S_OK;
2579}
2580
2581STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2582{
2583 AutoCaller autoCaller(this);
2584 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2585
2586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2587
2588 HRESULT rc = checkStateDependency(MutableStateDep);
2589 if (FAILED(rc)) return rc;
2590
2591 switch(property)
2592 {
2593 case HWVirtExPropertyType_Enabled:
2594 setModified(IsModified_MachineData);
2595 mHWData.backup();
2596 mHWData->mHWVirtExEnabled = !!aVal;
2597 break;
2598
2599 case HWVirtExPropertyType_Exclusive:
2600 setModified(IsModified_MachineData);
2601 mHWData.backup();
2602 mHWData->mHWVirtExExclusive = !!aVal;
2603 break;
2604
2605 case HWVirtExPropertyType_VPID:
2606 setModified(IsModified_MachineData);
2607 mHWData.backup();
2608 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2609 break;
2610
2611 case HWVirtExPropertyType_NestedPaging:
2612 setModified(IsModified_MachineData);
2613 mHWData.backup();
2614 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2615 break;
2616
2617 case HWVirtExPropertyType_UnrestrictedExecution:
2618 setModified(IsModified_MachineData);
2619 mHWData.backup();
2620 mHWData->mHWVirtExUXEnabled = !!aVal;
2621 break;
2622
2623 case HWVirtExPropertyType_LargePages:
2624 setModified(IsModified_MachineData);
2625 mHWData.backup();
2626 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2627 break;
2628
2629 case HWVirtExPropertyType_Force:
2630 setModified(IsModified_MachineData);
2631 mHWData.backup();
2632 mHWData->mHWVirtExForceEnabled = !!aVal;
2633 break;
2634
2635 default:
2636 return E_INVALIDARG;
2637 }
2638
2639 return S_OK;
2640}
2641
2642STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2643{
2644 CheckComArgOutPointerValid(aSnapshotFolder);
2645
2646 AutoCaller autoCaller(this);
2647 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2648
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 Utf8Str strFullSnapshotFolder;
2652 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2653 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2654
2655 return S_OK;
2656}
2657
2658STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2659{
2660 /* @todo (r=dmik):
2661 * 1. Allow to change the name of the snapshot folder containing snapshots
2662 * 2. Rename the folder on disk instead of just changing the property
2663 * value (to be smart and not to leave garbage). Note that it cannot be
2664 * done here because the change may be rolled back. Thus, the right
2665 * place is #saveSettings().
2666 */
2667
2668 AutoCaller autoCaller(this);
2669 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2670
2671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2672
2673 HRESULT rc = checkStateDependency(MutableStateDep);
2674 if (FAILED(rc)) return rc;
2675
2676 if (!mData->mCurrentSnapshot.isNull())
2677 return setError(E_FAIL,
2678 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2679
2680 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2681
2682 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2683 if (strSnapshotFolder.isEmpty())
2684 strSnapshotFolder = "Snapshots";
2685 int vrc = calculateFullPath(strSnapshotFolder,
2686 strSnapshotFolder);
2687 if (RT_FAILURE(vrc))
2688 return setError(E_FAIL,
2689 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2690 aSnapshotFolder, vrc);
2691
2692 setModified(IsModified_MachineData);
2693 mUserData.backup();
2694
2695 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2696
2697 return S_OK;
2698}
2699
2700STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2701{
2702 CheckComArgOutSafeArrayPointerValid(aAttachments);
2703
2704 AutoCaller autoCaller(this);
2705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2706
2707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2708
2709 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2710 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2711
2712 return S_OK;
2713}
2714
2715STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2716{
2717 CheckComArgOutPointerValid(vrdeServer);
2718
2719 AutoCaller autoCaller(this);
2720 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2721
2722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2723
2724 Assert(!!mVRDEServer);
2725 mVRDEServer.queryInterfaceTo(vrdeServer);
2726
2727 return S_OK;
2728}
2729
2730STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2731{
2732 CheckComArgOutPointerValid(audioAdapter);
2733
2734 AutoCaller autoCaller(this);
2735 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2736
2737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2738
2739 mAudioAdapter.queryInterfaceTo(audioAdapter);
2740 return S_OK;
2741}
2742
2743STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2744{
2745#ifdef VBOX_WITH_VUSB
2746 CheckComArgOutPointerValid(aUSBController);
2747
2748 AutoCaller autoCaller(this);
2749 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2750
2751 clearError();
2752 MultiResult rc(S_OK);
2753
2754# ifdef VBOX_WITH_USB
2755 rc = mParent->host()->checkUSBProxyService();
2756 if (FAILED(rc)) return rc;
2757# endif
2758
2759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2760
2761 return rc = mUSBController.queryInterfaceTo(aUSBController);
2762#else
2763 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2764 * extended error info to indicate that USB is simply not available
2765 * (w/o treating it as a failure), for example, as in OSE */
2766 NOREF(aUSBController);
2767 ReturnComNotImplemented();
2768#endif /* VBOX_WITH_VUSB */
2769}
2770
2771STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2772{
2773 CheckComArgOutPointerValid(aFilePath);
2774
2775 AutoLimitedCaller autoCaller(this);
2776 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2777
2778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2779
2780 mData->m_strConfigFileFull.cloneTo(aFilePath);
2781 return S_OK;
2782}
2783
2784STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2785{
2786 CheckComArgOutPointerValid(aModified);
2787
2788 AutoCaller autoCaller(this);
2789 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2790
2791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2792
2793 HRESULT rc = checkStateDependency(MutableStateDep);
2794 if (FAILED(rc)) return rc;
2795
2796 if (!mData->pMachineConfigFile->fileExists())
2797 // this is a new machine, and no config file exists yet:
2798 *aModified = TRUE;
2799 else
2800 *aModified = (mData->flModifications != 0);
2801
2802 return S_OK;
2803}
2804
2805STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2806{
2807 CheckComArgOutPointerValid(aSessionState);
2808
2809 AutoCaller autoCaller(this);
2810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2811
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 *aSessionState = mData->mSession.mState;
2815
2816 return S_OK;
2817}
2818
2819STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2820{
2821 CheckComArgOutPointerValid(aSessionType);
2822
2823 AutoCaller autoCaller(this);
2824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2825
2826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 mData->mSession.mType.cloneTo(aSessionType);
2829
2830 return S_OK;
2831}
2832
2833STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2834{
2835 CheckComArgOutPointerValid(aSessionPID);
2836
2837 AutoCaller autoCaller(this);
2838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2839
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 *aSessionPID = mData->mSession.mPID;
2843
2844 return S_OK;
2845}
2846
2847STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2848{
2849 CheckComArgOutPointerValid(machineState);
2850
2851 AutoCaller autoCaller(this);
2852 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2853
2854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 *machineState = mData->mMachineState;
2857
2858 return S_OK;
2859}
2860
2861STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2862{
2863 CheckComArgOutPointerValid(aLastStateChange);
2864
2865 AutoCaller autoCaller(this);
2866 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2867
2868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2871
2872 return S_OK;
2873}
2874
2875STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2876{
2877 CheckComArgOutPointerValid(aStateFilePath);
2878
2879 AutoCaller autoCaller(this);
2880 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2881
2882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2883
2884 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2885
2886 return S_OK;
2887}
2888
2889STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2890{
2891 CheckComArgOutPointerValid(aLogFolder);
2892
2893 AutoCaller autoCaller(this);
2894 AssertComRCReturnRC(autoCaller.rc());
2895
2896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2897
2898 Utf8Str logFolder;
2899 getLogFolder(logFolder);
2900 logFolder.cloneTo(aLogFolder);
2901
2902 return S_OK;
2903}
2904
2905STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2906{
2907 CheckComArgOutPointerValid(aCurrentSnapshot);
2908
2909 AutoCaller autoCaller(this);
2910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2911
2912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2913
2914 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2915
2916 return S_OK;
2917}
2918
2919STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2920{
2921 CheckComArgOutPointerValid(aSnapshotCount);
2922
2923 AutoCaller autoCaller(this);
2924 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2925
2926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2927
2928 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2929 ? 0
2930 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2931
2932 return S_OK;
2933}
2934
2935STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2936{
2937 CheckComArgOutPointerValid(aCurrentStateModified);
2938
2939 AutoCaller autoCaller(this);
2940 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2941
2942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2943
2944 /* Note: for machines with no snapshots, we always return FALSE
2945 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2946 * reasons :) */
2947
2948 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2949 ? FALSE
2950 : mData->mCurrentStateModified;
2951
2952 return S_OK;
2953}
2954
2955STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2956{
2957 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2958
2959 AutoCaller autoCaller(this);
2960 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2961
2962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2963
2964 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2965 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2966
2967 return S_OK;
2968}
2969
2970STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2971{
2972 CheckComArgOutPointerValid(aClipboardMode);
2973
2974 AutoCaller autoCaller(this);
2975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2976
2977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2978
2979 *aClipboardMode = mHWData->mClipboardMode;
2980
2981 return S_OK;
2982}
2983
2984STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2985{
2986 HRESULT rc = S_OK;
2987
2988 AutoCaller autoCaller(this);
2989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2990
2991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2992
2993 alock.release();
2994 rc = onClipboardModeChange(aClipboardMode);
2995 alock.acquire();
2996 if (FAILED(rc)) return rc;
2997
2998 setModified(IsModified_MachineData);
2999 mHWData.backup();
3000 mHWData->mClipboardMode = aClipboardMode;
3001
3002 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3003 if (Global::IsOnline(mData->mMachineState))
3004 saveSettings(NULL);
3005
3006 return S_OK;
3007}
3008
3009STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
3010{
3011 CheckComArgOutPointerValid(aDragAndDropMode);
3012
3013 AutoCaller autoCaller(this);
3014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3015
3016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3017
3018 *aDragAndDropMode = mHWData->mDragAndDropMode;
3019
3020 return S_OK;
3021}
3022
3023STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3024{
3025 HRESULT rc = S_OK;
3026
3027 AutoCaller autoCaller(this);
3028 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3029
3030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3031
3032 alock.release();
3033 rc = onDragAndDropModeChange(aDragAndDropMode);
3034 alock.acquire();
3035 if (FAILED(rc)) return rc;
3036
3037 setModified(IsModified_MachineData);
3038 mHWData.backup();
3039 mHWData->mDragAndDropMode = aDragAndDropMode;
3040
3041 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3042 if (Global::IsOnline(mData->mMachineState))
3043 saveSettings(NULL);
3044
3045 return S_OK;
3046}
3047
3048STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3049{
3050 CheckComArgOutPointerValid(aPatterns);
3051
3052 AutoCaller autoCaller(this);
3053 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3054
3055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3056
3057 try
3058 {
3059 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3060 }
3061 catch (...)
3062 {
3063 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3064 }
3065
3066 return S_OK;
3067}
3068
3069STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3070{
3071 AutoCaller autoCaller(this);
3072 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3073
3074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3075
3076 HRESULT rc = checkStateDependency(MutableStateDep);
3077 if (FAILED(rc)) return rc;
3078
3079 setModified(IsModified_MachineData);
3080 mHWData.backup();
3081 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3082 return rc;
3083}
3084
3085STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3086{
3087 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3088
3089 AutoCaller autoCaller(this);
3090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3091
3092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3093
3094 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3095 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3096
3097 return S_OK;
3098}
3099
3100STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3101{
3102 CheckComArgOutPointerValid(aEnabled);
3103
3104 AutoCaller autoCaller(this);
3105 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3106
3107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3108
3109 *aEnabled = mUserData->s.fTeleporterEnabled;
3110
3111 return S_OK;
3112}
3113
3114STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3115{
3116 AutoCaller autoCaller(this);
3117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3118
3119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3120
3121 /* Only allow it to be set to true when PoweredOff or Aborted.
3122 (Clearing it is always permitted.) */
3123 if ( aEnabled
3124 && mData->mRegistered
3125 && ( !isSessionMachine()
3126 || ( mData->mMachineState != MachineState_PoweredOff
3127 && mData->mMachineState != MachineState_Teleported
3128 && mData->mMachineState != MachineState_Aborted
3129 )
3130 )
3131 )
3132 return setError(VBOX_E_INVALID_VM_STATE,
3133 tr("The machine is not powered off (state is %s)"),
3134 Global::stringifyMachineState(mData->mMachineState));
3135
3136 setModified(IsModified_MachineData);
3137 mUserData.backup();
3138 mUserData->s.fTeleporterEnabled = !!aEnabled;
3139
3140 return S_OK;
3141}
3142
3143STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3144{
3145 CheckComArgOutPointerValid(aPort);
3146
3147 AutoCaller autoCaller(this);
3148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3149
3150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3151
3152 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3153
3154 return S_OK;
3155}
3156
3157STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3158{
3159 if (aPort >= _64K)
3160 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3161
3162 AutoCaller autoCaller(this);
3163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3164
3165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3166
3167 HRESULT rc = checkStateDependency(MutableStateDep);
3168 if (FAILED(rc)) return rc;
3169
3170 setModified(IsModified_MachineData);
3171 mUserData.backup();
3172 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3173
3174 return S_OK;
3175}
3176
3177STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3178{
3179 CheckComArgOutPointerValid(aAddress);
3180
3181 AutoCaller autoCaller(this);
3182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3183
3184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3185
3186 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3187
3188 return S_OK;
3189}
3190
3191STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3192{
3193 AutoCaller autoCaller(this);
3194 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3195
3196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3197
3198 HRESULT rc = checkStateDependency(MutableStateDep);
3199 if (FAILED(rc)) return rc;
3200
3201 setModified(IsModified_MachineData);
3202 mUserData.backup();
3203 mUserData->s.strTeleporterAddress = aAddress;
3204
3205 return S_OK;
3206}
3207
3208STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3209{
3210 CheckComArgOutPointerValid(aPassword);
3211
3212 AutoCaller autoCaller(this);
3213 HRESULT hrc = autoCaller.rc();
3214 if (SUCCEEDED(hrc))
3215 {
3216 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3217 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3218 }
3219
3220 return hrc;
3221}
3222
3223STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3224{
3225 /*
3226 * Hash the password first.
3227 */
3228 Utf8Str strPassword(aPassword);
3229 if (!strPassword.isEmpty())
3230 {
3231 if (VBoxIsPasswordHashed(&strPassword))
3232 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3233 VBoxHashPassword(&strPassword);
3234 }
3235
3236 /*
3237 * Do the update.
3238 */
3239 AutoCaller autoCaller(this);
3240 HRESULT hrc = autoCaller.rc();
3241 if (SUCCEEDED(hrc))
3242 {
3243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3244 hrc = checkStateDependency(MutableStateDep);
3245 if (SUCCEEDED(hrc))
3246 {
3247 setModified(IsModified_MachineData);
3248 mUserData.backup();
3249 mUserData->s.strTeleporterPassword = strPassword;
3250 }
3251 }
3252
3253 return hrc;
3254}
3255
3256STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3257{
3258 CheckComArgOutPointerValid(aState);
3259
3260 AutoCaller autoCaller(this);
3261 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3262
3263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3264
3265 *aState = mUserData->s.enmFaultToleranceState;
3266 return S_OK;
3267}
3268
3269STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3270{
3271 AutoCaller autoCaller(this);
3272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3273
3274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3275
3276 /* @todo deal with running state change. */
3277 HRESULT rc = checkStateDependency(MutableStateDep);
3278 if (FAILED(rc)) return rc;
3279
3280 setModified(IsModified_MachineData);
3281 mUserData.backup();
3282 mUserData->s.enmFaultToleranceState = aState;
3283 return S_OK;
3284}
3285
3286STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3287{
3288 CheckComArgOutPointerValid(aAddress);
3289
3290 AutoCaller autoCaller(this);
3291 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3292
3293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3294
3295 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3296 return S_OK;
3297}
3298
3299STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3300{
3301 AutoCaller autoCaller(this);
3302 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3303
3304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3305
3306 /* @todo deal with running state change. */
3307 HRESULT rc = checkStateDependency(MutableStateDep);
3308 if (FAILED(rc)) return rc;
3309
3310 setModified(IsModified_MachineData);
3311 mUserData.backup();
3312 mUserData->s.strFaultToleranceAddress = aAddress;
3313 return S_OK;
3314}
3315
3316STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3317{
3318 CheckComArgOutPointerValid(aPort);
3319
3320 AutoCaller autoCaller(this);
3321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3322
3323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3324
3325 *aPort = mUserData->s.uFaultTolerancePort;
3326 return S_OK;
3327}
3328
3329STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3330{
3331 AutoCaller autoCaller(this);
3332 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3333
3334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3335
3336 /* @todo deal with running state change. */
3337 HRESULT rc = checkStateDependency(MutableStateDep);
3338 if (FAILED(rc)) return rc;
3339
3340 setModified(IsModified_MachineData);
3341 mUserData.backup();
3342 mUserData->s.uFaultTolerancePort = aPort;
3343 return S_OK;
3344}
3345
3346STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3347{
3348 CheckComArgOutPointerValid(aPassword);
3349
3350 AutoCaller autoCaller(this);
3351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3352
3353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3354
3355 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3356
3357 return S_OK;
3358}
3359
3360STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3361{
3362 AutoCaller autoCaller(this);
3363 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3364
3365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3366
3367 /* @todo deal with running state change. */
3368 HRESULT rc = checkStateDependency(MutableStateDep);
3369 if (FAILED(rc)) return rc;
3370
3371 setModified(IsModified_MachineData);
3372 mUserData.backup();
3373 mUserData->s.strFaultTolerancePassword = aPassword;
3374
3375 return S_OK;
3376}
3377
3378STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3379{
3380 CheckComArgOutPointerValid(aInterval);
3381
3382 AutoCaller autoCaller(this);
3383 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3384
3385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3386
3387 *aInterval = mUserData->s.uFaultToleranceInterval;
3388 return S_OK;
3389}
3390
3391STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3392{
3393 AutoCaller autoCaller(this);
3394 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3395
3396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3397
3398 /* @todo deal with running state change. */
3399 HRESULT rc = checkStateDependency(MutableStateDep);
3400 if (FAILED(rc)) return rc;
3401
3402 setModified(IsModified_MachineData);
3403 mUserData.backup();
3404 mUserData->s.uFaultToleranceInterval = aInterval;
3405 return S_OK;
3406}
3407
3408STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3409{
3410 CheckComArgOutPointerValid(aEnabled);
3411
3412 AutoCaller autoCaller(this);
3413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3414
3415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3416
3417 *aEnabled = mUserData->s.fRTCUseUTC;
3418
3419 return S_OK;
3420}
3421
3422STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3423{
3424 AutoCaller autoCaller(this);
3425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3426
3427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3428
3429 /* Only allow it to be set to true when PoweredOff or Aborted.
3430 (Clearing it is always permitted.) */
3431 if ( aEnabled
3432 && mData->mRegistered
3433 && ( !isSessionMachine()
3434 || ( mData->mMachineState != MachineState_PoweredOff
3435 && mData->mMachineState != MachineState_Teleported
3436 && mData->mMachineState != MachineState_Aborted
3437 )
3438 )
3439 )
3440 return setError(VBOX_E_INVALID_VM_STATE,
3441 tr("The machine is not powered off (state is %s)"),
3442 Global::stringifyMachineState(mData->mMachineState));
3443
3444 setModified(IsModified_MachineData);
3445 mUserData.backup();
3446 mUserData->s.fRTCUseUTC = !!aEnabled;
3447
3448 return S_OK;
3449}
3450
3451STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3452{
3453 CheckComArgOutPointerValid(aEnabled);
3454
3455 AutoCaller autoCaller(this);
3456 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3457
3458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3459
3460 *aEnabled = mHWData->mIOCacheEnabled;
3461
3462 return S_OK;
3463}
3464
3465STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3466{
3467 AutoCaller autoCaller(this);
3468 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3469
3470 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3471
3472 HRESULT rc = checkStateDependency(MutableStateDep);
3473 if (FAILED(rc)) return rc;
3474
3475 setModified(IsModified_MachineData);
3476 mHWData.backup();
3477 mHWData->mIOCacheEnabled = aEnabled;
3478
3479 return S_OK;
3480}
3481
3482STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3483{
3484 CheckComArgOutPointerValid(aIOCacheSize);
3485
3486 AutoCaller autoCaller(this);
3487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3488
3489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3490
3491 *aIOCacheSize = mHWData->mIOCacheSize;
3492
3493 return S_OK;
3494}
3495
3496STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3497{
3498 AutoCaller autoCaller(this);
3499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3500
3501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3502
3503 HRESULT rc = checkStateDependency(MutableStateDep);
3504 if (FAILED(rc)) return rc;
3505
3506 setModified(IsModified_MachineData);
3507 mHWData.backup();
3508 mHWData->mIOCacheSize = aIOCacheSize;
3509
3510 return S_OK;
3511}
3512
3513
3514/**
3515 * @note Locks objects!
3516 */
3517STDMETHODIMP Machine::LockMachine(ISession *aSession,
3518 LockType_T lockType)
3519{
3520 CheckComArgNotNull(aSession);
3521
3522 AutoCaller autoCaller(this);
3523 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3524
3525 /* check the session state */
3526 SessionState_T state;
3527 HRESULT rc = aSession->COMGETTER(State)(&state);
3528 if (FAILED(rc)) return rc;
3529
3530 if (state != SessionState_Unlocked)
3531 return setError(VBOX_E_INVALID_OBJECT_STATE,
3532 tr("The given session is busy"));
3533
3534 // get the client's IInternalSessionControl interface
3535 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3536 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3537 E_INVALIDARG);
3538
3539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3540
3541 if (!mData->mRegistered)
3542 return setError(E_UNEXPECTED,
3543 tr("The machine '%s' is not registered"),
3544 mUserData->s.strName.c_str());
3545
3546 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3547
3548 SessionState_T oldState = mData->mSession.mState;
3549 /* Hack: in case the session is closing and there is a progress object
3550 * which allows waiting for the session to be closed, take the opportunity
3551 * and do a limited wait (max. 1 second). This helps a lot when the system
3552 * is busy and thus session closing can take a little while. */
3553 if ( mData->mSession.mState == SessionState_Unlocking
3554 && mData->mSession.mProgress)
3555 {
3556 alock.release();
3557 mData->mSession.mProgress->WaitForCompletion(1000);
3558 alock.acquire();
3559 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3560 }
3561
3562 // try again now
3563 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3564 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3565 )
3566 {
3567 // OK, share the session... we are now dealing with three processes:
3568 // 1) VBoxSVC (where this code runs);
3569 // 2) process C: the caller's client process (who wants a shared session);
3570 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3571
3572 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3573 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3574 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3575 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3576 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3577
3578 /*
3579 * Release the lock before calling the client process. It's safe here
3580 * since the only thing to do after we get the lock again is to add
3581 * the remote control to the list (which doesn't directly influence
3582 * anything).
3583 */
3584 alock.release();
3585
3586 // get the console of the session holding the write lock (this is a remote call)
3587 ComPtr<IConsole> pConsoleW;
3588 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3589 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3590 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3591 if (FAILED(rc))
3592 // the failure may occur w/o any error info (from RPC), so provide one
3593 return setError(VBOX_E_VM_ERROR,
3594 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3595
3596 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3597
3598 // share the session machine and W's console with the caller's session
3599 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3600 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3601 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3602
3603 if (FAILED(rc))
3604 // the failure may occur w/o any error info (from RPC), so provide one
3605 return setError(VBOX_E_VM_ERROR,
3606 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3607 alock.acquire();
3608
3609 // need to revalidate the state after acquiring the lock again
3610 if (mData->mSession.mState != SessionState_Locked)
3611 {
3612 pSessionControl->Uninitialize();
3613 return setError(VBOX_E_INVALID_SESSION_STATE,
3614 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3615 mUserData->s.strName.c_str());
3616 }
3617
3618 // add the caller's session to the list
3619 mData->mSession.mRemoteControls.push_back(pSessionControl);
3620 }
3621 else if ( mData->mSession.mState == SessionState_Locked
3622 || mData->mSession.mState == SessionState_Unlocking
3623 )
3624 {
3625 // sharing not permitted, or machine still unlocking:
3626 return setError(VBOX_E_INVALID_OBJECT_STATE,
3627 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3628 mUserData->s.strName.c_str());
3629 }
3630 else
3631 {
3632 // machine is not locked: then write-lock the machine (create the session machine)
3633
3634 // must not be busy
3635 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3636
3637 // get the caller's session PID
3638 RTPROCESS pid = NIL_RTPROCESS;
3639 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3640 pSessionControl->GetPID((ULONG*)&pid);
3641 Assert(pid != NIL_RTPROCESS);
3642
3643 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3644
3645 if (fLaunchingVMProcess)
3646 {
3647 // this machine is awaiting for a spawning session to be opened:
3648 // then the calling process must be the one that got started by
3649 // LaunchVMProcess()
3650
3651 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3652 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3653
3654 if (mData->mSession.mPID != pid)
3655 return setError(E_ACCESSDENIED,
3656 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3657 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3658 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3659 }
3660
3661 // create the mutable SessionMachine from the current machine
3662 ComObjPtr<SessionMachine> sessionMachine;
3663 sessionMachine.createObject();
3664 rc = sessionMachine->init(this);
3665 AssertComRC(rc);
3666
3667 /* NOTE: doing return from this function after this point but
3668 * before the end is forbidden since it may call SessionMachine::uninit()
3669 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3670 * lock while still holding the Machine lock in alock so that a deadlock
3671 * is possible due to the wrong lock order. */
3672
3673 if (SUCCEEDED(rc))
3674 {
3675 /*
3676 * Set the session state to Spawning to protect against subsequent
3677 * attempts to open a session and to unregister the machine after
3678 * we release the lock.
3679 */
3680 SessionState_T origState = mData->mSession.mState;
3681 mData->mSession.mState = SessionState_Spawning;
3682
3683 /*
3684 * Release the lock before calling the client process -- it will call
3685 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3686 * because the state is Spawning, so that LaunchVMProcess() and
3687 * LockMachine() calls will fail. This method, called before we
3688 * acquire the lock again, will fail because of the wrong PID.
3689 *
3690 * Note that mData->mSession.mRemoteControls accessed outside
3691 * the lock may not be modified when state is Spawning, so it's safe.
3692 */
3693 alock.release();
3694
3695 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3696 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3697 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3698
3699 /* The failure may occur w/o any error info (from RPC), so provide one */
3700 if (FAILED(rc))
3701 setError(VBOX_E_VM_ERROR,
3702 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3703
3704 if ( SUCCEEDED(rc)
3705 && fLaunchingVMProcess
3706 )
3707 {
3708 /* complete the remote session initialization */
3709
3710 /* get the console from the direct session */
3711 ComPtr<IConsole> console;
3712 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3713 ComAssertComRC(rc);
3714
3715 if (SUCCEEDED(rc) && !console)
3716 {
3717 ComAssert(!!console);
3718 rc = E_FAIL;
3719 }
3720
3721 /* assign machine & console to the remote session */
3722 if (SUCCEEDED(rc))
3723 {
3724 /*
3725 * after LaunchVMProcess(), the first and the only
3726 * entry in remoteControls is that remote session
3727 */
3728 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3729 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3730 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3731
3732 /* The failure may occur w/o any error info (from RPC), so provide one */
3733 if (FAILED(rc))
3734 setError(VBOX_E_VM_ERROR,
3735 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3736 }
3737
3738 if (FAILED(rc))
3739 pSessionControl->Uninitialize();
3740 }
3741
3742 /* acquire the lock again */
3743 alock.acquire();
3744
3745 /* Restore the session state */
3746 mData->mSession.mState = origState;
3747 }
3748
3749 // finalize spawning anyway (this is why we don't return on errors above)
3750 if (fLaunchingVMProcess)
3751 {
3752 /* Note that the progress object is finalized later */
3753 /** @todo Consider checking mData->mSession.mProgress for cancellation
3754 * around here. */
3755
3756 /* We don't reset mSession.mPID here because it is necessary for
3757 * SessionMachine::uninit() to reap the child process later. */
3758
3759 if (FAILED(rc))
3760 {
3761 /* Close the remote session, remove the remote control from the list
3762 * and reset session state to Closed (@note keep the code in sync
3763 * with the relevant part in openSession()). */
3764
3765 Assert(mData->mSession.mRemoteControls.size() == 1);
3766 if (mData->mSession.mRemoteControls.size() == 1)
3767 {
3768 ErrorInfoKeeper eik;
3769 mData->mSession.mRemoteControls.front()->Uninitialize();
3770 }
3771
3772 mData->mSession.mRemoteControls.clear();
3773 mData->mSession.mState = SessionState_Unlocked;
3774 }
3775 }
3776 else
3777 {
3778 /* memorize PID of the directly opened session */
3779 if (SUCCEEDED(rc))
3780 mData->mSession.mPID = pid;
3781 }
3782
3783 if (SUCCEEDED(rc))
3784 {
3785 /* memorize the direct session control and cache IUnknown for it */
3786 mData->mSession.mDirectControl = pSessionControl;
3787 mData->mSession.mState = SessionState_Locked;
3788 /* associate the SessionMachine with this Machine */
3789 mData->mSession.mMachine = sessionMachine;
3790
3791 /* request an IUnknown pointer early from the remote party for later
3792 * identity checks (it will be internally cached within mDirectControl
3793 * at least on XPCOM) */
3794 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3795 NOREF(unk);
3796 }
3797
3798 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3799 * would break the lock order */
3800 alock.release();
3801
3802 /* uninitialize the created session machine on failure */
3803 if (FAILED(rc))
3804 sessionMachine->uninit();
3805
3806 }
3807
3808 if (SUCCEEDED(rc))
3809 {
3810 /*
3811 * tell the client watcher thread to update the set of
3812 * machines that have open sessions
3813 */
3814 mParent->updateClientWatcher();
3815
3816 if (oldState != SessionState_Locked)
3817 /* fire an event */
3818 mParent->onSessionStateChange(getId(), SessionState_Locked);
3819 }
3820
3821 return rc;
3822}
3823
3824/**
3825 * @note Locks objects!
3826 */
3827STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3828 IN_BSTR aFrontend,
3829 IN_BSTR aEnvironment,
3830 IProgress **aProgress)
3831{
3832 CheckComArgStr(aFrontend);
3833 Utf8Str strFrontend(aFrontend);
3834 Utf8Str strEnvironment(aEnvironment);
3835 /* "emergencystop" doesn't need the session, so skip the checks/interface
3836 * retrieval. This code doesn't quite fit in here, but introducing a
3837 * special API method would be even more effort, and would require explicit
3838 * support by every API client. It's better to hide the feature a bit. */
3839 if (strFrontend != "emergencystop")
3840 CheckComArgNotNull(aSession);
3841 CheckComArgOutPointerValid(aProgress);
3842
3843 AutoCaller autoCaller(this);
3844 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3845
3846 HRESULT rc = S_OK;
3847 if (strFrontend.isEmpty())
3848 {
3849 Bstr bstrFrontend;
3850 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3851 if (FAILED(rc))
3852 return rc;
3853 strFrontend = bstrFrontend;
3854 if (strFrontend.isEmpty())
3855 {
3856 ComPtr<ISystemProperties> systemProperties;
3857 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3858 if (FAILED(rc))
3859 return rc;
3860 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3861 if (FAILED(rc))
3862 return rc;
3863 strFrontend = bstrFrontend;
3864 }
3865 /* paranoia - emergencystop is not a valid default */
3866 if (strFrontend == "emergencystop")
3867 strFrontend = Utf8Str::Empty;
3868 }
3869 /* default frontend: Qt GUI */
3870 if (strFrontend.isEmpty())
3871 strFrontend = "GUI/Qt";
3872
3873 if (strFrontend != "emergencystop")
3874 {
3875 /* check the session state */
3876 SessionState_T state;
3877 rc = aSession->COMGETTER(State)(&state);
3878 if (FAILED(rc))
3879 return rc;
3880
3881 if (state != SessionState_Unlocked)
3882 return setError(VBOX_E_INVALID_OBJECT_STATE,
3883 tr("The given session is busy"));
3884
3885 /* get the IInternalSessionControl interface */
3886 ComPtr<IInternalSessionControl> control(aSession);
3887 ComAssertMsgRet(!control.isNull(),
3888 ("No IInternalSessionControl interface"),
3889 E_INVALIDARG);
3890
3891 /* get the teleporter enable state for the progress object init. */
3892 BOOL fTeleporterEnabled;
3893 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3894 if (FAILED(rc))
3895 return rc;
3896
3897 /* create a progress object */
3898 ComObjPtr<ProgressProxy> progress;
3899 progress.createObject();
3900 rc = progress->init(mParent,
3901 static_cast<IMachine*>(this),
3902 Bstr(tr("Starting VM")).raw(),
3903 TRUE /* aCancelable */,
3904 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3905 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3906 2 /* uFirstOperationWeight */,
3907 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3908
3909 if (SUCCEEDED(rc))
3910 {
3911 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3912 if (SUCCEEDED(rc))
3913 {
3914 progress.queryInterfaceTo(aProgress);
3915
3916 /* signal the client watcher thread */
3917 mParent->updateClientWatcher();
3918
3919 /* fire an event */
3920 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3921 }
3922 }
3923 }
3924 else
3925 {
3926 /* no progress object - either instant success or failure */
3927 *aProgress = NULL;
3928
3929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3930
3931 if (mData->mSession.mState != SessionState_Locked)
3932 return setError(VBOX_E_INVALID_OBJECT_STATE,
3933 tr("The machine '%s' is not locked by a session"),
3934 mUserData->s.strName.c_str());
3935
3936 /* must have a VM process associated - do not kill normal API clients
3937 * with an open session */
3938 if (!Global::IsOnline(mData->mMachineState))
3939 return setError(VBOX_E_INVALID_OBJECT_STATE,
3940 tr("The machine '%s' does not have a VM process"),
3941 mUserData->s.strName.c_str());
3942
3943 /* forcibly terminate the VM process */
3944 if (mData->mSession.mPID != NIL_RTPROCESS)
3945 RTProcTerminate(mData->mSession.mPID);
3946
3947 /* signal the client watcher thread, as most likely the client has
3948 * been terminated */
3949 mParent->updateClientWatcher();
3950 }
3951
3952 return rc;
3953}
3954
3955STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3956{
3957 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3958 return setError(E_INVALIDARG,
3959 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3960 aPosition, SchemaDefs::MaxBootPosition);
3961
3962 if (aDevice == DeviceType_USB)
3963 return setError(E_NOTIMPL,
3964 tr("Booting from USB device is currently not supported"));
3965
3966 AutoCaller autoCaller(this);
3967 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3968
3969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3970
3971 HRESULT rc = checkStateDependency(MutableStateDep);
3972 if (FAILED(rc)) return rc;
3973
3974 setModified(IsModified_MachineData);
3975 mHWData.backup();
3976 mHWData->mBootOrder[aPosition - 1] = aDevice;
3977
3978 return S_OK;
3979}
3980
3981STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3982{
3983 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3984 return setError(E_INVALIDARG,
3985 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3986 aPosition, SchemaDefs::MaxBootPosition);
3987
3988 AutoCaller autoCaller(this);
3989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3990
3991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3992
3993 *aDevice = mHWData->mBootOrder[aPosition - 1];
3994
3995 return S_OK;
3996}
3997
3998STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3999 LONG aControllerPort,
4000 LONG aDevice,
4001 DeviceType_T aType,
4002 IMedium *aMedium)
4003{
4004 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4005 aControllerName, aControllerPort, aDevice, aType, aMedium));
4006
4007 CheckComArgStrNotEmptyOrNull(aControllerName);
4008
4009 AutoCaller autoCaller(this);
4010 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4011
4012 // request the host lock first, since might be calling Host methods for getting host drives;
4013 // next, protect the media tree all the while we're in here, as well as our member variables
4014 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4015 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4016
4017 HRESULT rc = checkStateDependency(MutableStateDep);
4018 if (FAILED(rc)) return rc;
4019
4020 /// @todo NEWMEDIA implicit machine registration
4021 if (!mData->mRegistered)
4022 return setError(VBOX_E_INVALID_OBJECT_STATE,
4023 tr("Cannot attach storage devices to an unregistered machine"));
4024
4025 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4026
4027 /* Check for an existing controller. */
4028 ComObjPtr<StorageController> ctl;
4029 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4030 if (FAILED(rc)) return rc;
4031
4032 StorageControllerType_T ctrlType;
4033 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4034 if (FAILED(rc))
4035 return setError(E_FAIL,
4036 tr("Could not get type of controller '%ls'"),
4037 aControllerName);
4038
4039 bool fSilent = false;
4040 Utf8Str strReconfig;
4041
4042 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4043 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4044 if (FAILED(rc))
4045 return rc;
4046 if ( mData->mMachineState == MachineState_Paused
4047 && strReconfig == "1")
4048 fSilent = true;
4049
4050 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4051 bool fHotplug = false;
4052 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4053 fHotplug = true;
4054
4055 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4056 return setError(VBOX_E_INVALID_VM_STATE,
4057 tr("Controller '%ls' does not support hotplugging"),
4058 aControllerName);
4059
4060 // check that the port and device are not out of range
4061 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4062 if (FAILED(rc)) return rc;
4063
4064 /* check if the device slot is already busy */
4065 MediumAttachment *pAttachTemp;
4066 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4067 aControllerName,
4068 aControllerPort,
4069 aDevice)))
4070 {
4071 Medium *pMedium = pAttachTemp->getMedium();
4072 if (pMedium)
4073 {
4074 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4075 return setError(VBOX_E_OBJECT_IN_USE,
4076 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4077 pMedium->getLocationFull().c_str(),
4078 aControllerPort,
4079 aDevice,
4080 aControllerName);
4081 }
4082 else
4083 return setError(VBOX_E_OBJECT_IN_USE,
4084 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4085 aControllerPort, aDevice, aControllerName);
4086 }
4087
4088 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4089 if (aMedium && medium.isNull())
4090 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4091
4092 AutoCaller mediumCaller(medium);
4093 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4094
4095 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4096
4097 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4098 && !medium.isNull()
4099 )
4100 return setError(VBOX_E_OBJECT_IN_USE,
4101 tr("Medium '%s' is already attached to this virtual machine"),
4102 medium->getLocationFull().c_str());
4103
4104 if (!medium.isNull())
4105 {
4106 MediumType_T mtype = medium->getType();
4107 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4108 // For DVDs it's not written to the config file, so needs no global config
4109 // version bump. For floppies it's a new attribute "type", which is ignored
4110 // by older VirtualBox version, so needs no global config version bump either.
4111 // For hard disks this type is not accepted.
4112 if (mtype == MediumType_MultiAttach)
4113 {
4114 // This type is new with VirtualBox 4.0 and therefore requires settings
4115 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4116 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4117 // two reasons: The medium type is a property of the media registry tree, which
4118 // can reside in the global config file (for pre-4.0 media); we would therefore
4119 // possibly need to bump the global config version. We don't want to do that though
4120 // because that might make downgrading to pre-4.0 impossible.
4121 // As a result, we can only use these two new types if the medium is NOT in the
4122 // global registry:
4123 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4124 if ( medium->isInRegistry(uuidGlobalRegistry)
4125 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4126 )
4127 return setError(VBOX_E_INVALID_OBJECT_STATE,
4128 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4129 "to machines that were created with VirtualBox 4.0 or later"),
4130 medium->getLocationFull().c_str());
4131 }
4132 }
4133
4134 bool fIndirect = false;
4135 if (!medium.isNull())
4136 fIndirect = medium->isReadOnly();
4137 bool associate = true;
4138
4139 do
4140 {
4141 if ( aType == DeviceType_HardDisk
4142 && mMediaData.isBackedUp())
4143 {
4144 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4145
4146 /* check if the medium was attached to the VM before we started
4147 * changing attachments in which case the attachment just needs to
4148 * be restored */
4149 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4150 {
4151 AssertReturn(!fIndirect, E_FAIL);
4152
4153 /* see if it's the same bus/channel/device */
4154 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4155 {
4156 /* the simplest case: restore the whole attachment
4157 * and return, nothing else to do */
4158 mMediaData->mAttachments.push_back(pAttachTemp);
4159
4160 /* Reattach the medium to the VM. */
4161 if (fHotplug || fSilent)
4162 {
4163 mediumLock.release();
4164 treeLock.release();
4165 alock.release();
4166
4167 MediumLockList *pMediumLockList(new MediumLockList());
4168
4169 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4170 true /* fMediumLockWrite */,
4171 NULL,
4172 *pMediumLockList);
4173 alock.acquire();
4174 if (FAILED(rc))
4175 delete pMediumLockList;
4176 else
4177 {
4178 mData->mSession.mLockedMedia.Unlock();
4179 alock.release();
4180 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4181 mData->mSession.mLockedMedia.Lock();
4182 alock.acquire();
4183 }
4184 alock.release();
4185
4186 if (SUCCEEDED(rc))
4187 {
4188 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4189 /* Remove lock list in case of error. */
4190 if (FAILED(rc))
4191 {
4192 mData->mSession.mLockedMedia.Unlock();
4193 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4194 mData->mSession.mLockedMedia.Lock();
4195 }
4196 }
4197 }
4198
4199 return S_OK;
4200 }
4201
4202 /* bus/channel/device differ; we need a new attachment object,
4203 * but don't try to associate it again */
4204 associate = false;
4205 break;
4206 }
4207 }
4208
4209 /* go further only if the attachment is to be indirect */
4210 if (!fIndirect)
4211 break;
4212
4213 /* perform the so called smart attachment logic for indirect
4214 * attachments. Note that smart attachment is only applicable to base
4215 * hard disks. */
4216
4217 if (medium->getParent().isNull())
4218 {
4219 /* first, investigate the backup copy of the current hard disk
4220 * attachments to make it possible to re-attach existing diffs to
4221 * another device slot w/o losing their contents */
4222 if (mMediaData.isBackedUp())
4223 {
4224 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4225
4226 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4227 uint32_t foundLevel = 0;
4228
4229 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4230 it != oldAtts.end();
4231 ++it)
4232 {
4233 uint32_t level = 0;
4234 MediumAttachment *pAttach = *it;
4235 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4236 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4237 if (pMedium.isNull())
4238 continue;
4239
4240 if (pMedium->getBase(&level) == medium)
4241 {
4242 /* skip the hard disk if its currently attached (we
4243 * cannot attach the same hard disk twice) */
4244 if (findAttachment(mMediaData->mAttachments,
4245 pMedium))
4246 continue;
4247
4248 /* matched device, channel and bus (i.e. attached to the
4249 * same place) will win and immediately stop the search;
4250 * otherwise the attachment that has the youngest
4251 * descendant of medium will be used
4252 */
4253 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4254 {
4255 /* the simplest case: restore the whole attachment
4256 * and return, nothing else to do */
4257 mMediaData->mAttachments.push_back(*it);
4258
4259 /* Reattach the medium to the VM. */
4260 if (fHotplug || fSilent)
4261 {
4262 mediumLock.release();
4263 treeLock.release();
4264 alock.release();
4265
4266 MediumLockList *pMediumLockList(new MediumLockList());
4267
4268 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4269 true /* fMediumLockWrite */,
4270 NULL,
4271 *pMediumLockList);
4272 alock.acquire();
4273 if (FAILED(rc))
4274 delete pMediumLockList;
4275 else
4276 {
4277 mData->mSession.mLockedMedia.Unlock();
4278 alock.release();
4279 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4280 mData->mSession.mLockedMedia.Lock();
4281 alock.acquire();
4282 }
4283 alock.release();
4284
4285 if (SUCCEEDED(rc))
4286 {
4287 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4288 /* Remove lock list in case of error. */
4289 if (FAILED(rc))
4290 {
4291 mData->mSession.mLockedMedia.Unlock();
4292 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4293 mData->mSession.mLockedMedia.Lock();
4294 }
4295 }
4296 }
4297
4298 return S_OK;
4299 }
4300 else if ( foundIt == oldAtts.end()
4301 || level > foundLevel /* prefer younger */
4302 )
4303 {
4304 foundIt = it;
4305 foundLevel = level;
4306 }
4307 }
4308 }
4309
4310 if (foundIt != oldAtts.end())
4311 {
4312 /* use the previously attached hard disk */
4313 medium = (*foundIt)->getMedium();
4314 mediumCaller.attach(medium);
4315 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4316 mediumLock.attach(medium);
4317 /* not implicit, doesn't require association with this VM */
4318 fIndirect = false;
4319 associate = false;
4320 /* go right to the MediumAttachment creation */
4321 break;
4322 }
4323 }
4324
4325 /* must give up the medium lock and medium tree lock as below we
4326 * go over snapshots, which needs a lock with higher lock order. */
4327 mediumLock.release();
4328 treeLock.release();
4329
4330 /* then, search through snapshots for the best diff in the given
4331 * hard disk's chain to base the new diff on */
4332
4333 ComObjPtr<Medium> base;
4334 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4335 while (snap)
4336 {
4337 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4338
4339 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4340
4341 MediumAttachment *pAttachFound = NULL;
4342 uint32_t foundLevel = 0;
4343
4344 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4345 it != snapAtts.end();
4346 ++it)
4347 {
4348 MediumAttachment *pAttach = *it;
4349 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4350 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4351 if (pMedium.isNull())
4352 continue;
4353
4354 uint32_t level = 0;
4355 if (pMedium->getBase(&level) == medium)
4356 {
4357 /* matched device, channel and bus (i.e. attached to the
4358 * same place) will win and immediately stop the search;
4359 * otherwise the attachment that has the youngest
4360 * descendant of medium will be used
4361 */
4362 if ( pAttach->getDevice() == aDevice
4363 && pAttach->getPort() == aControllerPort
4364 && pAttach->getControllerName() == aControllerName
4365 )
4366 {
4367 pAttachFound = pAttach;
4368 break;
4369 }
4370 else if ( !pAttachFound
4371 || level > foundLevel /* prefer younger */
4372 )
4373 {
4374 pAttachFound = pAttach;
4375 foundLevel = level;
4376 }
4377 }
4378 }
4379
4380 if (pAttachFound)
4381 {
4382 base = pAttachFound->getMedium();
4383 break;
4384 }
4385
4386 snap = snap->getParent();
4387 }
4388
4389 /* re-lock medium tree and the medium, as we need it below */
4390 treeLock.acquire();
4391 mediumLock.acquire();
4392
4393 /* found a suitable diff, use it as a base */
4394 if (!base.isNull())
4395 {
4396 medium = base;
4397 mediumCaller.attach(medium);
4398 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4399 mediumLock.attach(medium);
4400 }
4401 }
4402
4403 Utf8Str strFullSnapshotFolder;
4404 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4405
4406 ComObjPtr<Medium> diff;
4407 diff.createObject();
4408 // store this diff in the same registry as the parent
4409 Guid uuidRegistryParent;
4410 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4411 {
4412 // parent image has no registry: this can happen if we're attaching a new immutable
4413 // image that has not yet been attached (medium then points to the base and we're
4414 // creating the diff image for the immutable, and the parent is not yet registered);
4415 // put the parent in the machine registry then
4416 mediumLock.release();
4417 treeLock.release();
4418 alock.release();
4419 addMediumToRegistry(medium);
4420 alock.acquire();
4421 treeLock.acquire();
4422 mediumLock.acquire();
4423 medium->getFirstRegistryMachineId(uuidRegistryParent);
4424 }
4425 rc = diff->init(mParent,
4426 medium->getPreferredDiffFormat(),
4427 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4428 uuidRegistryParent);
4429 if (FAILED(rc)) return rc;
4430
4431 /* Apply the normal locking logic to the entire chain. */
4432 MediumLockList *pMediumLockList(new MediumLockList());
4433 mediumLock.release();
4434 treeLock.release();
4435 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4436 true /* fMediumLockWrite */,
4437 medium,
4438 *pMediumLockList);
4439 treeLock.acquire();
4440 mediumLock.acquire();
4441 if (SUCCEEDED(rc))
4442 {
4443 mediumLock.release();
4444 treeLock.release();
4445 rc = pMediumLockList->Lock();
4446 treeLock.acquire();
4447 mediumLock.acquire();
4448 if (FAILED(rc))
4449 setError(rc,
4450 tr("Could not lock medium when creating diff '%s'"),
4451 diff->getLocationFull().c_str());
4452 else
4453 {
4454 /* will release the lock before the potentially lengthy
4455 * operation, so protect with the special state */
4456 MachineState_T oldState = mData->mMachineState;
4457 setMachineState(MachineState_SettingUp);
4458
4459 mediumLock.release();
4460 treeLock.release();
4461 alock.release();
4462
4463 rc = medium->createDiffStorage(diff,
4464 MediumVariant_Standard,
4465 pMediumLockList,
4466 NULL /* aProgress */,
4467 true /* aWait */);
4468
4469 alock.acquire();
4470 treeLock.acquire();
4471 mediumLock.acquire();
4472
4473 setMachineState(oldState);
4474 }
4475 }
4476
4477 /* Unlock the media and free the associated memory. */
4478 delete pMediumLockList;
4479
4480 if (FAILED(rc)) return rc;
4481
4482 /* use the created diff for the actual attachment */
4483 medium = diff;
4484 mediumCaller.attach(medium);
4485 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4486 mediumLock.attach(medium);
4487 }
4488 while (0);
4489
4490 ComObjPtr<MediumAttachment> attachment;
4491 attachment.createObject();
4492 rc = attachment->init(this,
4493 medium,
4494 aControllerName,
4495 aControllerPort,
4496 aDevice,
4497 aType,
4498 fIndirect,
4499 false /* fPassthrough */,
4500 false /* fTempEject */,
4501 false /* fNonRotational */,
4502 false /* fDiscard */,
4503 Utf8Str::Empty);
4504 if (FAILED(rc)) return rc;
4505
4506 if (associate && !medium.isNull())
4507 {
4508 // as the last step, associate the medium to the VM
4509 rc = medium->addBackReference(mData->mUuid);
4510 // here we can fail because of Deleting, or being in process of creating a Diff
4511 if (FAILED(rc)) return rc;
4512
4513 mediumLock.release();
4514 treeLock.release();
4515 alock.release();
4516 addMediumToRegistry(medium);
4517 alock.acquire();
4518 treeLock.acquire();
4519 mediumLock.acquire();
4520 }
4521
4522 /* success: finally remember the attachment */
4523 setModified(IsModified_Storage);
4524 mMediaData.backup();
4525 mMediaData->mAttachments.push_back(attachment);
4526
4527 mediumLock.release();
4528 treeLock.release();
4529 alock.release();
4530
4531 if (fHotplug || fSilent)
4532 {
4533 if (!medium.isNull())
4534 {
4535 MediumLockList *pMediumLockList(new MediumLockList());
4536
4537 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4538 true /* fMediumLockWrite */,
4539 NULL,
4540 *pMediumLockList);
4541 alock.acquire();
4542 if (FAILED(rc))
4543 delete pMediumLockList;
4544 else
4545 {
4546 mData->mSession.mLockedMedia.Unlock();
4547 alock.release();
4548 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4549 mData->mSession.mLockedMedia.Lock();
4550 alock.acquire();
4551 }
4552 alock.release();
4553 }
4554
4555 if (SUCCEEDED(rc))
4556 {
4557 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4558 /* Remove lock list in case of error. */
4559 if (FAILED(rc))
4560 {
4561 mData->mSession.mLockedMedia.Unlock();
4562 mData->mSession.mLockedMedia.Remove(attachment);
4563 mData->mSession.mLockedMedia.Lock();
4564 }
4565 }
4566 }
4567
4568 mParent->saveModifiedRegistries();
4569
4570 return rc;
4571}
4572
4573STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4574 LONG aDevice)
4575{
4576 CheckComArgStrNotEmptyOrNull(aControllerName);
4577
4578 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4579 aControllerName, aControllerPort, aDevice));
4580
4581 AutoCaller autoCaller(this);
4582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4583
4584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4585
4586 HRESULT rc = checkStateDependency(MutableStateDep);
4587 if (FAILED(rc)) return rc;
4588
4589 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4590
4591 /* Check for an existing controller. */
4592 ComObjPtr<StorageController> ctl;
4593 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4594 if (FAILED(rc)) return rc;
4595
4596 StorageControllerType_T ctrlType;
4597 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4598 if (FAILED(rc))
4599 return setError(E_FAIL,
4600 tr("Could not get type of controller '%ls'"),
4601 aControllerName);
4602
4603 bool fSilent = false;
4604 Utf8Str strReconfig;
4605
4606 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4607 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4608 if (FAILED(rc))
4609 return rc;
4610 if ( mData->mMachineState == MachineState_Paused
4611 && strReconfig == "1")
4612 fSilent = true;
4613
4614 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4615 bool fHotplug = false;
4616 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4617 fHotplug = true;
4618
4619 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4620 return setError(VBOX_E_INVALID_VM_STATE,
4621 tr("Controller '%ls' does not support hotplugging"),
4622 aControllerName);
4623
4624 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4625 aControllerName,
4626 aControllerPort,
4627 aDevice);
4628 if (!pAttach)
4629 return setError(VBOX_E_OBJECT_NOT_FOUND,
4630 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4631 aDevice, aControllerPort, aControllerName);
4632
4633 /*
4634 * The VM has to detach the device before we delete any implicit diffs.
4635 * If this fails we can roll back without loosing data.
4636 */
4637 if (fHotplug || fSilent)
4638 {
4639 alock.release();
4640 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4641 alock.acquire();
4642 }
4643 if (FAILED(rc)) return rc;
4644
4645 /* If we are here everything went well and we can delete the implicit now. */
4646 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4647
4648 alock.release();
4649
4650 mParent->saveModifiedRegistries();
4651
4652 return rc;
4653}
4654
4655STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4656 LONG aDevice, BOOL aPassthrough)
4657{
4658 CheckComArgStrNotEmptyOrNull(aControllerName);
4659
4660 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4661 aControllerName, aControllerPort, aDevice, aPassthrough));
4662
4663 AutoCaller autoCaller(this);
4664 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4665
4666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4667
4668 HRESULT rc = checkStateDependency(MutableStateDep);
4669 if (FAILED(rc)) return rc;
4670
4671 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4672
4673 if (Global::IsOnlineOrTransient(mData->mMachineState))
4674 return setError(VBOX_E_INVALID_VM_STATE,
4675 tr("Invalid machine state: %s"),
4676 Global::stringifyMachineState(mData->mMachineState));
4677
4678 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4679 aControllerName,
4680 aControllerPort,
4681 aDevice);
4682 if (!pAttach)
4683 return setError(VBOX_E_OBJECT_NOT_FOUND,
4684 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4685 aDevice, aControllerPort, aControllerName);
4686
4687
4688 setModified(IsModified_Storage);
4689 mMediaData.backup();
4690
4691 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4692
4693 if (pAttach->getType() != DeviceType_DVD)
4694 return setError(E_INVALIDARG,
4695 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4696 aDevice, aControllerPort, aControllerName);
4697 pAttach->updatePassthrough(!!aPassthrough);
4698
4699 return S_OK;
4700}
4701
4702STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4703 LONG aDevice, BOOL aTemporaryEject)
4704{
4705 CheckComArgStrNotEmptyOrNull(aControllerName);
4706
4707 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4708 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4709
4710 AutoCaller autoCaller(this);
4711 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4712
4713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4714
4715 HRESULT rc = checkStateDependency(MutableStateDep);
4716 if (FAILED(rc)) return rc;
4717
4718 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4719 aControllerName,
4720 aControllerPort,
4721 aDevice);
4722 if (!pAttach)
4723 return setError(VBOX_E_OBJECT_NOT_FOUND,
4724 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4725 aDevice, aControllerPort, aControllerName);
4726
4727
4728 setModified(IsModified_Storage);
4729 mMediaData.backup();
4730
4731 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4732
4733 if (pAttach->getType() != DeviceType_DVD)
4734 return setError(E_INVALIDARG,
4735 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4736 aDevice, aControllerPort, aControllerName);
4737 pAttach->updateTempEject(!!aTemporaryEject);
4738
4739 return S_OK;
4740}
4741
4742STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4743 LONG aDevice, BOOL aNonRotational)
4744{
4745 CheckComArgStrNotEmptyOrNull(aControllerName);
4746
4747 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4748 aControllerName, aControllerPort, aDevice, aNonRotational));
4749
4750 AutoCaller autoCaller(this);
4751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4752
4753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4754
4755 HRESULT rc = checkStateDependency(MutableStateDep);
4756 if (FAILED(rc)) return rc;
4757
4758 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4759
4760 if (Global::IsOnlineOrTransient(mData->mMachineState))
4761 return setError(VBOX_E_INVALID_VM_STATE,
4762 tr("Invalid machine state: %s"),
4763 Global::stringifyMachineState(mData->mMachineState));
4764
4765 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4766 aControllerName,
4767 aControllerPort,
4768 aDevice);
4769 if (!pAttach)
4770 return setError(VBOX_E_OBJECT_NOT_FOUND,
4771 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4772 aDevice, aControllerPort, aControllerName);
4773
4774
4775 setModified(IsModified_Storage);
4776 mMediaData.backup();
4777
4778 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4779
4780 if (pAttach->getType() != DeviceType_HardDisk)
4781 return setError(E_INVALIDARG,
4782 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"),
4783 aDevice, aControllerPort, aControllerName);
4784 pAttach->updateNonRotational(!!aNonRotational);
4785
4786 return S_OK;
4787}
4788
4789STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4790 LONG aDevice, BOOL aDiscard)
4791{
4792 CheckComArgStrNotEmptyOrNull(aControllerName);
4793
4794 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4795 aControllerName, aControllerPort, aDevice, aDiscard));
4796
4797 AutoCaller autoCaller(this);
4798 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4799
4800 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4801
4802 HRESULT rc = checkStateDependency(MutableStateDep);
4803 if (FAILED(rc)) return rc;
4804
4805 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4806
4807 if (Global::IsOnlineOrTransient(mData->mMachineState))
4808 return setError(VBOX_E_INVALID_VM_STATE,
4809 tr("Invalid machine state: %s"),
4810 Global::stringifyMachineState(mData->mMachineState));
4811
4812 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4813 aControllerName,
4814 aControllerPort,
4815 aDevice);
4816 if (!pAttach)
4817 return setError(VBOX_E_OBJECT_NOT_FOUND,
4818 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4819 aDevice, aControllerPort, aControllerName);
4820
4821
4822 setModified(IsModified_Storage);
4823 mMediaData.backup();
4824
4825 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4826
4827 if (pAttach->getType() != DeviceType_HardDisk)
4828 return setError(E_INVALIDARG,
4829 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"),
4830 aDevice, aControllerPort, aControllerName);
4831 pAttach->updateDiscard(!!aDiscard);
4832
4833 return S_OK;
4834}
4835
4836STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4837 LONG aDevice)
4838{
4839 int rc = S_OK;
4840 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4841 aControllerName, aControllerPort, aDevice));
4842
4843 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4844
4845 return rc;
4846}
4847
4848STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4849 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4850{
4851 CheckComArgStrNotEmptyOrNull(aControllerName);
4852
4853 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4854 aControllerName, aControllerPort, aDevice));
4855
4856 AutoCaller autoCaller(this);
4857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4858
4859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4860
4861 HRESULT rc = checkStateDependency(MutableStateDep);
4862 if (FAILED(rc)) return rc;
4863
4864 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4865
4866 if (Global::IsOnlineOrTransient(mData->mMachineState))
4867 return setError(VBOX_E_INVALID_VM_STATE,
4868 tr("Invalid machine state: %s"),
4869 Global::stringifyMachineState(mData->mMachineState));
4870
4871 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4872 aControllerName,
4873 aControllerPort,
4874 aDevice);
4875 if (!pAttach)
4876 return setError(VBOX_E_OBJECT_NOT_FOUND,
4877 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4878 aDevice, aControllerPort, aControllerName);
4879
4880
4881 setModified(IsModified_Storage);
4882 mMediaData.backup();
4883
4884 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4885 if (aBandwidthGroup && group.isNull())
4886 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4887
4888 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4889
4890 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4891 if (strBandwidthGroupOld.isNotEmpty())
4892 {
4893 /* Get the bandwidth group object and release it - this must not fail. */
4894 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4895 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4896 Assert(SUCCEEDED(rc));
4897
4898 pBandwidthGroupOld->release();
4899 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4900 }
4901
4902 if (!group.isNull())
4903 {
4904 group->reference();
4905 pAttach->updateBandwidthGroup(group->getName());
4906 }
4907
4908 return S_OK;
4909}
4910
4911STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4912 LONG aControllerPort,
4913 LONG aDevice,
4914 DeviceType_T aType)
4915{
4916 HRESULT rc = S_OK;
4917
4918 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4919 aControllerName, aControllerPort, aDevice, aType));
4920
4921 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4922
4923 return rc;
4924}
4925
4926
4927
4928STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4929 LONG aControllerPort,
4930 LONG aDevice,
4931 BOOL aForce)
4932{
4933 int rc = S_OK;
4934 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4935 aControllerName, aControllerPort, aForce));
4936
4937 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4938
4939 return rc;
4940}
4941
4942STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4943 LONG aControllerPort,
4944 LONG aDevice,
4945 IMedium *aMedium,
4946 BOOL aForce)
4947{
4948 int rc = S_OK;
4949 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4950 aControllerName, aControllerPort, aDevice, aForce));
4951
4952 CheckComArgStrNotEmptyOrNull(aControllerName);
4953
4954 AutoCaller autoCaller(this);
4955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4956
4957 // request the host lock first, since might be calling Host methods for getting host drives;
4958 // next, protect the media tree all the while we're in here, as well as our member variables
4959 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4960 this->lockHandle(),
4961 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4962
4963 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4964 aControllerName,
4965 aControllerPort,
4966 aDevice);
4967 if (pAttach.isNull())
4968 return setError(VBOX_E_OBJECT_NOT_FOUND,
4969 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4970 aDevice, aControllerPort, aControllerName);
4971
4972 /* Remember previously mounted medium. The medium before taking the
4973 * backup is not necessarily the same thing. */
4974 ComObjPtr<Medium> oldmedium;
4975 oldmedium = pAttach->getMedium();
4976
4977 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4978 if (aMedium && pMedium.isNull())
4979 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4980
4981 AutoCaller mediumCaller(pMedium);
4982 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4983
4984 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4985 if (pMedium)
4986 {
4987 DeviceType_T mediumType = pAttach->getType();
4988 switch (mediumType)
4989 {
4990 case DeviceType_DVD:
4991 case DeviceType_Floppy:
4992 break;
4993
4994 default:
4995 return setError(VBOX_E_INVALID_OBJECT_STATE,
4996 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4997 aControllerPort,
4998 aDevice,
4999 aControllerName);
5000 }
5001 }
5002
5003 setModified(IsModified_Storage);
5004 mMediaData.backup();
5005
5006 {
5007 // The backup operation makes the pAttach reference point to the
5008 // old settings. Re-get the correct reference.
5009 pAttach = findAttachment(mMediaData->mAttachments,
5010 aControllerName,
5011 aControllerPort,
5012 aDevice);
5013 if (!oldmedium.isNull())
5014 oldmedium->removeBackReference(mData->mUuid);
5015 if (!pMedium.isNull())
5016 {
5017 pMedium->addBackReference(mData->mUuid);
5018
5019 mediumLock.release();
5020 multiLock.release();
5021 addMediumToRegistry(pMedium);
5022 multiLock.acquire();
5023 mediumLock.acquire();
5024 }
5025
5026 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5027 pAttach->updateMedium(pMedium);
5028 }
5029
5030 setModified(IsModified_Storage);
5031
5032 mediumLock.release();
5033 multiLock.release();
5034 rc = onMediumChange(pAttach, aForce);
5035 multiLock.acquire();
5036 mediumLock.acquire();
5037
5038 /* On error roll back this change only. */
5039 if (FAILED(rc))
5040 {
5041 if (!pMedium.isNull())
5042 pMedium->removeBackReference(mData->mUuid);
5043 pAttach = findAttachment(mMediaData->mAttachments,
5044 aControllerName,
5045 aControllerPort,
5046 aDevice);
5047 /* If the attachment is gone in the meantime, bail out. */
5048 if (pAttach.isNull())
5049 return rc;
5050 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5051 if (!oldmedium.isNull())
5052 oldmedium->addBackReference(mData->mUuid);
5053 pAttach->updateMedium(oldmedium);
5054 }
5055
5056 mediumLock.release();
5057 multiLock.release();
5058
5059 mParent->saveModifiedRegistries();
5060
5061 return rc;
5062}
5063
5064STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5065 LONG aControllerPort,
5066 LONG aDevice,
5067 IMedium **aMedium)
5068{
5069 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5070 aControllerName, aControllerPort, aDevice));
5071
5072 CheckComArgStrNotEmptyOrNull(aControllerName);
5073 CheckComArgOutPointerValid(aMedium);
5074
5075 AutoCaller autoCaller(this);
5076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5077
5078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5079
5080 *aMedium = NULL;
5081
5082 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5083 aControllerName,
5084 aControllerPort,
5085 aDevice);
5086 if (pAttach.isNull())
5087 return setError(VBOX_E_OBJECT_NOT_FOUND,
5088 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5089 aDevice, aControllerPort, aControllerName);
5090
5091 pAttach->getMedium().queryInterfaceTo(aMedium);
5092
5093 return S_OK;
5094}
5095
5096STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5097{
5098 CheckComArgOutPointerValid(port);
5099 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5100
5101 AutoCaller autoCaller(this);
5102 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5103
5104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5105
5106 mSerialPorts[slot].queryInterfaceTo(port);
5107
5108 return S_OK;
5109}
5110
5111STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5112{
5113 CheckComArgOutPointerValid(port);
5114 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5115
5116 AutoCaller autoCaller(this);
5117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5118
5119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5120
5121 mParallelPorts[slot].queryInterfaceTo(port);
5122
5123 return S_OK;
5124}
5125
5126STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5127{
5128 CheckComArgOutPointerValid(adapter);
5129 /* Do not assert if slot is out of range, just return the advertised
5130 status. testdriver/vbox.py triggers this in logVmInfo. */
5131 if (slot >= mNetworkAdapters.size())
5132 return setError(E_INVALIDARG,
5133 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5134 slot, mNetworkAdapters.size());
5135
5136 AutoCaller autoCaller(this);
5137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5138
5139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5140
5141 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5142
5143 return S_OK;
5144}
5145
5146STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5147{
5148 CheckComArgOutSafeArrayPointerValid(aKeys);
5149
5150 AutoCaller autoCaller(this);
5151 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5152
5153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5154
5155 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5156 int i = 0;
5157 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5158 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5159 ++it, ++i)
5160 {
5161 const Utf8Str &strKey = it->first;
5162 strKey.cloneTo(&saKeys[i]);
5163 }
5164 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5165
5166 return S_OK;
5167 }
5168
5169 /**
5170 * @note Locks this object for reading.
5171 */
5172STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5173 BSTR *aValue)
5174{
5175 CheckComArgStrNotEmptyOrNull(aKey);
5176 CheckComArgOutPointerValid(aValue);
5177
5178 AutoCaller autoCaller(this);
5179 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5180
5181 /* start with nothing found */
5182 Bstr bstrResult("");
5183
5184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5185
5186 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5187 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5188 // found:
5189 bstrResult = it->second; // source is a Utf8Str
5190
5191 /* return the result to caller (may be empty) */
5192 bstrResult.cloneTo(aValue);
5193
5194 return S_OK;
5195}
5196
5197 /**
5198 * @note Locks mParent for writing + this object for writing.
5199 */
5200STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5201{
5202 CheckComArgStrNotEmptyOrNull(aKey);
5203
5204 AutoCaller autoCaller(this);
5205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5206
5207 Utf8Str strKey(aKey);
5208 Utf8Str strValue(aValue);
5209 Utf8Str strOldValue; // empty
5210
5211 // locking note: we only hold the read lock briefly to look up the old value,
5212 // then release it and call the onExtraCanChange callbacks. There is a small
5213 // chance of a race insofar as the callback might be called twice if two callers
5214 // change the same key at the same time, but that's a much better solution
5215 // than the deadlock we had here before. The actual changing of the extradata
5216 // is then performed under the write lock and race-free.
5217
5218 // look up the old value first; if nothing has changed then we need not do anything
5219 {
5220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5221 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5222 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5223 strOldValue = it->second;
5224 }
5225
5226 bool fChanged;
5227 if ((fChanged = (strOldValue != strValue)))
5228 {
5229 // ask for permission from all listeners outside the locks;
5230 // onExtraDataCanChange() only briefly requests the VirtualBox
5231 // lock to copy the list of callbacks to invoke
5232 Bstr error;
5233 Bstr bstrValue(aValue);
5234
5235 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5236 {
5237 const char *sep = error.isEmpty() ? "" : ": ";
5238 CBSTR err = error.raw();
5239 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5240 sep, err));
5241 return setError(E_ACCESSDENIED,
5242 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5243 aKey,
5244 bstrValue.raw(),
5245 sep,
5246 err);
5247 }
5248
5249 // data is changing and change not vetoed: then write it out under the lock
5250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5251
5252 if (isSnapshotMachine())
5253 {
5254 HRESULT rc = checkStateDependency(MutableStateDep);
5255 if (FAILED(rc)) return rc;
5256 }
5257
5258 if (strValue.isEmpty())
5259 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5260 else
5261 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5262 // creates a new key if needed
5263
5264 bool fNeedsGlobalSaveSettings = false;
5265 saveSettings(&fNeedsGlobalSaveSettings);
5266
5267 if (fNeedsGlobalSaveSettings)
5268 {
5269 // save the global settings; for that we should hold only the VirtualBox lock
5270 alock.release();
5271 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5272 mParent->saveSettings();
5273 }
5274 }
5275
5276 // fire notification outside the lock
5277 if (fChanged)
5278 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5279
5280 return S_OK;
5281}
5282
5283STDMETHODIMP Machine::SaveSettings()
5284{
5285 AutoCaller autoCaller(this);
5286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5287
5288 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5289
5290 /* when there was auto-conversion, we want to save the file even if
5291 * the VM is saved */
5292 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5293 if (FAILED(rc)) return rc;
5294
5295 /* the settings file path may never be null */
5296 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5297
5298 /* save all VM data excluding snapshots */
5299 bool fNeedsGlobalSaveSettings = false;
5300 rc = saveSettings(&fNeedsGlobalSaveSettings);
5301 mlock.release();
5302
5303 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5304 {
5305 // save the global settings; for that we should hold only the VirtualBox lock
5306 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5307 rc = mParent->saveSettings();
5308 }
5309
5310 return rc;
5311}
5312
5313STDMETHODIMP Machine::DiscardSettings()
5314{
5315 AutoCaller autoCaller(this);
5316 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5317
5318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5319
5320 HRESULT rc = checkStateDependency(MutableStateDep);
5321 if (FAILED(rc)) return rc;
5322
5323 /*
5324 * during this rollback, the session will be notified if data has
5325 * been actually changed
5326 */
5327 rollback(true /* aNotify */);
5328
5329 return S_OK;
5330}
5331
5332/** @note Locks objects! */
5333STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5334 ComSafeArrayOut(IMedium*, aMedia))
5335{
5336 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5337 AutoLimitedCaller autoCaller(this);
5338 AssertComRCReturnRC(autoCaller.rc());
5339
5340 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5341
5342 Guid id(getId());
5343
5344 if (mData->mSession.mState != SessionState_Unlocked)
5345 return setError(VBOX_E_INVALID_OBJECT_STATE,
5346 tr("Cannot unregister the machine '%s' while it is locked"),
5347 mUserData->s.strName.c_str());
5348
5349 // wait for state dependents to drop to zero
5350 ensureNoStateDependencies();
5351
5352 if (!mData->mAccessible)
5353 {
5354 // inaccessible maschines can only be unregistered; uninitialize ourselves
5355 // here because currently there may be no unregistered that are inaccessible
5356 // (this state combination is not supported). Note releasing the caller and
5357 // leaving the lock before calling uninit()
5358 alock.release();
5359 autoCaller.release();
5360
5361 uninit();
5362
5363 mParent->unregisterMachine(this, id);
5364 // calls VirtualBox::saveSettings()
5365
5366 return S_OK;
5367 }
5368
5369 HRESULT rc = S_OK;
5370
5371 // discard saved state
5372 if (mData->mMachineState == MachineState_Saved)
5373 {
5374 // add the saved state file to the list of files the caller should delete
5375 Assert(!mSSData->strStateFilePath.isEmpty());
5376 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5377
5378 mSSData->strStateFilePath.setNull();
5379
5380 // unconditionally set the machine state to powered off, we now
5381 // know no session has locked the machine
5382 mData->mMachineState = MachineState_PoweredOff;
5383 }
5384
5385 size_t cSnapshots = 0;
5386 if (mData->mFirstSnapshot)
5387 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5388 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5389 // fail now before we start detaching media
5390 return setError(VBOX_E_INVALID_OBJECT_STATE,
5391 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5392 mUserData->s.strName.c_str(), cSnapshots);
5393
5394 // This list collects the medium objects from all medium attachments
5395 // which we will detach from the machine and its snapshots, in a specific
5396 // order which allows for closing all media without getting "media in use"
5397 // errors, simply by going through the list from the front to the back:
5398 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5399 // and must be closed before the parent media from the snapshots, or closing the parents
5400 // will fail because they still have children);
5401 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5402 // the root ("first") snapshot of the machine.
5403 MediaList llMedia;
5404
5405 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5406 && mMediaData->mAttachments.size()
5407 )
5408 {
5409 // we have media attachments: detach them all and add the Medium objects to our list
5410 if (cleanupMode != CleanupMode_UnregisterOnly)
5411 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5412 else
5413 return setError(VBOX_E_INVALID_OBJECT_STATE,
5414 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5415 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5416 }
5417
5418 if (cSnapshots)
5419 {
5420 // autoCleanup must be true here, or we would have failed above
5421
5422 // add the media from the medium attachments of the snapshots to llMedia
5423 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5424 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5425 // into the children first
5426
5427 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5428 MachineState_T oldState = mData->mMachineState;
5429 mData->mMachineState = MachineState_DeletingSnapshot;
5430
5431 // make a copy of the first snapshot so the refcount does not drop to 0
5432 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5433 // because of the AutoCaller voodoo)
5434 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5435
5436 // GO!
5437 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5438
5439 mData->mMachineState = oldState;
5440 }
5441
5442 if (FAILED(rc))
5443 {
5444 rollbackMedia();
5445 return rc;
5446 }
5447
5448 // commit all the media changes made above
5449 commitMedia();
5450
5451 mData->mRegistered = false;
5452
5453 // machine lock no longer needed
5454 alock.release();
5455
5456 // return media to caller
5457 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5458 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5459
5460 mParent->unregisterMachine(this, id);
5461 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5462
5463 return S_OK;
5464}
5465
5466struct Machine::DeleteTask
5467{
5468 ComObjPtr<Machine> pMachine;
5469 RTCList<ComPtr<IMedium> > llMediums;
5470 StringsList llFilesToDelete;
5471 ComObjPtr<Progress> pProgress;
5472};
5473
5474STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5475{
5476 LogFlowFuncEnter();
5477
5478 AutoCaller autoCaller(this);
5479 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5480
5481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5482
5483 HRESULT rc = checkStateDependency(MutableStateDep);
5484 if (FAILED(rc)) return rc;
5485
5486 if (mData->mRegistered)
5487 return setError(VBOX_E_INVALID_VM_STATE,
5488 tr("Cannot delete settings of a registered machine"));
5489
5490 DeleteTask *pTask = new DeleteTask;
5491 pTask->pMachine = this;
5492 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5493
5494 // collect files to delete
5495 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5496
5497 for (size_t i = 0; i < sfaMedia.size(); ++i)
5498 {
5499 IMedium *pIMedium(sfaMedia[i]);
5500 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5501 if (pMedium.isNull())
5502 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5503 SafeArray<BSTR> ids;
5504 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5505 if (FAILED(rc)) return rc;
5506 /* At this point the medium should not have any back references
5507 * anymore. If it has it is attached to another VM and *must* not
5508 * deleted. */
5509 if (ids.size() < 1)
5510 pTask->llMediums.append(pMedium);
5511 }
5512 if (mData->pMachineConfigFile->fileExists())
5513 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5514
5515 pTask->pProgress.createObject();
5516 pTask->pProgress->init(getVirtualBox(),
5517 static_cast<IMachine*>(this) /* aInitiator */,
5518 Bstr(tr("Deleting files")).raw(),
5519 true /* fCancellable */,
5520 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5521 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5522
5523 int vrc = RTThreadCreate(NULL,
5524 Machine::deleteThread,
5525 (void*)pTask,
5526 0,
5527 RTTHREADTYPE_MAIN_WORKER,
5528 0,
5529 "MachineDelete");
5530
5531 pTask->pProgress.queryInterfaceTo(aProgress);
5532
5533 if (RT_FAILURE(vrc))
5534 {
5535 delete pTask;
5536 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5537 }
5538
5539 LogFlowFuncLeave();
5540
5541 return S_OK;
5542}
5543
5544/**
5545 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5546 * calls Machine::deleteTaskWorker() on the actual machine object.
5547 * @param Thread
5548 * @param pvUser
5549 * @return
5550 */
5551/*static*/
5552DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5553{
5554 LogFlowFuncEnter();
5555
5556 DeleteTask *pTask = (DeleteTask*)pvUser;
5557 Assert(pTask);
5558 Assert(pTask->pMachine);
5559 Assert(pTask->pProgress);
5560
5561 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5562 pTask->pProgress->notifyComplete(rc);
5563
5564 delete pTask;
5565
5566 LogFlowFuncLeave();
5567
5568 NOREF(Thread);
5569
5570 return VINF_SUCCESS;
5571}
5572
5573/**
5574 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5575 * @param task
5576 * @return
5577 */
5578HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5579{
5580 AutoCaller autoCaller(this);
5581 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5582
5583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5584
5585 HRESULT rc = S_OK;
5586
5587 try
5588 {
5589 ULONG uLogHistoryCount = 3;
5590 ComPtr<ISystemProperties> systemProperties;
5591 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5592 if (FAILED(rc)) throw rc;
5593
5594 if (!systemProperties.isNull())
5595 {
5596 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5597 if (FAILED(rc)) throw rc;
5598 }
5599
5600 MachineState_T oldState = mData->mMachineState;
5601 setMachineState(MachineState_SettingUp);
5602 alock.release();
5603 for (size_t i = 0; i < task.llMediums.size(); ++i)
5604 {
5605 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5606 {
5607 AutoCaller mac(pMedium);
5608 if (FAILED(mac.rc())) throw mac.rc();
5609 Utf8Str strLocation = pMedium->getLocationFull();
5610 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5611 if (FAILED(rc)) throw rc;
5612 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5613 }
5614 ComPtr<IProgress> pProgress2;
5615 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5616 if (FAILED(rc)) throw rc;
5617 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5618 if (FAILED(rc)) throw rc;
5619 /* Check the result of the asynchrony process. */
5620 LONG iRc;
5621 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5622 if (FAILED(rc)) throw rc;
5623 /* If the thread of the progress object has an error, then
5624 * retrieve the error info from there, or it'll be lost. */
5625 if (FAILED(iRc))
5626 throw setError(ProgressErrorInfo(pProgress2));
5627 }
5628 setMachineState(oldState);
5629 alock.acquire();
5630
5631 // delete the files pushed on the task list by Machine::Delete()
5632 // (this includes saved states of the machine and snapshots and
5633 // medium storage files from the IMedium list passed in, and the
5634 // machine XML file)
5635 StringsList::const_iterator it = task.llFilesToDelete.begin();
5636 while (it != task.llFilesToDelete.end())
5637 {
5638 const Utf8Str &strFile = *it;
5639 LogFunc(("Deleting file %s\n", strFile.c_str()));
5640 int vrc = RTFileDelete(strFile.c_str());
5641 if (RT_FAILURE(vrc))
5642 throw setError(VBOX_E_IPRT_ERROR,
5643 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5644
5645 ++it;
5646 if (it == task.llFilesToDelete.end())
5647 {
5648 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5649 if (FAILED(rc)) throw rc;
5650 break;
5651 }
5652
5653 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5654 if (FAILED(rc)) throw rc;
5655 }
5656
5657 /* delete the settings only when the file actually exists */
5658 if (mData->pMachineConfigFile->fileExists())
5659 {
5660 /* Delete any backup or uncommitted XML files. Ignore failures.
5661 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5662 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5663 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5664 RTFileDelete(otherXml.c_str());
5665 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5666 RTFileDelete(otherXml.c_str());
5667
5668 /* delete the Logs folder, nothing important should be left
5669 * there (we don't check for errors because the user might have
5670 * some private files there that we don't want to delete) */
5671 Utf8Str logFolder;
5672 getLogFolder(logFolder);
5673 Assert(logFolder.length());
5674 if (RTDirExists(logFolder.c_str()))
5675 {
5676 /* Delete all VBox.log[.N] files from the Logs folder
5677 * (this must be in sync with the rotation logic in
5678 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5679 * files that may have been created by the GUI. */
5680 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5681 logFolder.c_str(), RTPATH_DELIMITER);
5682 RTFileDelete(log.c_str());
5683 log = Utf8StrFmt("%s%cVBox.png",
5684 logFolder.c_str(), RTPATH_DELIMITER);
5685 RTFileDelete(log.c_str());
5686 for (int i = uLogHistoryCount; i > 0; i--)
5687 {
5688 log = Utf8StrFmt("%s%cVBox.log.%d",
5689 logFolder.c_str(), RTPATH_DELIMITER, i);
5690 RTFileDelete(log.c_str());
5691 log = Utf8StrFmt("%s%cVBox.png.%d",
5692 logFolder.c_str(), RTPATH_DELIMITER, i);
5693 RTFileDelete(log.c_str());
5694 }
5695
5696 RTDirRemove(logFolder.c_str());
5697 }
5698
5699 /* delete the Snapshots folder, nothing important should be left
5700 * there (we don't check for errors because the user might have
5701 * some private files there that we don't want to delete) */
5702 Utf8Str strFullSnapshotFolder;
5703 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5704 Assert(!strFullSnapshotFolder.isEmpty());
5705 if (RTDirExists(strFullSnapshotFolder.c_str()))
5706 RTDirRemove(strFullSnapshotFolder.c_str());
5707
5708 // delete the directory that contains the settings file, but only
5709 // if it matches the VM name
5710 Utf8Str settingsDir;
5711 if (isInOwnDir(&settingsDir))
5712 RTDirRemove(settingsDir.c_str());
5713 }
5714
5715 alock.release();
5716
5717 mParent->saveModifiedRegistries();
5718 }
5719 catch (HRESULT aRC) { rc = aRC; }
5720
5721 return rc;
5722}
5723
5724STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5725{
5726 CheckComArgOutPointerValid(aSnapshot);
5727
5728 AutoCaller autoCaller(this);
5729 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5730
5731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5732
5733 ComObjPtr<Snapshot> pSnapshot;
5734 HRESULT rc;
5735
5736 if (!aNameOrId || !*aNameOrId)
5737 // null case (caller wants root snapshot): findSnapshotById() handles this
5738 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5739 else
5740 {
5741 Guid uuid(aNameOrId);
5742 if (uuid.isValid())
5743 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5744 else
5745 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5746 }
5747 pSnapshot.queryInterfaceTo(aSnapshot);
5748
5749 return rc;
5750}
5751
5752STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5753{
5754 CheckComArgStrNotEmptyOrNull(aName);
5755 CheckComArgStrNotEmptyOrNull(aHostPath);
5756
5757 AutoCaller autoCaller(this);
5758 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5759
5760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5761
5762 HRESULT rc = checkStateDependency(MutableStateDep);
5763 if (FAILED(rc)) return rc;
5764
5765 Utf8Str strName(aName);
5766
5767 ComObjPtr<SharedFolder> sharedFolder;
5768 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5769 if (SUCCEEDED(rc))
5770 return setError(VBOX_E_OBJECT_IN_USE,
5771 tr("Shared folder named '%s' already exists"),
5772 strName.c_str());
5773
5774 sharedFolder.createObject();
5775 rc = sharedFolder->init(getMachine(),
5776 strName,
5777 aHostPath,
5778 !!aWritable,
5779 !!aAutoMount,
5780 true /* fFailOnError */);
5781 if (FAILED(rc)) return rc;
5782
5783 setModified(IsModified_SharedFolders);
5784 mHWData.backup();
5785 mHWData->mSharedFolders.push_back(sharedFolder);
5786
5787 /* inform the direct session if any */
5788 alock.release();
5789 onSharedFolderChange();
5790
5791 return S_OK;
5792}
5793
5794STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5795{
5796 CheckComArgStrNotEmptyOrNull(aName);
5797
5798 AutoCaller autoCaller(this);
5799 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5800
5801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5802
5803 HRESULT rc = checkStateDependency(MutableStateDep);
5804 if (FAILED(rc)) return rc;
5805
5806 ComObjPtr<SharedFolder> sharedFolder;
5807 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5808 if (FAILED(rc)) return rc;
5809
5810 setModified(IsModified_SharedFolders);
5811 mHWData.backup();
5812 mHWData->mSharedFolders.remove(sharedFolder);
5813
5814 /* inform the direct session if any */
5815 alock.release();
5816 onSharedFolderChange();
5817
5818 return S_OK;
5819}
5820
5821STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5822{
5823 CheckComArgOutPointerValid(aCanShow);
5824
5825 /* start with No */
5826 *aCanShow = FALSE;
5827
5828 AutoCaller autoCaller(this);
5829 AssertComRCReturnRC(autoCaller.rc());
5830
5831 ComPtr<IInternalSessionControl> directControl;
5832 {
5833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5834
5835 if (mData->mSession.mState != SessionState_Locked)
5836 return setError(VBOX_E_INVALID_VM_STATE,
5837 tr("Machine is not locked for session (session state: %s)"),
5838 Global::stringifySessionState(mData->mSession.mState));
5839
5840 directControl = mData->mSession.mDirectControl;
5841 }
5842
5843 /* ignore calls made after #OnSessionEnd() is called */
5844 if (!directControl)
5845 return S_OK;
5846
5847 LONG64 dummy;
5848 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5849}
5850
5851STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5852{
5853 CheckComArgOutPointerValid(aWinId);
5854
5855 AutoCaller autoCaller(this);
5856 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5857
5858 ComPtr<IInternalSessionControl> directControl;
5859 {
5860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5861
5862 if (mData->mSession.mState != SessionState_Locked)
5863 return setError(E_FAIL,
5864 tr("Machine is not locked for session (session state: %s)"),
5865 Global::stringifySessionState(mData->mSession.mState));
5866
5867 directControl = mData->mSession.mDirectControl;
5868 }
5869
5870 /* ignore calls made after #OnSessionEnd() is called */
5871 if (!directControl)
5872 return S_OK;
5873
5874 BOOL dummy;
5875 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5876}
5877
5878#ifdef VBOX_WITH_GUEST_PROPS
5879/**
5880 * Look up a guest property in VBoxSVC's internal structures.
5881 */
5882HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5883 BSTR *aValue,
5884 LONG64 *aTimestamp,
5885 BSTR *aFlags) const
5886{
5887 using namespace guestProp;
5888
5889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5890 Utf8Str strName(aName);
5891 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5892
5893 if (it != mHWData->mGuestProperties.end())
5894 {
5895 char szFlags[MAX_FLAGS_LEN + 1];
5896 it->second.strValue.cloneTo(aValue);
5897 *aTimestamp = it->second.mTimestamp;
5898 writeFlags(it->second.mFlags, szFlags);
5899 Bstr(szFlags).cloneTo(aFlags);
5900 }
5901
5902 return S_OK;
5903}
5904
5905/**
5906 * Query the VM that a guest property belongs to for the property.
5907 * @returns E_ACCESSDENIED if the VM process is not available or not
5908 * currently handling queries and the lookup should then be done in
5909 * VBoxSVC.
5910 */
5911HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5912 BSTR *aValue,
5913 LONG64 *aTimestamp,
5914 BSTR *aFlags) const
5915{
5916 HRESULT rc;
5917 ComPtr<IInternalSessionControl> directControl;
5918 directControl = mData->mSession.mDirectControl;
5919
5920 /* fail if we were called after #OnSessionEnd() is called. This is a
5921 * silly race condition. */
5922
5923 if (!directControl)
5924 rc = E_ACCESSDENIED;
5925 else
5926 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5927 false /* isSetter */,
5928 aValue, aTimestamp, aFlags);
5929 return rc;
5930}
5931#endif // VBOX_WITH_GUEST_PROPS
5932
5933STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5934 BSTR *aValue,
5935 LONG64 *aTimestamp,
5936 BSTR *aFlags)
5937{
5938#ifndef VBOX_WITH_GUEST_PROPS
5939 ReturnComNotImplemented();
5940#else // VBOX_WITH_GUEST_PROPS
5941 CheckComArgStrNotEmptyOrNull(aName);
5942 CheckComArgOutPointerValid(aValue);
5943 CheckComArgOutPointerValid(aTimestamp);
5944 CheckComArgOutPointerValid(aFlags);
5945
5946 AutoCaller autoCaller(this);
5947 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5948
5949 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5950 if (rc == E_ACCESSDENIED)
5951 /* The VM is not running or the service is not (yet) accessible */
5952 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5953 return rc;
5954#endif // VBOX_WITH_GUEST_PROPS
5955}
5956
5957STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5958{
5959 LONG64 dummyTimestamp;
5960 Bstr dummyFlags;
5961 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5962}
5963
5964STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5965{
5966 Bstr dummyValue;
5967 Bstr dummyFlags;
5968 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5969}
5970
5971#ifdef VBOX_WITH_GUEST_PROPS
5972/**
5973 * Set a guest property in VBoxSVC's internal structures.
5974 */
5975HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5976 IN_BSTR aFlags)
5977{
5978 using namespace guestProp;
5979
5980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5981 HRESULT rc = S_OK;
5982
5983 rc = checkStateDependency(MutableStateDep);
5984 if (FAILED(rc)) return rc;
5985
5986 try
5987 {
5988 Utf8Str utf8Name(aName);
5989 Utf8Str utf8Flags(aFlags);
5990 uint32_t fFlags = NILFLAG;
5991 if ( aFlags != NULL
5992 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
5993 return setError(E_INVALIDARG,
5994 tr("Invalid guest property flag values: '%ls'"),
5995 aFlags);
5996
5997 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
5998 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
5999 if (it == mHWData->mGuestProperties.end())
6000 {
6001 if (!fDelete)
6002 {
6003 setModified(IsModified_MachineData);
6004 mHWData.backupEx();
6005
6006 RTTIMESPEC time;
6007 HWData::GuestProperty prop;
6008 prop.strValue = aValue;
6009 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6010 prop.mFlags = fFlags;
6011 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6012 }
6013 }
6014 else
6015 {
6016 if (it->second.mFlags & (RDONLYHOST))
6017 {
6018 rc = setError(E_ACCESSDENIED,
6019 tr("The property '%ls' cannot be changed by the host"),
6020 aName);
6021 }
6022 else
6023 {
6024 setModified(IsModified_MachineData);
6025 mHWData.backupEx();
6026
6027 /* The backupEx() operation invalidates our iterator,
6028 * so get a new one. */
6029 it = mHWData->mGuestProperties.find(utf8Name);
6030 Assert(it != mHWData->mGuestProperties.end());
6031
6032 if (!fDelete)
6033 {
6034 RTTIMESPEC time;
6035 it->second.strValue = aValue;
6036 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6037 it->second.mFlags = fFlags;
6038 }
6039 else
6040 mHWData->mGuestProperties.erase(it);
6041 }
6042 }
6043
6044 if ( SUCCEEDED(rc)
6045 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6046 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6047 RTSTR_MAX,
6048 utf8Name.c_str(),
6049 RTSTR_MAX,
6050 NULL)
6051 )
6052 )
6053 {
6054 alock.release();
6055
6056 mParent->onGuestPropertyChange(mData->mUuid, aName,
6057 aValue ? aValue : Bstr("").raw(),
6058 aFlags ? aFlags : Bstr("").raw());
6059 }
6060 }
6061 catch (std::bad_alloc &)
6062 {
6063 rc = E_OUTOFMEMORY;
6064 }
6065
6066 return rc;
6067}
6068
6069/**
6070 * Set a property on the VM that that property belongs to.
6071 * @returns E_ACCESSDENIED if the VM process is not available or not
6072 * currently handling queries and the setting should then be done in
6073 * VBoxSVC.
6074 */
6075HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6076 IN_BSTR aFlags)
6077{
6078 HRESULT rc;
6079
6080 try
6081 {
6082 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6083
6084 BSTR dummy = NULL; /* will not be changed (setter) */
6085 LONG64 dummy64;
6086 if (!directControl)
6087 rc = E_ACCESSDENIED;
6088 else
6089 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6090 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6091 true /* isSetter */,
6092 &dummy, &dummy64, &dummy);
6093 }
6094 catch (std::bad_alloc &)
6095 {
6096 rc = E_OUTOFMEMORY;
6097 }
6098
6099 return rc;
6100}
6101#endif // VBOX_WITH_GUEST_PROPS
6102
6103STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6104 IN_BSTR aFlags)
6105{
6106#ifndef VBOX_WITH_GUEST_PROPS
6107 ReturnComNotImplemented();
6108#else // VBOX_WITH_GUEST_PROPS
6109 CheckComArgStrNotEmptyOrNull(aName);
6110 CheckComArgMaybeNull(aFlags);
6111 CheckComArgMaybeNull(aValue);
6112
6113 AutoCaller autoCaller(this);
6114 if (FAILED(autoCaller.rc()))
6115 return autoCaller.rc();
6116
6117 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6118 if (rc == E_ACCESSDENIED)
6119 /* The VM is not running or the service is not (yet) accessible */
6120 rc = setGuestPropertyToService(aName, aValue, aFlags);
6121 return rc;
6122#endif // VBOX_WITH_GUEST_PROPS
6123}
6124
6125STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6126{
6127 return SetGuestProperty(aName, aValue, NULL);
6128}
6129
6130STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6131{
6132 return SetGuestProperty(aName, NULL, NULL);
6133}
6134
6135#ifdef VBOX_WITH_GUEST_PROPS
6136/**
6137 * Enumerate the guest properties in VBoxSVC's internal structures.
6138 */
6139HRESULT Machine::enumerateGuestPropertiesInService
6140 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6141 ComSafeArrayOut(BSTR, aValues),
6142 ComSafeArrayOut(LONG64, aTimestamps),
6143 ComSafeArrayOut(BSTR, aFlags))
6144{
6145 using namespace guestProp;
6146
6147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6148 Utf8Str strPatterns(aPatterns);
6149
6150 HWData::GuestPropertyMap propMap;
6151
6152 /*
6153 * Look for matching patterns and build up a list.
6154 */
6155 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6156 while (it != mHWData->mGuestProperties.end())
6157 {
6158 if ( strPatterns.isEmpty()
6159 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6160 RTSTR_MAX,
6161 it->first.c_str(),
6162 RTSTR_MAX,
6163 NULL)
6164 )
6165 {
6166 propMap.insert(*it);
6167 }
6168
6169 it++;
6170 }
6171
6172 alock.release();
6173
6174 /*
6175 * And build up the arrays for returning the property information.
6176 */
6177 size_t cEntries = propMap.size();
6178 SafeArray<BSTR> names(cEntries);
6179 SafeArray<BSTR> values(cEntries);
6180 SafeArray<LONG64> timestamps(cEntries);
6181 SafeArray<BSTR> flags(cEntries);
6182 size_t iProp = 0;
6183
6184 it = propMap.begin();
6185 while (it != propMap.end())
6186 {
6187 char szFlags[MAX_FLAGS_LEN + 1];
6188 it->first.cloneTo(&names[iProp]);
6189 it->second.strValue.cloneTo(&values[iProp]);
6190 timestamps[iProp] = it->second.mTimestamp;
6191 writeFlags(it->second.mFlags, szFlags);
6192 Bstr(szFlags).cloneTo(&flags[iProp++]);
6193 it++;
6194 }
6195 names.detachTo(ComSafeArrayOutArg(aNames));
6196 values.detachTo(ComSafeArrayOutArg(aValues));
6197 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6198 flags.detachTo(ComSafeArrayOutArg(aFlags));
6199 return S_OK;
6200}
6201
6202/**
6203 * Enumerate the properties managed by a VM.
6204 * @returns E_ACCESSDENIED if the VM process is not available or not
6205 * currently handling queries and the setting should then be done in
6206 * VBoxSVC.
6207 */
6208HRESULT Machine::enumerateGuestPropertiesOnVM
6209 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6210 ComSafeArrayOut(BSTR, aValues),
6211 ComSafeArrayOut(LONG64, aTimestamps),
6212 ComSafeArrayOut(BSTR, aFlags))
6213{
6214 HRESULT rc;
6215 ComPtr<IInternalSessionControl> directControl;
6216 directControl = mData->mSession.mDirectControl;
6217
6218 if (!directControl)
6219 rc = E_ACCESSDENIED;
6220 else
6221 rc = directControl->EnumerateGuestProperties
6222 (aPatterns, ComSafeArrayOutArg(aNames),
6223 ComSafeArrayOutArg(aValues),
6224 ComSafeArrayOutArg(aTimestamps),
6225 ComSafeArrayOutArg(aFlags));
6226 return rc;
6227}
6228#endif // VBOX_WITH_GUEST_PROPS
6229
6230STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6231 ComSafeArrayOut(BSTR, aNames),
6232 ComSafeArrayOut(BSTR, aValues),
6233 ComSafeArrayOut(LONG64, aTimestamps),
6234 ComSafeArrayOut(BSTR, aFlags))
6235{
6236#ifndef VBOX_WITH_GUEST_PROPS
6237 ReturnComNotImplemented();
6238#else // VBOX_WITH_GUEST_PROPS
6239 CheckComArgMaybeNull(aPatterns);
6240 CheckComArgOutSafeArrayPointerValid(aNames);
6241 CheckComArgOutSafeArrayPointerValid(aValues);
6242 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6243 CheckComArgOutSafeArrayPointerValid(aFlags);
6244
6245 AutoCaller autoCaller(this);
6246 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6247
6248 HRESULT rc = enumerateGuestPropertiesOnVM
6249 (aPatterns, ComSafeArrayOutArg(aNames),
6250 ComSafeArrayOutArg(aValues),
6251 ComSafeArrayOutArg(aTimestamps),
6252 ComSafeArrayOutArg(aFlags));
6253 if (rc == E_ACCESSDENIED)
6254 /* The VM is not running or the service is not (yet) accessible */
6255 rc = enumerateGuestPropertiesInService
6256 (aPatterns, ComSafeArrayOutArg(aNames),
6257 ComSafeArrayOutArg(aValues),
6258 ComSafeArrayOutArg(aTimestamps),
6259 ComSafeArrayOutArg(aFlags));
6260 return rc;
6261#endif // VBOX_WITH_GUEST_PROPS
6262}
6263
6264STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6265 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6266{
6267 MediaData::AttachmentList atts;
6268
6269 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6270 if (FAILED(rc)) return rc;
6271
6272 SafeIfaceArray<IMediumAttachment> attachments(atts);
6273 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6274
6275 return S_OK;
6276}
6277
6278STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6279 LONG aControllerPort,
6280 LONG aDevice,
6281 IMediumAttachment **aAttachment)
6282{
6283 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6284 aControllerName, aControllerPort, aDevice));
6285
6286 CheckComArgStrNotEmptyOrNull(aControllerName);
6287 CheckComArgOutPointerValid(aAttachment);
6288
6289 AutoCaller autoCaller(this);
6290 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6291
6292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6293
6294 *aAttachment = NULL;
6295
6296 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6297 aControllerName,
6298 aControllerPort,
6299 aDevice);
6300 if (pAttach.isNull())
6301 return setError(VBOX_E_OBJECT_NOT_FOUND,
6302 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6303 aDevice, aControllerPort, aControllerName);
6304
6305 pAttach.queryInterfaceTo(aAttachment);
6306
6307 return S_OK;
6308}
6309
6310STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6311 StorageBus_T aConnectionType,
6312 IStorageController **controller)
6313{
6314 CheckComArgStrNotEmptyOrNull(aName);
6315
6316 if ( (aConnectionType <= StorageBus_Null)
6317 || (aConnectionType > StorageBus_SAS))
6318 return setError(E_INVALIDARG,
6319 tr("Invalid connection type: %d"),
6320 aConnectionType);
6321
6322 AutoCaller autoCaller(this);
6323 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6324
6325 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6326
6327 HRESULT rc = checkStateDependency(MutableStateDep);
6328 if (FAILED(rc)) return rc;
6329
6330 /* try to find one with the name first. */
6331 ComObjPtr<StorageController> ctrl;
6332
6333 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6334 if (SUCCEEDED(rc))
6335 return setError(VBOX_E_OBJECT_IN_USE,
6336 tr("Storage controller named '%ls' already exists"),
6337 aName);
6338
6339 ctrl.createObject();
6340
6341 /* get a new instance number for the storage controller */
6342 ULONG ulInstance = 0;
6343 bool fBootable = true;
6344 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6345 it != mStorageControllers->end();
6346 ++it)
6347 {
6348 if ((*it)->getStorageBus() == aConnectionType)
6349 {
6350 ULONG ulCurInst = (*it)->getInstance();
6351
6352 if (ulCurInst >= ulInstance)
6353 ulInstance = ulCurInst + 1;
6354
6355 /* Only one controller of each type can be marked as bootable. */
6356 if ((*it)->getBootable())
6357 fBootable = false;
6358 }
6359 }
6360
6361 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6362 if (FAILED(rc)) return rc;
6363
6364 setModified(IsModified_Storage);
6365 mStorageControllers.backup();
6366 mStorageControllers->push_back(ctrl);
6367
6368 ctrl.queryInterfaceTo(controller);
6369
6370 /* inform the direct session if any */
6371 alock.release();
6372 onStorageControllerChange();
6373
6374 return S_OK;
6375}
6376
6377STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6378 IStorageController **aStorageController)
6379{
6380 CheckComArgStrNotEmptyOrNull(aName);
6381
6382 AutoCaller autoCaller(this);
6383 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6384
6385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6386
6387 ComObjPtr<StorageController> ctrl;
6388
6389 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6390 if (SUCCEEDED(rc))
6391 ctrl.queryInterfaceTo(aStorageController);
6392
6393 return rc;
6394}
6395
6396STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6397 IStorageController **aStorageController)
6398{
6399 AutoCaller autoCaller(this);
6400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6401
6402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6403
6404 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6405 it != mStorageControllers->end();
6406 ++it)
6407 {
6408 if ((*it)->getInstance() == aInstance)
6409 {
6410 (*it).queryInterfaceTo(aStorageController);
6411 return S_OK;
6412 }
6413 }
6414
6415 return setError(VBOX_E_OBJECT_NOT_FOUND,
6416 tr("Could not find a storage controller with instance number '%lu'"),
6417 aInstance);
6418}
6419
6420STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6421{
6422 AutoCaller autoCaller(this);
6423 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6424
6425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6426
6427 HRESULT rc = checkStateDependency(MutableStateDep);
6428 if (FAILED(rc)) return rc;
6429
6430 ComObjPtr<StorageController> ctrl;
6431
6432 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6433 if (SUCCEEDED(rc))
6434 {
6435 /* Ensure that only one controller of each type is marked as bootable. */
6436 if (fBootable == TRUE)
6437 {
6438 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6439 it != mStorageControllers->end();
6440 ++it)
6441 {
6442 ComObjPtr<StorageController> aCtrl = (*it);
6443
6444 if ( (aCtrl->getName() != Utf8Str(aName))
6445 && aCtrl->getBootable() == TRUE
6446 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6447 && aCtrl->getControllerType() == ctrl->getControllerType())
6448 {
6449 aCtrl->setBootable(FALSE);
6450 break;
6451 }
6452 }
6453 }
6454
6455 if (SUCCEEDED(rc))
6456 {
6457 ctrl->setBootable(fBootable);
6458 setModified(IsModified_Storage);
6459 }
6460 }
6461
6462 if (SUCCEEDED(rc))
6463 {
6464 /* inform the direct session if any */
6465 alock.release();
6466 onStorageControllerChange();
6467 }
6468
6469 return rc;
6470}
6471
6472STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6473{
6474 CheckComArgStrNotEmptyOrNull(aName);
6475
6476 AutoCaller autoCaller(this);
6477 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6478
6479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6480
6481 HRESULT rc = checkStateDependency(MutableStateDep);
6482 if (FAILED(rc)) return rc;
6483
6484 ComObjPtr<StorageController> ctrl;
6485 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6486 if (FAILED(rc)) return rc;
6487
6488 {
6489 /* find all attached devices to the appropriate storage controller and detach them all */
6490 // make a temporary list because detachDevice invalidates iterators into
6491 // mMediaData->mAttachments
6492 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6493
6494 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6495 it != llAttachments2.end();
6496 ++it)
6497 {
6498 MediumAttachment *pAttachTemp = *it;
6499
6500 AutoCaller localAutoCaller(pAttachTemp);
6501 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6502
6503 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6504
6505 if (pAttachTemp->getControllerName() == aName)
6506 {
6507 rc = detachDevice(pAttachTemp, alock, NULL);
6508 if (FAILED(rc)) return rc;
6509 }
6510 }
6511 }
6512
6513 /* We can remove it now. */
6514 setModified(IsModified_Storage);
6515 mStorageControllers.backup();
6516
6517 ctrl->unshare();
6518
6519 mStorageControllers->remove(ctrl);
6520
6521 /* inform the direct session if any */
6522 alock.release();
6523 onStorageControllerChange();
6524
6525 return S_OK;
6526}
6527
6528STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6529 ULONG *puOriginX,
6530 ULONG *puOriginY,
6531 ULONG *puWidth,
6532 ULONG *puHeight,
6533 BOOL *pfEnabled)
6534{
6535 LogFlowThisFunc(("\n"));
6536
6537 CheckComArgNotNull(puOriginX);
6538 CheckComArgNotNull(puOriginY);
6539 CheckComArgNotNull(puWidth);
6540 CheckComArgNotNull(puHeight);
6541 CheckComArgNotNull(pfEnabled);
6542
6543 uint32_t u32OriginX= 0;
6544 uint32_t u32OriginY= 0;
6545 uint32_t u32Width = 0;
6546 uint32_t u32Height = 0;
6547 uint16_t u16Flags = 0;
6548
6549 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6550 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6551 if (RT_FAILURE(vrc))
6552 {
6553#ifdef RT_OS_WINDOWS
6554 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6555 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6556 * So just assign fEnable to TRUE again.
6557 * The right fix would be to change GUI API wrappers to make sure that parameters
6558 * are changed only if API succeeds.
6559 */
6560 *pfEnabled = TRUE;
6561#endif
6562 return setError(VBOX_E_IPRT_ERROR,
6563 tr("Saved guest size is not available (%Rrc)"),
6564 vrc);
6565 }
6566
6567 *puOriginX = u32OriginX;
6568 *puOriginY = u32OriginY;
6569 *puWidth = u32Width;
6570 *puHeight = u32Height;
6571 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6572
6573 return S_OK;
6574}
6575
6576STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6577{
6578 LogFlowThisFunc(("\n"));
6579
6580 CheckComArgNotNull(aSize);
6581 CheckComArgNotNull(aWidth);
6582 CheckComArgNotNull(aHeight);
6583
6584 if (aScreenId != 0)
6585 return E_NOTIMPL;
6586
6587 AutoCaller autoCaller(this);
6588 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6589
6590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6591
6592 uint8_t *pu8Data = NULL;
6593 uint32_t cbData = 0;
6594 uint32_t u32Width = 0;
6595 uint32_t u32Height = 0;
6596
6597 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6598
6599 if (RT_FAILURE(vrc))
6600 return setError(VBOX_E_IPRT_ERROR,
6601 tr("Saved screenshot data is not available (%Rrc)"),
6602 vrc);
6603
6604 *aSize = cbData;
6605 *aWidth = u32Width;
6606 *aHeight = u32Height;
6607
6608 freeSavedDisplayScreenshot(pu8Data);
6609
6610 return S_OK;
6611}
6612
6613STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6614{
6615 LogFlowThisFunc(("\n"));
6616
6617 CheckComArgNotNull(aWidth);
6618 CheckComArgNotNull(aHeight);
6619 CheckComArgOutSafeArrayPointerValid(aData);
6620
6621 if (aScreenId != 0)
6622 return E_NOTIMPL;
6623
6624 AutoCaller autoCaller(this);
6625 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6626
6627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6628
6629 uint8_t *pu8Data = NULL;
6630 uint32_t cbData = 0;
6631 uint32_t u32Width = 0;
6632 uint32_t u32Height = 0;
6633
6634 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6635
6636 if (RT_FAILURE(vrc))
6637 return setError(VBOX_E_IPRT_ERROR,
6638 tr("Saved screenshot data is not available (%Rrc)"),
6639 vrc);
6640
6641 *aWidth = u32Width;
6642 *aHeight = u32Height;
6643
6644 com::SafeArray<BYTE> bitmap(cbData);
6645 /* Convert pixels to format expected by the API caller. */
6646 if (aBGR)
6647 {
6648 /* [0] B, [1] G, [2] R, [3] A. */
6649 for (unsigned i = 0; i < cbData; i += 4)
6650 {
6651 bitmap[i] = pu8Data[i];
6652 bitmap[i + 1] = pu8Data[i + 1];
6653 bitmap[i + 2] = pu8Data[i + 2];
6654 bitmap[i + 3] = 0xff;
6655 }
6656 }
6657 else
6658 {
6659 /* [0] R, [1] G, [2] B, [3] A. */
6660 for (unsigned i = 0; i < cbData; i += 4)
6661 {
6662 bitmap[i] = pu8Data[i + 2];
6663 bitmap[i + 1] = pu8Data[i + 1];
6664 bitmap[i + 2] = pu8Data[i];
6665 bitmap[i + 3] = 0xff;
6666 }
6667 }
6668 bitmap.detachTo(ComSafeArrayOutArg(aData));
6669
6670 freeSavedDisplayScreenshot(pu8Data);
6671
6672 return S_OK;
6673}
6674
6675
6676STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6677{
6678 LogFlowThisFunc(("\n"));
6679
6680 CheckComArgNotNull(aWidth);
6681 CheckComArgNotNull(aHeight);
6682 CheckComArgOutSafeArrayPointerValid(aData);
6683
6684 if (aScreenId != 0)
6685 return E_NOTIMPL;
6686
6687 AutoCaller autoCaller(this);
6688 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6689
6690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6691
6692 uint8_t *pu8Data = NULL;
6693 uint32_t cbData = 0;
6694 uint32_t u32Width = 0;
6695 uint32_t u32Height = 0;
6696
6697 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6698
6699 if (RT_FAILURE(vrc))
6700 return setError(VBOX_E_IPRT_ERROR,
6701 tr("Saved screenshot data is not available (%Rrc)"),
6702 vrc);
6703
6704 *aWidth = u32Width;
6705 *aHeight = u32Height;
6706
6707 HRESULT rc = S_OK;
6708 uint8_t *pu8PNG = NULL;
6709 uint32_t cbPNG = 0;
6710 uint32_t cxPNG = 0;
6711 uint32_t cyPNG = 0;
6712
6713 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6714
6715 if (RT_SUCCESS(vrc))
6716 {
6717 com::SafeArray<BYTE> screenData(cbPNG);
6718 screenData.initFrom(pu8PNG, cbPNG);
6719 if (pu8PNG)
6720 RTMemFree(pu8PNG);
6721 screenData.detachTo(ComSafeArrayOutArg(aData));
6722 }
6723 else
6724 {
6725 if (pu8PNG)
6726 RTMemFree(pu8PNG);
6727 return setError(VBOX_E_IPRT_ERROR,
6728 tr("Could not convert screenshot to PNG (%Rrc)"),
6729 vrc);
6730 }
6731
6732 freeSavedDisplayScreenshot(pu8Data);
6733
6734 return rc;
6735}
6736
6737STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6738{
6739 LogFlowThisFunc(("\n"));
6740
6741 CheckComArgNotNull(aSize);
6742 CheckComArgNotNull(aWidth);
6743 CheckComArgNotNull(aHeight);
6744
6745 if (aScreenId != 0)
6746 return E_NOTIMPL;
6747
6748 AutoCaller autoCaller(this);
6749 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6750
6751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6752
6753 uint8_t *pu8Data = NULL;
6754 uint32_t cbData = 0;
6755 uint32_t u32Width = 0;
6756 uint32_t u32Height = 0;
6757
6758 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6759
6760 if (RT_FAILURE(vrc))
6761 return setError(VBOX_E_IPRT_ERROR,
6762 tr("Saved screenshot data is not available (%Rrc)"),
6763 vrc);
6764
6765 *aSize = cbData;
6766 *aWidth = u32Width;
6767 *aHeight = u32Height;
6768
6769 freeSavedDisplayScreenshot(pu8Data);
6770
6771 return S_OK;
6772}
6773
6774STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6775{
6776 LogFlowThisFunc(("\n"));
6777
6778 CheckComArgNotNull(aWidth);
6779 CheckComArgNotNull(aHeight);
6780 CheckComArgOutSafeArrayPointerValid(aData);
6781
6782 if (aScreenId != 0)
6783 return E_NOTIMPL;
6784
6785 AutoCaller autoCaller(this);
6786 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6787
6788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6789
6790 uint8_t *pu8Data = NULL;
6791 uint32_t cbData = 0;
6792 uint32_t u32Width = 0;
6793 uint32_t u32Height = 0;
6794
6795 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6796
6797 if (RT_FAILURE(vrc))
6798 return setError(VBOX_E_IPRT_ERROR,
6799 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6800 vrc);
6801
6802 *aWidth = u32Width;
6803 *aHeight = u32Height;
6804
6805 com::SafeArray<BYTE> png(cbData);
6806 png.initFrom(pu8Data, cbData);
6807 png.detachTo(ComSafeArrayOutArg(aData));
6808
6809 freeSavedDisplayScreenshot(pu8Data);
6810
6811 return S_OK;
6812}
6813
6814STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6815{
6816 HRESULT rc = S_OK;
6817 LogFlowThisFunc(("\n"));
6818
6819 AutoCaller autoCaller(this);
6820 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6821
6822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6823
6824 if (!mHWData->mCPUHotPlugEnabled)
6825 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6826
6827 if (aCpu >= mHWData->mCPUCount)
6828 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6829
6830 if (mHWData->mCPUAttached[aCpu])
6831 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6832
6833 alock.release();
6834 rc = onCPUChange(aCpu, false);
6835 alock.acquire();
6836 if (FAILED(rc)) return rc;
6837
6838 setModified(IsModified_MachineData);
6839 mHWData.backup();
6840 mHWData->mCPUAttached[aCpu] = true;
6841
6842 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6843 if (Global::IsOnline(mData->mMachineState))
6844 saveSettings(NULL);
6845
6846 return S_OK;
6847}
6848
6849STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6850{
6851 HRESULT rc = S_OK;
6852 LogFlowThisFunc(("\n"));
6853
6854 AutoCaller autoCaller(this);
6855 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6856
6857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6858
6859 if (!mHWData->mCPUHotPlugEnabled)
6860 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6861
6862 if (aCpu >= SchemaDefs::MaxCPUCount)
6863 return setError(E_INVALIDARG,
6864 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6865 SchemaDefs::MaxCPUCount);
6866
6867 if (!mHWData->mCPUAttached[aCpu])
6868 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6869
6870 /* CPU 0 can't be detached */
6871 if (aCpu == 0)
6872 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6873
6874 alock.release();
6875 rc = onCPUChange(aCpu, true);
6876 alock.acquire();
6877 if (FAILED(rc)) return rc;
6878
6879 setModified(IsModified_MachineData);
6880 mHWData.backup();
6881 mHWData->mCPUAttached[aCpu] = false;
6882
6883 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6884 if (Global::IsOnline(mData->mMachineState))
6885 saveSettings(NULL);
6886
6887 return S_OK;
6888}
6889
6890STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6891{
6892 LogFlowThisFunc(("\n"));
6893
6894 CheckComArgNotNull(aCpuAttached);
6895
6896 *aCpuAttached = false;
6897
6898 AutoCaller autoCaller(this);
6899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6900
6901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6902
6903 /* If hotplug is enabled the CPU is always enabled. */
6904 if (!mHWData->mCPUHotPlugEnabled)
6905 {
6906 if (aCpu < mHWData->mCPUCount)
6907 *aCpuAttached = true;
6908 }
6909 else
6910 {
6911 if (aCpu < SchemaDefs::MaxCPUCount)
6912 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6913 }
6914
6915 return S_OK;
6916}
6917
6918STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6919{
6920 CheckComArgOutPointerValid(aName);
6921
6922 AutoCaller autoCaller(this);
6923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6924
6925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6926
6927 Utf8Str log = queryLogFilename(aIdx);
6928 if (!RTFileExists(log.c_str()))
6929 log.setNull();
6930 log.cloneTo(aName);
6931
6932 return S_OK;
6933}
6934
6935STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6936{
6937 LogFlowThisFunc(("\n"));
6938 CheckComArgOutSafeArrayPointerValid(aData);
6939 if (aSize < 0)
6940 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6941
6942 AutoCaller autoCaller(this);
6943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6944
6945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6946
6947 HRESULT rc = S_OK;
6948 Utf8Str log = queryLogFilename(aIdx);
6949
6950 /* do not unnecessarily hold the lock while doing something which does
6951 * not need the lock and potentially takes a long time. */
6952 alock.release();
6953
6954 /* Limit the chunk size to 32K for now, as that gives better performance
6955 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6956 * One byte expands to approx. 25 bytes of breathtaking XML. */
6957 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6958 com::SafeArray<BYTE> logData(cbData);
6959
6960 RTFILE LogFile;
6961 int vrc = RTFileOpen(&LogFile, log.c_str(),
6962 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6963 if (RT_SUCCESS(vrc))
6964 {
6965 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6966 if (RT_SUCCESS(vrc))
6967 logData.resize(cbData);
6968 else
6969 rc = setError(VBOX_E_IPRT_ERROR,
6970 tr("Could not read log file '%s' (%Rrc)"),
6971 log.c_str(), vrc);
6972 RTFileClose(LogFile);
6973 }
6974 else
6975 rc = setError(VBOX_E_IPRT_ERROR,
6976 tr("Could not open log file '%s' (%Rrc)"),
6977 log.c_str(), vrc);
6978
6979 if (FAILED(rc))
6980 logData.resize(0);
6981 logData.detachTo(ComSafeArrayOutArg(aData));
6982
6983 return rc;
6984}
6985
6986
6987/**
6988 * Currently this method doesn't attach device to the running VM,
6989 * just makes sure it's plugged on next VM start.
6990 */
6991STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6992{
6993 AutoCaller autoCaller(this);
6994 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6995
6996 // lock scope
6997 {
6998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6999
7000 HRESULT rc = checkStateDependency(MutableStateDep);
7001 if (FAILED(rc)) return rc;
7002
7003 ChipsetType_T aChipset = ChipsetType_PIIX3;
7004 COMGETTER(ChipsetType)(&aChipset);
7005
7006 if (aChipset != ChipsetType_ICH9)
7007 {
7008 return setError(E_INVALIDARG,
7009 tr("Host PCI attachment only supported with ICH9 chipset"));
7010 }
7011
7012 // check if device with this host PCI address already attached
7013 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7014 it != mHWData->mPCIDeviceAssignments.end();
7015 ++it)
7016 {
7017 LONG iHostAddress = -1;
7018 ComPtr<PCIDeviceAttachment> pAttach;
7019 pAttach = *it;
7020 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7021 if (iHostAddress == hostAddress)
7022 return setError(E_INVALIDARG,
7023 tr("Device with host PCI address already attached to this VM"));
7024 }
7025
7026 ComObjPtr<PCIDeviceAttachment> pda;
7027 char name[32];
7028
7029 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7030 Bstr bname(name);
7031 pda.createObject();
7032 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7033 setModified(IsModified_MachineData);
7034 mHWData.backup();
7035 mHWData->mPCIDeviceAssignments.push_back(pda);
7036 }
7037
7038 return S_OK;
7039}
7040
7041/**
7042 * Currently this method doesn't detach device from the running VM,
7043 * just makes sure it's not plugged on next VM start.
7044 */
7045STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7046{
7047 AutoCaller autoCaller(this);
7048 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7049
7050 ComObjPtr<PCIDeviceAttachment> pAttach;
7051 bool fRemoved = false;
7052 HRESULT rc;
7053
7054 // lock scope
7055 {
7056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7057
7058 rc = checkStateDependency(MutableStateDep);
7059 if (FAILED(rc)) return rc;
7060
7061 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7062 it != mHWData->mPCIDeviceAssignments.end();
7063 ++it)
7064 {
7065 LONG iHostAddress = -1;
7066 pAttach = *it;
7067 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7068 if (iHostAddress != -1 && iHostAddress == hostAddress)
7069 {
7070 setModified(IsModified_MachineData);
7071 mHWData.backup();
7072 mHWData->mPCIDeviceAssignments.remove(pAttach);
7073 fRemoved = true;
7074 break;
7075 }
7076 }
7077 }
7078
7079
7080 /* Fire event outside of the lock */
7081 if (fRemoved)
7082 {
7083 Assert(!pAttach.isNull());
7084 ComPtr<IEventSource> es;
7085 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7086 Assert(SUCCEEDED(rc));
7087 Bstr mid;
7088 rc = this->COMGETTER(Id)(mid.asOutParam());
7089 Assert(SUCCEEDED(rc));
7090 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7091 }
7092
7093 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7094 tr("No host PCI device %08x attached"),
7095 hostAddress
7096 );
7097}
7098
7099STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7100{
7101 CheckComArgOutSafeArrayPointerValid(aAssignments);
7102
7103 AutoCaller autoCaller(this);
7104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7105
7106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7107
7108 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7109 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7110
7111 return S_OK;
7112}
7113
7114STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7115{
7116 CheckComArgOutPointerValid(aBandwidthControl);
7117
7118 AutoCaller autoCaller(this);
7119 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7120
7121 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7122
7123 return S_OK;
7124}
7125
7126STDMETHODIMP Machine::COMGETTER(TracingEnabled)(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->mDebugging.fTracingEnabled;
7135 }
7136 return hrc;
7137}
7138
7139STDMETHODIMP Machine::COMSETTER(TracingEnabled)(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 {
7149 hrc = mHWData.backupEx();
7150 if (SUCCEEDED(hrc))
7151 {
7152 setModified(IsModified_MachineData);
7153 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7154 }
7155 }
7156 }
7157 return hrc;
7158}
7159
7160STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7161{
7162 CheckComArgOutPointerValid(pbstrConfig);
7163 AutoCaller autoCaller(this);
7164 HRESULT hrc = autoCaller.rc();
7165 if (SUCCEEDED(hrc))
7166 {
7167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7168 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7169 }
7170 return hrc;
7171}
7172
7173STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7174{
7175 CheckComArgStr(bstrConfig);
7176 AutoCaller autoCaller(this);
7177 HRESULT hrc = autoCaller.rc();
7178 if (SUCCEEDED(hrc))
7179 {
7180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7181 hrc = checkStateDependency(MutableStateDep);
7182 if (SUCCEEDED(hrc))
7183 {
7184 hrc = mHWData.backupEx();
7185 if (SUCCEEDED(hrc))
7186 {
7187 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7188 if (SUCCEEDED(hrc))
7189 setModified(IsModified_MachineData);
7190 }
7191 }
7192 }
7193 return hrc;
7194
7195}
7196
7197STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7198{
7199 CheckComArgOutPointerValid(pfAllow);
7200 AutoCaller autoCaller(this);
7201 HRESULT hrc = autoCaller.rc();
7202 if (SUCCEEDED(hrc))
7203 {
7204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7205 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7206 }
7207 return hrc;
7208}
7209
7210STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7211{
7212 AutoCaller autoCaller(this);
7213 HRESULT hrc = autoCaller.rc();
7214 if (SUCCEEDED(hrc))
7215 {
7216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7217 hrc = checkStateDependency(MutableStateDep);
7218 if (SUCCEEDED(hrc))
7219 {
7220 hrc = mHWData.backupEx();
7221 if (SUCCEEDED(hrc))
7222 {
7223 setModified(IsModified_MachineData);
7224 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7225 }
7226 }
7227 }
7228 return hrc;
7229}
7230
7231STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7232{
7233 CheckComArgOutPointerValid(pfEnabled);
7234 AutoCaller autoCaller(this);
7235 HRESULT hrc = autoCaller.rc();
7236 if (SUCCEEDED(hrc))
7237 {
7238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7239 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7240 }
7241 return hrc;
7242}
7243
7244STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7245{
7246 AutoCaller autoCaller(this);
7247 HRESULT hrc = autoCaller.rc();
7248 if (SUCCEEDED(hrc))
7249 {
7250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7251 hrc = checkStateDependency(MutableStateDep);
7252 if ( SUCCEEDED(hrc)
7253 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7254 {
7255 AutostartDb *autostartDb = mParent->getAutostartDb();
7256 int vrc;
7257
7258 if (fEnabled)
7259 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7260 else
7261 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7262
7263 if (RT_SUCCESS(vrc))
7264 {
7265 hrc = mHWData.backupEx();
7266 if (SUCCEEDED(hrc))
7267 {
7268 setModified(IsModified_MachineData);
7269 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7270 }
7271 }
7272 else if (vrc == VERR_NOT_SUPPORTED)
7273 hrc = setError(VBOX_E_NOT_SUPPORTED,
7274 tr("The VM autostart feature is not supported on this platform"));
7275 else if (vrc == VERR_PATH_NOT_FOUND)
7276 hrc = setError(E_FAIL,
7277 tr("The path to the autostart database is not set"));
7278 else
7279 hrc = setError(E_UNEXPECTED,
7280 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7281 fEnabled ? "Adding" : "Removing",
7282 mUserData->s.strName.c_str(), vrc);
7283 }
7284 }
7285 return hrc;
7286}
7287
7288STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7289{
7290 CheckComArgOutPointerValid(puDelay);
7291 AutoCaller autoCaller(this);
7292 HRESULT hrc = autoCaller.rc();
7293 if (SUCCEEDED(hrc))
7294 {
7295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7296 *puDelay = mHWData->mAutostart.uAutostartDelay;
7297 }
7298 return hrc;
7299}
7300
7301STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7302{
7303 AutoCaller autoCaller(this);
7304 HRESULT hrc = autoCaller.rc();
7305 if (SUCCEEDED(hrc))
7306 {
7307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7308 hrc = checkStateDependency(MutableStateDep);
7309 if (SUCCEEDED(hrc))
7310 {
7311 hrc = mHWData.backupEx();
7312 if (SUCCEEDED(hrc))
7313 {
7314 setModified(IsModified_MachineData);
7315 mHWData->mAutostart.uAutostartDelay = uDelay;
7316 }
7317 }
7318 }
7319 return hrc;
7320}
7321
7322STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7323{
7324 CheckComArgOutPointerValid(penmAutostopType);
7325 AutoCaller autoCaller(this);
7326 HRESULT hrc = autoCaller.rc();
7327 if (SUCCEEDED(hrc))
7328 {
7329 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7330 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7331 }
7332 return hrc;
7333}
7334
7335STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7336{
7337 AutoCaller autoCaller(this);
7338 HRESULT hrc = autoCaller.rc();
7339 if (SUCCEEDED(hrc))
7340 {
7341 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7342 hrc = checkStateDependency(MutableStateDep);
7343 if ( SUCCEEDED(hrc)
7344 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7345 {
7346 AutostartDb *autostartDb = mParent->getAutostartDb();
7347 int vrc;
7348
7349 if (enmAutostopType != AutostopType_Disabled)
7350 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7351 else
7352 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7353
7354 if (RT_SUCCESS(vrc))
7355 {
7356 hrc = mHWData.backupEx();
7357 if (SUCCEEDED(hrc))
7358 {
7359 setModified(IsModified_MachineData);
7360 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7361 }
7362 }
7363 else if (vrc == VERR_NOT_SUPPORTED)
7364 hrc = setError(VBOX_E_NOT_SUPPORTED,
7365 tr("The VM autostop feature is not supported on this platform"));
7366 else if (vrc == VERR_PATH_NOT_FOUND)
7367 hrc = setError(E_FAIL,
7368 tr("The path to the autostart database is not set"));
7369 else
7370 hrc = setError(E_UNEXPECTED,
7371 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7372 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7373 mUserData->s.strName.c_str(), vrc);
7374 }
7375 }
7376 return hrc;
7377}
7378
7379STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7380{
7381 CheckComArgOutPointerValid(aDefaultFrontend);
7382 AutoCaller autoCaller(this);
7383 HRESULT hrc = autoCaller.rc();
7384 if (SUCCEEDED(hrc))
7385 {
7386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7387 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7388 }
7389 return hrc;
7390}
7391
7392STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7393{
7394 CheckComArgStr(aDefaultFrontend);
7395 AutoCaller autoCaller(this);
7396 HRESULT hrc = autoCaller.rc();
7397 if (SUCCEEDED(hrc))
7398 {
7399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7400 hrc = checkStateDependency(MutableOrSavedStateDep);
7401 if (SUCCEEDED(hrc))
7402 {
7403 hrc = mHWData.backupEx();
7404 if (SUCCEEDED(hrc))
7405 {
7406 setModified(IsModified_MachineData);
7407 mHWData->mDefaultFrontend = aDefaultFrontend;
7408 }
7409 }
7410 }
7411 return hrc;
7412}
7413
7414STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7415{
7416 CheckComArgSafeArrayNotNull(aIcon);
7417 CheckComArgOutSafeArrayPointerValid(aIcon);
7418 AutoCaller autoCaller(this);
7419 HRESULT hrc = autoCaller.rc();
7420 if (SUCCEEDED(hrc))
7421 {
7422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7423 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7424 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7425 icon.detachTo(ComSafeArrayOutArg(aIcon));
7426 }
7427 return hrc;
7428}
7429
7430STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7431{
7432 CheckComArgSafeArrayNotNull(aIcon);
7433 AutoCaller autoCaller(this);
7434 HRESULT hrc = autoCaller.rc();
7435 if (SUCCEEDED(hrc))
7436 {
7437 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7438 hrc = checkStateDependency(MutableOrSavedStateDep);
7439 if (SUCCEEDED(hrc))
7440 {
7441 setModified(IsModified_MachineData);
7442 mUserData.backup();
7443 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7444 mUserData->mIcon.clear();
7445 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7446 }
7447 }
7448 return hrc;
7449}
7450
7451STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7452{
7453 LogFlowFuncEnter();
7454
7455 CheckComArgNotNull(pTarget);
7456 CheckComArgOutPointerValid(pProgress);
7457
7458 /* Convert the options. */
7459 RTCList<CloneOptions_T> optList;
7460 if (options != NULL)
7461 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7462
7463 if (optList.contains(CloneOptions_Link))
7464 {
7465 if (!isSnapshotMachine())
7466 return setError(E_INVALIDARG,
7467 tr("Linked clone can only be created from a snapshot"));
7468 if (mode != CloneMode_MachineState)
7469 return setError(E_INVALIDARG,
7470 tr("Linked clone can only be created for a single machine state"));
7471 }
7472 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7473
7474 AutoCaller autoCaller(this);
7475 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7476
7477
7478 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7479
7480 HRESULT rc = pWorker->start(pProgress);
7481
7482 LogFlowFuncLeave();
7483
7484 return rc;
7485}
7486
7487// public methods for internal purposes
7488/////////////////////////////////////////////////////////////////////////////
7489
7490/**
7491 * Adds the given IsModified_* flag to the dirty flags of the machine.
7492 * This must be called either during loadSettings or under the machine write lock.
7493 * @param fl
7494 */
7495void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7496{
7497 mData->flModifications |= fl;
7498 if (fAllowStateModification && isStateModificationAllowed())
7499 mData->mCurrentStateModified = true;
7500}
7501
7502/**
7503 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7504 * care of the write locking.
7505 *
7506 * @param fModifications The flag to add.
7507 */
7508void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7509{
7510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7511 setModified(fModification, fAllowStateModification);
7512}
7513
7514/**
7515 * Saves the registry entry of this machine to the given configuration node.
7516 *
7517 * @param aEntryNode Node to save the registry entry to.
7518 *
7519 * @note locks this object for reading.
7520 */
7521HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7522{
7523 AutoLimitedCaller autoCaller(this);
7524 AssertComRCReturnRC(autoCaller.rc());
7525
7526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7527
7528 data.uuid = mData->mUuid;
7529 data.strSettingsFile = mData->m_strConfigFile;
7530
7531 return S_OK;
7532}
7533
7534/**
7535 * Calculates the absolute path of the given path taking the directory of the
7536 * machine settings file as the current directory.
7537 *
7538 * @param aPath Path to calculate the absolute path for.
7539 * @param aResult Where to put the result (used only on success, can be the
7540 * same Utf8Str instance as passed in @a aPath).
7541 * @return IPRT result.
7542 *
7543 * @note Locks this object for reading.
7544 */
7545int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7546{
7547 AutoCaller autoCaller(this);
7548 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7549
7550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7551
7552 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7553
7554 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7555
7556 strSettingsDir.stripFilename();
7557 char folder[RTPATH_MAX];
7558 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7559 if (RT_SUCCESS(vrc))
7560 aResult = folder;
7561
7562 return vrc;
7563}
7564
7565/**
7566 * Copies strSource to strTarget, making it relative to the machine folder
7567 * if it is a subdirectory thereof, or simply copying it otherwise.
7568 *
7569 * @param strSource Path to evaluate and copy.
7570 * @param strTarget Buffer to receive target path.
7571 *
7572 * @note Locks this object for reading.
7573 */
7574void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7575 Utf8Str &strTarget)
7576{
7577 AutoCaller autoCaller(this);
7578 AssertComRCReturn(autoCaller.rc(), (void)0);
7579
7580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7581
7582 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7583 // use strTarget as a temporary buffer to hold the machine settings dir
7584 strTarget = mData->m_strConfigFileFull;
7585 strTarget.stripFilename();
7586 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7587 {
7588 // is relative: then append what's left
7589 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7590 // for empty paths (only possible for subdirs) use "." to avoid
7591 // triggering default settings for not present config attributes.
7592 if (strTarget.isEmpty())
7593 strTarget = ".";
7594 }
7595 else
7596 // is not relative: then overwrite
7597 strTarget = strSource;
7598}
7599
7600/**
7601 * Returns the full path to the machine's log folder in the
7602 * \a aLogFolder argument.
7603 */
7604void Machine::getLogFolder(Utf8Str &aLogFolder)
7605{
7606 AutoCaller autoCaller(this);
7607 AssertComRCReturnVoid(autoCaller.rc());
7608
7609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7610
7611 char szTmp[RTPATH_MAX];
7612 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7613 if (RT_SUCCESS(vrc))
7614 {
7615 if (szTmp[0] && !mUserData.isNull())
7616 {
7617 char szTmp2[RTPATH_MAX];
7618 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7619 if (RT_SUCCESS(vrc))
7620 aLogFolder = BstrFmt("%s%c%s",
7621 szTmp2,
7622 RTPATH_DELIMITER,
7623 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7624 }
7625 else
7626 vrc = VERR_PATH_IS_RELATIVE;
7627 }
7628
7629 if (RT_FAILURE(vrc))
7630 {
7631 // fallback if VBOX_USER_LOGHOME is not set or invalid
7632 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7633 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7634 aLogFolder.append(RTPATH_DELIMITER);
7635 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7636 }
7637}
7638
7639/**
7640 * Returns the full path to the machine's log file for an given index.
7641 */
7642Utf8Str Machine::queryLogFilename(ULONG idx)
7643{
7644 Utf8Str logFolder;
7645 getLogFolder(logFolder);
7646 Assert(logFolder.length());
7647 Utf8Str log;
7648 if (idx == 0)
7649 log = Utf8StrFmt("%s%cVBox.log",
7650 logFolder.c_str(), RTPATH_DELIMITER);
7651 else
7652 log = Utf8StrFmt("%s%cVBox.log.%d",
7653 logFolder.c_str(), RTPATH_DELIMITER, idx);
7654 return log;
7655}
7656
7657/**
7658 * Composes a unique saved state filename based on the current system time. The filename is
7659 * granular to the second so this will work so long as no more than one snapshot is taken on
7660 * a machine per second.
7661 *
7662 * Before version 4.1, we used this formula for saved state files:
7663 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7664 * which no longer works because saved state files can now be shared between the saved state of the
7665 * "saved" machine and an online snapshot, and the following would cause problems:
7666 * 1) save machine
7667 * 2) create online snapshot from that machine state --> reusing saved state file
7668 * 3) save machine again --> filename would be reused, breaking the online snapshot
7669 *
7670 * So instead we now use a timestamp.
7671 *
7672 * @param str
7673 */
7674void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7675{
7676 AutoCaller autoCaller(this);
7677 AssertComRCReturnVoid(autoCaller.rc());
7678
7679 {
7680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7681 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7682 }
7683
7684 RTTIMESPEC ts;
7685 RTTimeNow(&ts);
7686 RTTIME time;
7687 RTTimeExplode(&time, &ts);
7688
7689 strStateFilePath += RTPATH_DELIMITER;
7690 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7691 time.i32Year, time.u8Month, time.u8MonthDay,
7692 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7693}
7694
7695/**
7696 * Returns the full path to the default video capture file.
7697 */
7698void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7699{
7700 AutoCaller autoCaller(this);
7701 AssertComRCReturnVoid(autoCaller.rc());
7702
7703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7704
7705 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7706 strFile.stripExt(); // path/to/machinesfolder/vmname/vmname
7707 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7708}
7709
7710/**
7711 * @note Locks this object for writing, calls the client process
7712 * (inside the lock).
7713 */
7714HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7715 const Utf8Str &strFrontend,
7716 const Utf8Str &strEnvironment,
7717 ProgressProxy *aProgress)
7718{
7719 LogFlowThisFuncEnter();
7720
7721 AssertReturn(aControl, E_FAIL);
7722 AssertReturn(aProgress, E_FAIL);
7723 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7724
7725 AutoCaller autoCaller(this);
7726 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7727
7728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7729
7730 if (!mData->mRegistered)
7731 return setError(E_UNEXPECTED,
7732 tr("The machine '%s' is not registered"),
7733 mUserData->s.strName.c_str());
7734
7735 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7736
7737 if ( mData->mSession.mState == SessionState_Locked
7738 || mData->mSession.mState == SessionState_Spawning
7739 || mData->mSession.mState == SessionState_Unlocking)
7740 return setError(VBOX_E_INVALID_OBJECT_STATE,
7741 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7742 mUserData->s.strName.c_str());
7743
7744 /* may not be busy */
7745 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7746
7747 /* get the path to the executable */
7748 char szPath[RTPATH_MAX];
7749 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7750 size_t sz = strlen(szPath);
7751 szPath[sz++] = RTPATH_DELIMITER;
7752 szPath[sz] = 0;
7753 char *cmd = szPath + sz;
7754 sz = RTPATH_MAX - sz;
7755
7756 int vrc = VINF_SUCCESS;
7757 RTPROCESS pid = NIL_RTPROCESS;
7758
7759 RTENV env = RTENV_DEFAULT;
7760
7761 if (!strEnvironment.isEmpty())
7762 {
7763 char *newEnvStr = NULL;
7764
7765 do
7766 {
7767 /* clone the current environment */
7768 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7769 AssertRCBreakStmt(vrc2, vrc = vrc2);
7770
7771 newEnvStr = RTStrDup(strEnvironment.c_str());
7772 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7773
7774 /* put new variables to the environment
7775 * (ignore empty variable names here since RTEnv API
7776 * intentionally doesn't do that) */
7777 char *var = newEnvStr;
7778 for (char *p = newEnvStr; *p; ++p)
7779 {
7780 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7781 {
7782 *p = '\0';
7783 if (*var)
7784 {
7785 char *val = strchr(var, '=');
7786 if (val)
7787 {
7788 *val++ = '\0';
7789 vrc2 = RTEnvSetEx(env, var, val);
7790 }
7791 else
7792 vrc2 = RTEnvUnsetEx(env, var);
7793 if (RT_FAILURE(vrc2))
7794 break;
7795 }
7796 var = p + 1;
7797 }
7798 }
7799 if (RT_SUCCESS(vrc2) && *var)
7800 vrc2 = RTEnvPutEx(env, var);
7801
7802 AssertRCBreakStmt(vrc2, vrc = vrc2);
7803 }
7804 while (0);
7805
7806 if (newEnvStr != NULL)
7807 RTStrFree(newEnvStr);
7808 }
7809
7810#ifdef VBOX_WITH_QTGUI
7811 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7812 {
7813# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7814 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7815# else
7816 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7817# endif
7818 Assert(sz >= sizeof(VirtualBox_exe));
7819 strcpy(cmd, VirtualBox_exe);
7820
7821 Utf8Str idStr = mData->mUuid.toString();
7822 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7823 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7824 }
7825#else /* !VBOX_WITH_QTGUI */
7826 if (0)
7827 ;
7828#endif /* VBOX_WITH_QTGUI */
7829
7830 else
7831
7832#ifdef VBOX_WITH_VBOXSDL
7833 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7834 {
7835 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7836 Assert(sz >= sizeof(VBoxSDL_exe));
7837 strcpy(cmd, VBoxSDL_exe);
7838
7839 Utf8Str idStr = mData->mUuid.toString();
7840 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7841 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7842 }
7843#else /* !VBOX_WITH_VBOXSDL */
7844 if (0)
7845 ;
7846#endif /* !VBOX_WITH_VBOXSDL */
7847
7848 else
7849
7850#ifdef VBOX_WITH_HEADLESS
7851 if ( strFrontend == "headless"
7852 || strFrontend == "capture"
7853 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7854 )
7855 {
7856 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7857 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7858 * and a VM works even if the server has not been installed.
7859 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7860 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7861 * differently in 4.0 and 3.x.
7862 */
7863 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7864 Assert(sz >= sizeof(VBoxHeadless_exe));
7865 strcpy(cmd, VBoxHeadless_exe);
7866
7867 Utf8Str idStr = mData->mUuid.toString();
7868 /* Leave space for "--capture" arg. */
7869 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7870 "--startvm", idStr.c_str(),
7871 "--vrde", "config",
7872 0, /* For "--capture". */
7873 0 };
7874 if (strFrontend == "capture")
7875 {
7876 unsigned pos = RT_ELEMENTS(args) - 2;
7877 args[pos] = "--capture";
7878 }
7879 vrc = RTProcCreate(szPath, args, env,
7880#ifdef RT_OS_WINDOWS
7881 RTPROC_FLAGS_NO_WINDOW
7882#else
7883 0
7884#endif
7885 , &pid);
7886 }
7887#else /* !VBOX_WITH_HEADLESS */
7888 if (0)
7889 ;
7890#endif /* !VBOX_WITH_HEADLESS */
7891 else
7892 {
7893 RTEnvDestroy(env);
7894 return setError(E_INVALIDARG,
7895 tr("Invalid frontend name: '%s'"),
7896 strFrontend.c_str());
7897 }
7898
7899 RTEnvDestroy(env);
7900
7901 if (RT_FAILURE(vrc))
7902 return setError(VBOX_E_IPRT_ERROR,
7903 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7904 mUserData->s.strName.c_str(), vrc);
7905
7906 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7907
7908 /*
7909 * Note that we don't release the lock here before calling the client,
7910 * because it doesn't need to call us back if called with a NULL argument.
7911 * Releasing the lock here is dangerous because we didn't prepare the
7912 * launch data yet, but the client we've just started may happen to be
7913 * too fast and call openSession() that will fail (because of PID, etc.),
7914 * so that the Machine will never get out of the Spawning session state.
7915 */
7916
7917 /* inform the session that it will be a remote one */
7918 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7919 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7920 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7921
7922 if (FAILED(rc))
7923 {
7924 /* restore the session state */
7925 mData->mSession.mState = SessionState_Unlocked;
7926 /* The failure may occur w/o any error info (from RPC), so provide one */
7927 return setError(VBOX_E_VM_ERROR,
7928 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7929 }
7930
7931 /* attach launch data to the machine */
7932 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7933 mData->mSession.mRemoteControls.push_back(aControl);
7934 mData->mSession.mProgress = aProgress;
7935 mData->mSession.mPID = pid;
7936 mData->mSession.mState = SessionState_Spawning;
7937 mData->mSession.mType = strFrontend;
7938
7939 LogFlowThisFuncLeave();
7940 return S_OK;
7941}
7942
7943/**
7944 * Returns @c true if the given machine has an open direct session and returns
7945 * the session machine instance and additional session data (on some platforms)
7946 * if so.
7947 *
7948 * Note that when the method returns @c false, the arguments remain unchanged.
7949 *
7950 * @param aMachine Session machine object.
7951 * @param aControl Direct session control object (optional).
7952 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7953 *
7954 * @note locks this object for reading.
7955 */
7956#if defined(RT_OS_WINDOWS)
7957bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7958 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7959 HANDLE *aIPCSem /*= NULL*/,
7960 bool aAllowClosing /*= false*/)
7961#elif defined(RT_OS_OS2)
7962bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7963 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7964 HMTX *aIPCSem /*= NULL*/,
7965 bool aAllowClosing /*= false*/)
7966#else
7967bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7968 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7969 bool aAllowClosing /*= false*/)
7970#endif
7971{
7972 AutoLimitedCaller autoCaller(this);
7973 AssertComRCReturn(autoCaller.rc(), false);
7974
7975 /* just return false for inaccessible machines */
7976 if (autoCaller.state() != Ready)
7977 return false;
7978
7979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7980
7981 if ( mData->mSession.mState == SessionState_Locked
7982 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7983 )
7984 {
7985 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7986
7987 aMachine = mData->mSession.mMachine;
7988
7989 if (aControl != NULL)
7990 *aControl = mData->mSession.mDirectControl;
7991
7992#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7993 /* Additional session data */
7994 if (aIPCSem != NULL)
7995 *aIPCSem = aMachine->mIPCSem;
7996#endif
7997 return true;
7998 }
7999
8000 return false;
8001}
8002
8003/**
8004 * Returns @c true if the given machine has an spawning direct session and
8005 * returns and additional session data (on some platforms) if so.
8006 *
8007 * Note that when the method returns @c false, the arguments remain unchanged.
8008 *
8009 * @param aPID PID of the spawned direct session process.
8010 *
8011 * @note locks this object for reading.
8012 */
8013#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8014bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
8015#else
8016bool Machine::isSessionSpawning()
8017#endif
8018{
8019 AutoLimitedCaller autoCaller(this);
8020 AssertComRCReturn(autoCaller.rc(), false);
8021
8022 /* just return false for inaccessible machines */
8023 if (autoCaller.state() != Ready)
8024 return false;
8025
8026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8027
8028 if (mData->mSession.mState == SessionState_Spawning)
8029 {
8030#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8031 /* Additional session data */
8032 if (aPID != NULL)
8033 {
8034 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
8035 *aPID = mData->mSession.mPID;
8036 }
8037#endif
8038 return true;
8039 }
8040
8041 return false;
8042}
8043
8044/**
8045 * Called from the client watcher thread to check for unexpected client process
8046 * death during Session_Spawning state (e.g. before it successfully opened a
8047 * direct session).
8048 *
8049 * On Win32 and on OS/2, this method is called only when we've got the
8050 * direct client's process termination notification, so it always returns @c
8051 * true.
8052 *
8053 * On other platforms, this method returns @c true if the client process is
8054 * terminated and @c false if it's still alive.
8055 *
8056 * @note Locks this object for writing.
8057 */
8058bool Machine::checkForSpawnFailure()
8059{
8060 AutoCaller autoCaller(this);
8061 if (!autoCaller.isOk())
8062 {
8063 /* nothing to do */
8064 LogFlowThisFunc(("Already uninitialized!\n"));
8065 return true;
8066 }
8067
8068 /* VirtualBox::addProcessToReap() needs a write lock */
8069 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
8070
8071 if (mData->mSession.mState != SessionState_Spawning)
8072 {
8073 /* nothing to do */
8074 LogFlowThisFunc(("Not spawning any more!\n"));
8075 return true;
8076 }
8077
8078 HRESULT rc = S_OK;
8079
8080#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8081
8082 /* the process was already unexpectedly terminated, we just need to set an
8083 * error and finalize session spawning */
8084 rc = setError(E_FAIL,
8085 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
8086 getName().c_str());
8087#else
8088
8089 /* PID not yet initialized, skip check. */
8090 if (mData->mSession.mPID == NIL_RTPROCESS)
8091 return false;
8092
8093 RTPROCSTATUS status;
8094 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
8095 &status);
8096
8097 if (vrc != VERR_PROCESS_RUNNING)
8098 {
8099 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8100 rc = setError(E_FAIL,
8101 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8102 getName().c_str(), status.iStatus);
8103 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8104 rc = setError(E_FAIL,
8105 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8106 getName().c_str(), status.iStatus);
8107 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8108 rc = setError(E_FAIL,
8109 tr("The virtual machine '%s' has terminated abnormally"),
8110 getName().c_str(), status.iStatus);
8111 else
8112 rc = setError(E_FAIL,
8113 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8114 getName().c_str(), rc);
8115 }
8116
8117#endif
8118
8119 if (FAILED(rc))
8120 {
8121 /* Close the remote session, remove the remote control from the list
8122 * and reset session state to Closed (@note keep the code in sync with
8123 * the relevant part in checkForSpawnFailure()). */
8124
8125 Assert(mData->mSession.mRemoteControls.size() == 1);
8126 if (mData->mSession.mRemoteControls.size() == 1)
8127 {
8128 ErrorInfoKeeper eik;
8129 mData->mSession.mRemoteControls.front()->Uninitialize();
8130 }
8131
8132 mData->mSession.mRemoteControls.clear();
8133 mData->mSession.mState = SessionState_Unlocked;
8134
8135 /* finalize the progress after setting the state */
8136 if (!mData->mSession.mProgress.isNull())
8137 {
8138 mData->mSession.mProgress->notifyComplete(rc);
8139 mData->mSession.mProgress.setNull();
8140 }
8141
8142 mParent->addProcessToReap(mData->mSession.mPID);
8143 mData->mSession.mPID = NIL_RTPROCESS;
8144
8145 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8146 return true;
8147 }
8148
8149 return false;
8150}
8151
8152/**
8153 * Checks whether the machine can be registered. If so, commits and saves
8154 * all settings.
8155 *
8156 * @note Must be called from mParent's write lock. Locks this object and
8157 * children for writing.
8158 */
8159HRESULT Machine::prepareRegister()
8160{
8161 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8162
8163 AutoLimitedCaller autoCaller(this);
8164 AssertComRCReturnRC(autoCaller.rc());
8165
8166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8167
8168 /* wait for state dependents to drop to zero */
8169 ensureNoStateDependencies();
8170
8171 if (!mData->mAccessible)
8172 return setError(VBOX_E_INVALID_OBJECT_STATE,
8173 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8174 mUserData->s.strName.c_str(),
8175 mData->mUuid.toString().c_str());
8176
8177 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8178
8179 if (mData->mRegistered)
8180 return setError(VBOX_E_INVALID_OBJECT_STATE,
8181 tr("The machine '%s' with UUID {%s} is already registered"),
8182 mUserData->s.strName.c_str(),
8183 mData->mUuid.toString().c_str());
8184
8185 HRESULT rc = S_OK;
8186
8187 // Ensure the settings are saved. If we are going to be registered and
8188 // no config file exists yet, create it by calling saveSettings() too.
8189 if ( (mData->flModifications)
8190 || (!mData->pMachineConfigFile->fileExists())
8191 )
8192 {
8193 rc = saveSettings(NULL);
8194 // no need to check whether VirtualBox.xml needs saving too since
8195 // we can't have a machine XML file rename pending
8196 if (FAILED(rc)) return rc;
8197 }
8198
8199 /* more config checking goes here */
8200
8201 if (SUCCEEDED(rc))
8202 {
8203 /* we may have had implicit modifications we want to fix on success */
8204 commit();
8205
8206 mData->mRegistered = true;
8207 }
8208 else
8209 {
8210 /* we may have had implicit modifications we want to cancel on failure*/
8211 rollback(false /* aNotify */);
8212 }
8213
8214 return rc;
8215}
8216
8217/**
8218 * Increases the number of objects dependent on the machine state or on the
8219 * registered state. Guarantees that these two states will not change at least
8220 * until #releaseStateDependency() is called.
8221 *
8222 * Depending on the @a aDepType value, additional state checks may be made.
8223 * These checks will set extended error info on failure. See
8224 * #checkStateDependency() for more info.
8225 *
8226 * If this method returns a failure, the dependency is not added and the caller
8227 * is not allowed to rely on any particular machine state or registration state
8228 * value and may return the failed result code to the upper level.
8229 *
8230 * @param aDepType Dependency type to add.
8231 * @param aState Current machine state (NULL if not interested).
8232 * @param aRegistered Current registered state (NULL if not interested).
8233 *
8234 * @note Locks this object for writing.
8235 */
8236HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8237 MachineState_T *aState /* = NULL */,
8238 BOOL *aRegistered /* = NULL */)
8239{
8240 AutoCaller autoCaller(this);
8241 AssertComRCReturnRC(autoCaller.rc());
8242
8243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8244
8245 HRESULT rc = checkStateDependency(aDepType);
8246 if (FAILED(rc)) return rc;
8247
8248 {
8249 if (mData->mMachineStateChangePending != 0)
8250 {
8251 /* ensureNoStateDependencies() is waiting for state dependencies to
8252 * drop to zero so don't add more. It may make sense to wait a bit
8253 * and retry before reporting an error (since the pending state
8254 * transition should be really quick) but let's just assert for
8255 * now to see if it ever happens on practice. */
8256
8257 AssertFailed();
8258
8259 return setError(E_ACCESSDENIED,
8260 tr("Machine state change is in progress. Please retry the operation later."));
8261 }
8262
8263 ++mData->mMachineStateDeps;
8264 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8265 }
8266
8267 if (aState)
8268 *aState = mData->mMachineState;
8269 if (aRegistered)
8270 *aRegistered = mData->mRegistered;
8271
8272 return S_OK;
8273}
8274
8275/**
8276 * Decreases the number of objects dependent on the machine state.
8277 * Must always complete the #addStateDependency() call after the state
8278 * dependency is no more necessary.
8279 */
8280void Machine::releaseStateDependency()
8281{
8282 AutoCaller autoCaller(this);
8283 AssertComRCReturnVoid(autoCaller.rc());
8284
8285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8286
8287 /* releaseStateDependency() w/o addStateDependency()? */
8288 AssertReturnVoid(mData->mMachineStateDeps != 0);
8289 -- mData->mMachineStateDeps;
8290
8291 if (mData->mMachineStateDeps == 0)
8292 {
8293 /* inform ensureNoStateDependencies() that there are no more deps */
8294 if (mData->mMachineStateChangePending != 0)
8295 {
8296 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8297 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8298 }
8299 }
8300}
8301
8302Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8303{
8304 /* start with nothing found */
8305 Utf8Str strResult("");
8306
8307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8308
8309 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8310 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8311 // found:
8312 strResult = it->second; // source is a Utf8Str
8313
8314 return strResult;
8315}
8316
8317// protected methods
8318/////////////////////////////////////////////////////////////////////////////
8319
8320/**
8321 * Performs machine state checks based on the @a aDepType value. If a check
8322 * fails, this method will set extended error info, otherwise it will return
8323 * S_OK. It is supposed, that on failure, the caller will immediately return
8324 * the return value of this method to the upper level.
8325 *
8326 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8327 *
8328 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8329 * current state of this machine object allows to change settings of the
8330 * machine (i.e. the machine is not registered, or registered but not running
8331 * and not saved). It is useful to call this method from Machine setters
8332 * before performing any change.
8333 *
8334 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8335 * as for MutableStateDep except that if the machine is saved, S_OK is also
8336 * returned. This is useful in setters which allow changing machine
8337 * properties when it is in the saved state.
8338 *
8339 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8340 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8341 * Aborted).
8342 *
8343 * @param aDepType Dependency type to check.
8344 *
8345 * @note Non Machine based classes should use #addStateDependency() and
8346 * #releaseStateDependency() methods or the smart AutoStateDependency
8347 * template.
8348 *
8349 * @note This method must be called from under this object's read or write
8350 * lock.
8351 */
8352HRESULT Machine::checkStateDependency(StateDependency aDepType)
8353{
8354 switch (aDepType)
8355 {
8356 case AnyStateDep:
8357 {
8358 break;
8359 }
8360 case MutableStateDep:
8361 {
8362 if ( mData->mRegistered
8363 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8364 || ( mData->mMachineState != MachineState_Paused
8365 && mData->mMachineState != MachineState_Running
8366 && mData->mMachineState != MachineState_Aborted
8367 && mData->mMachineState != MachineState_Teleported
8368 && mData->mMachineState != MachineState_PoweredOff
8369 )
8370 )
8371 )
8372 return setError(VBOX_E_INVALID_VM_STATE,
8373 tr("The machine is not mutable (state is %s)"),
8374 Global::stringifyMachineState(mData->mMachineState));
8375 break;
8376 }
8377 case MutableOrSavedStateDep:
8378 {
8379 if ( mData->mRegistered
8380 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8381 || ( mData->mMachineState != MachineState_Paused
8382 && mData->mMachineState != MachineState_Running
8383 && mData->mMachineState != MachineState_Aborted
8384 && mData->mMachineState != MachineState_Teleported
8385 && mData->mMachineState != MachineState_Saved
8386 && mData->mMachineState != MachineState_PoweredOff
8387 )
8388 )
8389 )
8390 return setError(VBOX_E_INVALID_VM_STATE,
8391 tr("The machine is not mutable (state is %s)"),
8392 Global::stringifyMachineState(mData->mMachineState));
8393 break;
8394 }
8395 case OfflineStateDep:
8396 {
8397 if ( mData->mRegistered
8398 && ( !isSessionMachine()
8399 || ( mData->mMachineState != MachineState_PoweredOff
8400 && mData->mMachineState != MachineState_Saved
8401 && mData->mMachineState != MachineState_Aborted
8402 && mData->mMachineState != MachineState_Teleported
8403 )
8404 )
8405 )
8406 return setError(VBOX_E_INVALID_VM_STATE,
8407 tr("The machine is not offline (state is %s)"),
8408 Global::stringifyMachineState(mData->mMachineState));
8409 break;
8410 }
8411 }
8412
8413 return S_OK;
8414}
8415
8416/**
8417 * Helper to initialize all associated child objects and allocate data
8418 * structures.
8419 *
8420 * This method must be called as a part of the object's initialization procedure
8421 * (usually done in the #init() method).
8422 *
8423 * @note Must be called only from #init() or from #registeredInit().
8424 */
8425HRESULT Machine::initDataAndChildObjects()
8426{
8427 AutoCaller autoCaller(this);
8428 AssertComRCReturnRC(autoCaller.rc());
8429 AssertComRCReturn(autoCaller.state() == InInit ||
8430 autoCaller.state() == Limited, E_FAIL);
8431
8432 AssertReturn(!mData->mAccessible, E_FAIL);
8433
8434 /* allocate data structures */
8435 mSSData.allocate();
8436 mUserData.allocate();
8437 mHWData.allocate();
8438 mMediaData.allocate();
8439 mStorageControllers.allocate();
8440
8441 /* initialize mOSTypeId */
8442 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8443
8444 /* create associated BIOS settings object */
8445 unconst(mBIOSSettings).createObject();
8446 mBIOSSettings->init(this);
8447
8448 /* create an associated VRDE object (default is disabled) */
8449 unconst(mVRDEServer).createObject();
8450 mVRDEServer->init(this);
8451
8452 /* create associated serial port objects */
8453 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8454 {
8455 unconst(mSerialPorts[slot]).createObject();
8456 mSerialPorts[slot]->init(this, slot);
8457 }
8458
8459 /* create associated parallel port objects */
8460 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8461 {
8462 unconst(mParallelPorts[slot]).createObject();
8463 mParallelPorts[slot]->init(this, slot);
8464 }
8465
8466 /* create the audio adapter object (always present, default is disabled) */
8467 unconst(mAudioAdapter).createObject();
8468 mAudioAdapter->init(this);
8469
8470 /* create the USB controller object (always present, default is disabled) */
8471 unconst(mUSBController).createObject();
8472 mUSBController->init(this);
8473
8474 /* create associated network adapter objects */
8475 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8476 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8477 {
8478 unconst(mNetworkAdapters[slot]).createObject();
8479 mNetworkAdapters[slot]->init(this, slot);
8480 }
8481
8482 /* create the bandwidth control */
8483 unconst(mBandwidthControl).createObject();
8484 mBandwidthControl->init(this);
8485
8486 return S_OK;
8487}
8488
8489/**
8490 * Helper to uninitialize all associated child objects and to free all data
8491 * structures.
8492 *
8493 * This method must be called as a part of the object's uninitialization
8494 * procedure (usually done in the #uninit() method).
8495 *
8496 * @note Must be called only from #uninit() or from #registeredInit().
8497 */
8498void Machine::uninitDataAndChildObjects()
8499{
8500 AutoCaller autoCaller(this);
8501 AssertComRCReturnVoid(autoCaller.rc());
8502 AssertComRCReturnVoid( autoCaller.state() == InUninit
8503 || autoCaller.state() == Limited);
8504
8505 /* tell all our other child objects we've been uninitialized */
8506 if (mBandwidthControl)
8507 {
8508 mBandwidthControl->uninit();
8509 unconst(mBandwidthControl).setNull();
8510 }
8511
8512 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8513 {
8514 if (mNetworkAdapters[slot])
8515 {
8516 mNetworkAdapters[slot]->uninit();
8517 unconst(mNetworkAdapters[slot]).setNull();
8518 }
8519 }
8520
8521 if (mUSBController)
8522 {
8523 mUSBController->uninit();
8524 unconst(mUSBController).setNull();
8525 }
8526
8527 if (mAudioAdapter)
8528 {
8529 mAudioAdapter->uninit();
8530 unconst(mAudioAdapter).setNull();
8531 }
8532
8533 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8534 {
8535 if (mParallelPorts[slot])
8536 {
8537 mParallelPorts[slot]->uninit();
8538 unconst(mParallelPorts[slot]).setNull();
8539 }
8540 }
8541
8542 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8543 {
8544 if (mSerialPorts[slot])
8545 {
8546 mSerialPorts[slot]->uninit();
8547 unconst(mSerialPorts[slot]).setNull();
8548 }
8549 }
8550
8551 if (mVRDEServer)
8552 {
8553 mVRDEServer->uninit();
8554 unconst(mVRDEServer).setNull();
8555 }
8556
8557 if (mBIOSSettings)
8558 {
8559 mBIOSSettings->uninit();
8560 unconst(mBIOSSettings).setNull();
8561 }
8562
8563 /* Deassociate media (only when a real Machine or a SnapshotMachine
8564 * instance is uninitialized; SessionMachine instances refer to real
8565 * Machine media). This is necessary for a clean re-initialization of
8566 * the VM after successfully re-checking the accessibility state. Note
8567 * that in case of normal Machine or SnapshotMachine uninitialization (as
8568 * a result of unregistering or deleting the snapshot), outdated media
8569 * attachments will already be uninitialized and deleted, so this
8570 * code will not affect them. */
8571 if ( !!mMediaData
8572 && (!isSessionMachine())
8573 )
8574 {
8575 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8576 it != mMediaData->mAttachments.end();
8577 ++it)
8578 {
8579 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8580 if (pMedium.isNull())
8581 continue;
8582 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8583 AssertComRC(rc);
8584 }
8585 }
8586
8587 if (!isSessionMachine() && !isSnapshotMachine())
8588 {
8589 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8590 if (mData->mFirstSnapshot)
8591 {
8592 // snapshots tree is protected by machine write lock; strictly
8593 // this isn't necessary here since we're deleting the entire
8594 // machine, but otherwise we assert in Snapshot::uninit()
8595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8596 mData->mFirstSnapshot->uninit();
8597 mData->mFirstSnapshot.setNull();
8598 }
8599
8600 mData->mCurrentSnapshot.setNull();
8601 }
8602
8603 /* free data structures (the essential mData structure is not freed here
8604 * since it may be still in use) */
8605 mMediaData.free();
8606 mStorageControllers.free();
8607 mHWData.free();
8608 mUserData.free();
8609 mSSData.free();
8610}
8611
8612/**
8613 * Returns a pointer to the Machine object for this machine that acts like a
8614 * parent for complex machine data objects such as shared folders, etc.
8615 *
8616 * For primary Machine objects and for SnapshotMachine objects, returns this
8617 * object's pointer itself. For SessionMachine objects, returns the peer
8618 * (primary) machine pointer.
8619 */
8620Machine* Machine::getMachine()
8621{
8622 if (isSessionMachine())
8623 return (Machine*)mPeer;
8624 return this;
8625}
8626
8627/**
8628 * Makes sure that there are no machine state dependents. If necessary, waits
8629 * for the number of dependents to drop to zero.
8630 *
8631 * Make sure this method is called from under this object's write lock to
8632 * guarantee that no new dependents may be added when this method returns
8633 * control to the caller.
8634 *
8635 * @note Locks this object for writing. The lock will be released while waiting
8636 * (if necessary).
8637 *
8638 * @warning To be used only in methods that change the machine state!
8639 */
8640void Machine::ensureNoStateDependencies()
8641{
8642 AssertReturnVoid(isWriteLockOnCurrentThread());
8643
8644 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8645
8646 /* Wait for all state dependents if necessary */
8647 if (mData->mMachineStateDeps != 0)
8648 {
8649 /* lazy semaphore creation */
8650 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8651 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8652
8653 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8654 mData->mMachineStateDeps));
8655
8656 ++mData->mMachineStateChangePending;
8657
8658 /* reset the semaphore before waiting, the last dependent will signal
8659 * it */
8660 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8661
8662 alock.release();
8663
8664 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8665
8666 alock.acquire();
8667
8668 -- mData->mMachineStateChangePending;
8669 }
8670}
8671
8672/**
8673 * Changes the machine state and informs callbacks.
8674 *
8675 * This method is not intended to fail so it either returns S_OK or asserts (and
8676 * returns a failure).
8677 *
8678 * @note Locks this object for writing.
8679 */
8680HRESULT Machine::setMachineState(MachineState_T aMachineState)
8681{
8682 LogFlowThisFuncEnter();
8683 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8684
8685 AutoCaller autoCaller(this);
8686 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8687
8688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8689
8690 /* wait for state dependents to drop to zero */
8691 ensureNoStateDependencies();
8692
8693 if (mData->mMachineState != aMachineState)
8694 {
8695 mData->mMachineState = aMachineState;
8696
8697 RTTimeNow(&mData->mLastStateChange);
8698
8699 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8700 }
8701
8702 LogFlowThisFuncLeave();
8703 return S_OK;
8704}
8705
8706/**
8707 * Searches for a shared folder with the given logical name
8708 * in the collection of shared folders.
8709 *
8710 * @param aName logical name of the shared folder
8711 * @param aSharedFolder where to return the found object
8712 * @param aSetError whether to set the error info if the folder is
8713 * not found
8714 * @return
8715 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8716 *
8717 * @note
8718 * must be called from under the object's lock!
8719 */
8720HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8721 ComObjPtr<SharedFolder> &aSharedFolder,
8722 bool aSetError /* = false */)
8723{
8724 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8725 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8726 it != mHWData->mSharedFolders.end();
8727 ++it)
8728 {
8729 SharedFolder *pSF = *it;
8730 AutoCaller autoCaller(pSF);
8731 if (pSF->getName() == aName)
8732 {
8733 aSharedFolder = pSF;
8734 rc = S_OK;
8735 break;
8736 }
8737 }
8738
8739 if (aSetError && FAILED(rc))
8740 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8741
8742 return rc;
8743}
8744
8745/**
8746 * Initializes all machine instance data from the given settings structures
8747 * from XML. The exception is the machine UUID which needs special handling
8748 * depending on the caller's use case, so the caller needs to set that herself.
8749 *
8750 * This gets called in several contexts during machine initialization:
8751 *
8752 * -- When machine XML exists on disk already and needs to be loaded into memory,
8753 * for example, from registeredInit() to load all registered machines on
8754 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8755 * attached to the machine should be part of some media registry already.
8756 *
8757 * -- During OVF import, when a machine config has been constructed from an
8758 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8759 * ensure that the media listed as attachments in the config (which have
8760 * been imported from the OVF) receive the correct registry ID.
8761 *
8762 * -- During VM cloning.
8763 *
8764 * @param config Machine settings from XML.
8765 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8766 * @return
8767 */
8768HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8769 const Guid *puuidRegistry)
8770{
8771 // copy name, description, OS type, teleporter, UTC etc.
8772 #define DECODE_STR_MAX _1M
8773 mUserData->s = config.machineUserData;
8774
8775 // Decode the Icon overide data from config userdata and set onto Machine.
8776 const char* pszStr = config.machineUserData.ovIcon.c_str();
8777 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8778 if (cbOut > DECODE_STR_MAX)
8779 return setError(E_FAIL,
8780 tr("Icon Data too long.'%d' > '%d'"),
8781 cbOut,
8782 DECODE_STR_MAX);
8783 com::SafeArray<BYTE> iconByte(cbOut);
8784 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8785 if (FAILED(rc))
8786 return setError(E_FAIL,
8787 tr("Failure to Decode Icon Data. '%s' (%d)"),
8788 pszStr,
8789 rc);
8790 COMSETTER(Icon)(ComSafeArrayAsInParam(iconByte));
8791
8792 // look up the object by Id to check it is valid
8793 ComPtr<IGuestOSType> guestOSType;
8794 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8795 guestOSType.asOutParam());
8796 if (FAILED(rc)) return rc;
8797
8798 // stateFile (optional)
8799 if (config.strStateFile.isEmpty())
8800 mSSData->strStateFilePath.setNull();
8801 else
8802 {
8803 Utf8Str stateFilePathFull(config.strStateFile);
8804 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8805 if (RT_FAILURE(vrc))
8806 return setError(E_FAIL,
8807 tr("Invalid saved state file path '%s' (%Rrc)"),
8808 config.strStateFile.c_str(),
8809 vrc);
8810 mSSData->strStateFilePath = stateFilePathFull;
8811 }
8812
8813 // snapshot folder needs special processing so set it again
8814 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8815 if (FAILED(rc)) return rc;
8816
8817 /* Copy the extra data items (Not in any case config is already the same as
8818 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8819 * make sure the extra data map is copied). */
8820 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8821
8822 /* currentStateModified (optional, default is true) */
8823 mData->mCurrentStateModified = config.fCurrentStateModified;
8824
8825 mData->mLastStateChange = config.timeLastStateChange;
8826
8827 /*
8828 * note: all mUserData members must be assigned prior this point because
8829 * we need to commit changes in order to let mUserData be shared by all
8830 * snapshot machine instances.
8831 */
8832 mUserData.commitCopy();
8833
8834 // machine registry, if present (must be loaded before snapshots)
8835 if (config.canHaveOwnMediaRegistry())
8836 {
8837 // determine machine folder
8838 Utf8Str strMachineFolder = getSettingsFileFull();
8839 strMachineFolder.stripFilename();
8840 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8841 config.mediaRegistry,
8842 strMachineFolder);
8843 if (FAILED(rc)) return rc;
8844 }
8845
8846 /* Snapshot node (optional) */
8847 size_t cRootSnapshots;
8848 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8849 {
8850 // there must be only one root snapshot
8851 Assert(cRootSnapshots == 1);
8852
8853 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8854
8855 rc = loadSnapshot(snap,
8856 config.uuidCurrentSnapshot,
8857 NULL); // no parent == first snapshot
8858 if (FAILED(rc)) return rc;
8859 }
8860
8861 // hardware data
8862 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8863 if (FAILED(rc)) return rc;
8864
8865 // load storage controllers
8866 rc = loadStorageControllers(config.storageMachine,
8867 puuidRegistry,
8868 NULL /* puuidSnapshot */);
8869 if (FAILED(rc)) return rc;
8870
8871 /*
8872 * NOTE: the assignment below must be the last thing to do,
8873 * otherwise it will be not possible to change the settings
8874 * somewhere in the code above because all setters will be
8875 * blocked by checkStateDependency(MutableStateDep).
8876 */
8877
8878 /* set the machine state to Aborted or Saved when appropriate */
8879 if (config.fAborted)
8880 {
8881 mSSData->strStateFilePath.setNull();
8882
8883 /* no need to use setMachineState() during init() */
8884 mData->mMachineState = MachineState_Aborted;
8885 }
8886 else if (!mSSData->strStateFilePath.isEmpty())
8887 {
8888 /* no need to use setMachineState() during init() */
8889 mData->mMachineState = MachineState_Saved;
8890 }
8891
8892 // after loading settings, we are no longer different from the XML on disk
8893 mData->flModifications = 0;
8894
8895 return S_OK;
8896}
8897
8898/**
8899 * Recursively loads all snapshots starting from the given.
8900 *
8901 * @param aNode <Snapshot> node.
8902 * @param aCurSnapshotId Current snapshot ID from the settings file.
8903 * @param aParentSnapshot Parent snapshot.
8904 */
8905HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8906 const Guid &aCurSnapshotId,
8907 Snapshot *aParentSnapshot)
8908{
8909 AssertReturn(!isSnapshotMachine(), E_FAIL);
8910 AssertReturn(!isSessionMachine(), E_FAIL);
8911
8912 HRESULT rc = S_OK;
8913
8914 Utf8Str strStateFile;
8915 if (!data.strStateFile.isEmpty())
8916 {
8917 /* optional */
8918 strStateFile = data.strStateFile;
8919 int vrc = calculateFullPath(strStateFile, strStateFile);
8920 if (RT_FAILURE(vrc))
8921 return setError(E_FAIL,
8922 tr("Invalid saved state file path '%s' (%Rrc)"),
8923 strStateFile.c_str(),
8924 vrc);
8925 }
8926
8927 /* create a snapshot machine object */
8928 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8929 pSnapshotMachine.createObject();
8930 rc = pSnapshotMachine->initFromSettings(this,
8931 data.hardware,
8932 &data.debugging,
8933 &data.autostart,
8934 data.storage,
8935 data.uuid.ref(),
8936 strStateFile);
8937 if (FAILED(rc)) return rc;
8938
8939 /* create a snapshot object */
8940 ComObjPtr<Snapshot> pSnapshot;
8941 pSnapshot.createObject();
8942 /* initialize the snapshot */
8943 rc = pSnapshot->init(mParent, // VirtualBox object
8944 data.uuid,
8945 data.strName,
8946 data.strDescription,
8947 data.timestamp,
8948 pSnapshotMachine,
8949 aParentSnapshot);
8950 if (FAILED(rc)) return rc;
8951
8952 /* memorize the first snapshot if necessary */
8953 if (!mData->mFirstSnapshot)
8954 mData->mFirstSnapshot = pSnapshot;
8955
8956 /* memorize the current snapshot when appropriate */
8957 if ( !mData->mCurrentSnapshot
8958 && pSnapshot->getId() == aCurSnapshotId
8959 )
8960 mData->mCurrentSnapshot = pSnapshot;
8961
8962 // now create the children
8963 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8964 it != data.llChildSnapshots.end();
8965 ++it)
8966 {
8967 const settings::Snapshot &childData = *it;
8968 // recurse
8969 rc = loadSnapshot(childData,
8970 aCurSnapshotId,
8971 pSnapshot); // parent = the one we created above
8972 if (FAILED(rc)) return rc;
8973 }
8974
8975 return rc;
8976}
8977
8978/**
8979 * Loads settings into mHWData.
8980 *
8981 * @param data Reference to the hardware settings.
8982 * @param pDbg Pointer to the debugging settings.
8983 * @param pAutostart Pointer to the autostart settings.
8984 */
8985HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8986 const settings::Autostart *pAutostart)
8987{
8988 AssertReturn(!isSessionMachine(), E_FAIL);
8989
8990 HRESULT rc = S_OK;
8991
8992 try
8993 {
8994 /* The hardware version attribute (optional). */
8995 mHWData->mHWVersion = data.strVersion;
8996 mHWData->mHardwareUUID = data.uuid;
8997
8998 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8999 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
9000 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9001 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9002 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9003 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9004 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9005 mHWData->mPAEEnabled = data.fPAE;
9006 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9007 mHWData->mLongMode = data.enmLongMode;
9008 mHWData->mCPUCount = data.cCPUs;
9009 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9010 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9011
9012 // cpu
9013 if (mHWData->mCPUHotPlugEnabled)
9014 {
9015 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9016 it != data.llCpus.end();
9017 ++it)
9018 {
9019 const settings::Cpu &cpu = *it;
9020
9021 mHWData->mCPUAttached[cpu.ulId] = true;
9022 }
9023 }
9024
9025 // cpuid leafs
9026 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9027 it != data.llCpuIdLeafs.end();
9028 ++it)
9029 {
9030 const settings::CpuIdLeaf &leaf = *it;
9031
9032 switch (leaf.ulId)
9033 {
9034 case 0x0:
9035 case 0x1:
9036 case 0x2:
9037 case 0x3:
9038 case 0x4:
9039 case 0x5:
9040 case 0x6:
9041 case 0x7:
9042 case 0x8:
9043 case 0x9:
9044 case 0xA:
9045 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9046 break;
9047
9048 case 0x80000000:
9049 case 0x80000001:
9050 case 0x80000002:
9051 case 0x80000003:
9052 case 0x80000004:
9053 case 0x80000005:
9054 case 0x80000006:
9055 case 0x80000007:
9056 case 0x80000008:
9057 case 0x80000009:
9058 case 0x8000000A:
9059 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9060 break;
9061
9062 default:
9063 /* just ignore */
9064 break;
9065 }
9066 }
9067
9068 mHWData->mMemorySize = data.ulMemorySizeMB;
9069 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9070
9071 // boot order
9072 for (size_t i = 0;
9073 i < RT_ELEMENTS(mHWData->mBootOrder);
9074 i++)
9075 {
9076 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9077 if (it == data.mapBootOrder.end())
9078 mHWData->mBootOrder[i] = DeviceType_Null;
9079 else
9080 mHWData->mBootOrder[i] = it->second;
9081 }
9082
9083 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9084 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9085 mHWData->mMonitorCount = data.cMonitors;
9086 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9087 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9088 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9089 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9090 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9091 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9092 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9093 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9094 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9095 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9096 if (!data.strVideoCaptureFile.isEmpty())
9097 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9098 else
9099 mHWData->mVideoCaptureFile.setNull();
9100 mHWData->mFirmwareType = data.firmwareType;
9101 mHWData->mPointingHIDType = data.pointingHIDType;
9102 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9103 mHWData->mChipsetType = data.chipsetType;
9104 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9105 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9106 mHWData->mHPETEnabled = data.fHPETEnabled;
9107
9108 /* VRDEServer */
9109 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9110 if (FAILED(rc)) return rc;
9111
9112 /* BIOS */
9113 rc = mBIOSSettings->loadSettings(data.biosSettings);
9114 if (FAILED(rc)) return rc;
9115
9116 // Bandwidth control (must come before network adapters)
9117 rc = mBandwidthControl->loadSettings(data.ioSettings);
9118 if (FAILED(rc)) return rc;
9119
9120 /* USB Controller */
9121 rc = mUSBController->loadSettings(data.usbController);
9122 if (FAILED(rc)) return rc;
9123
9124 // network adapters
9125 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9126 uint32_t oldCount = mNetworkAdapters.size();
9127 if (newCount > oldCount)
9128 {
9129 mNetworkAdapters.resize(newCount);
9130 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9131 {
9132 unconst(mNetworkAdapters[slot]).createObject();
9133 mNetworkAdapters[slot]->init(this, slot);
9134 }
9135 }
9136 else if (newCount < oldCount)
9137 mNetworkAdapters.resize(newCount);
9138 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9139 it != data.llNetworkAdapters.end();
9140 ++it)
9141 {
9142 const settings::NetworkAdapter &nic = *it;
9143
9144 /* slot unicity is guaranteed by XML Schema */
9145 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9146 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9147 if (FAILED(rc)) return rc;
9148 }
9149
9150 // serial ports
9151 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9152 it != data.llSerialPorts.end();
9153 ++it)
9154 {
9155 const settings::SerialPort &s = *it;
9156
9157 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9158 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9159 if (FAILED(rc)) return rc;
9160 }
9161
9162 // parallel ports (optional)
9163 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9164 it != data.llParallelPorts.end();
9165 ++it)
9166 {
9167 const settings::ParallelPort &p = *it;
9168
9169 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9170 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9171 if (FAILED(rc)) return rc;
9172 }
9173
9174 /* AudioAdapter */
9175 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9176 if (FAILED(rc)) return rc;
9177
9178 /* Shared folders */
9179 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9180 it != data.llSharedFolders.end();
9181 ++it)
9182 {
9183 const settings::SharedFolder &sf = *it;
9184
9185 ComObjPtr<SharedFolder> sharedFolder;
9186 /* Check for double entries. Not allowed! */
9187 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9188 if (SUCCEEDED(rc))
9189 return setError(VBOX_E_OBJECT_IN_USE,
9190 tr("Shared folder named '%s' already exists"),
9191 sf.strName.c_str());
9192
9193 /* Create the new shared folder. Don't break on error. This will be
9194 * reported when the machine starts. */
9195 sharedFolder.createObject();
9196 rc = sharedFolder->init(getMachine(),
9197 sf.strName,
9198 sf.strHostPath,
9199 RT_BOOL(sf.fWritable),
9200 RT_BOOL(sf.fAutoMount),
9201 false /* fFailOnError */);
9202 if (FAILED(rc)) return rc;
9203 mHWData->mSharedFolders.push_back(sharedFolder);
9204 }
9205
9206 // Clipboard
9207 mHWData->mClipboardMode = data.clipboardMode;
9208
9209 // drag'n'drop
9210 mHWData->mDragAndDropMode = data.dragAndDropMode;
9211
9212 // guest settings
9213 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9214
9215 // IO settings
9216 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9217 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9218
9219 // Host PCI devices
9220 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9221 it != data.pciAttachments.end();
9222 ++it)
9223 {
9224 const settings::HostPCIDeviceAttachment &hpda = *it;
9225 ComObjPtr<PCIDeviceAttachment> pda;
9226
9227 pda.createObject();
9228 pda->loadSettings(this, hpda);
9229 mHWData->mPCIDeviceAssignments.push_back(pda);
9230 }
9231
9232 /*
9233 * (The following isn't really real hardware, but it lives in HWData
9234 * for reasons of convenience.)
9235 */
9236
9237#ifdef VBOX_WITH_GUEST_PROPS
9238 /* Guest properties (optional) */
9239 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9240 it != data.llGuestProperties.end();
9241 ++it)
9242 {
9243 const settings::GuestProperty &prop = *it;
9244 uint32_t fFlags = guestProp::NILFLAG;
9245 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9246 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9247 mHWData->mGuestProperties[prop.strName] = property;
9248 }
9249
9250 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9251#endif /* VBOX_WITH_GUEST_PROPS defined */
9252
9253 rc = loadDebugging(pDbg);
9254 if (FAILED(rc))
9255 return rc;
9256
9257 mHWData->mAutostart = *pAutostart;
9258
9259 /* default frontend */
9260 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9261 }
9262 catch(std::bad_alloc &)
9263 {
9264 return E_OUTOFMEMORY;
9265 }
9266
9267 AssertComRC(rc);
9268 return rc;
9269}
9270
9271/**
9272 * Called from Machine::loadHardware() to load the debugging settings of the
9273 * machine.
9274 *
9275 * @param pDbg Pointer to the settings.
9276 */
9277HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9278{
9279 mHWData->mDebugging = *pDbg;
9280 /* no more processing currently required, this will probably change. */
9281 return S_OK;
9282}
9283
9284/**
9285 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9286 *
9287 * @param data
9288 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9289 * @param puuidSnapshot
9290 * @return
9291 */
9292HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9293 const Guid *puuidRegistry,
9294 const Guid *puuidSnapshot)
9295{
9296 AssertReturn(!isSessionMachine(), E_FAIL);
9297
9298 HRESULT rc = S_OK;
9299
9300 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9301 it != data.llStorageControllers.end();
9302 ++it)
9303 {
9304 const settings::StorageController &ctlData = *it;
9305
9306 ComObjPtr<StorageController> pCtl;
9307 /* Try to find one with the name first. */
9308 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9309 if (SUCCEEDED(rc))
9310 return setError(VBOX_E_OBJECT_IN_USE,
9311 tr("Storage controller named '%s' already exists"),
9312 ctlData.strName.c_str());
9313
9314 pCtl.createObject();
9315 rc = pCtl->init(this,
9316 ctlData.strName,
9317 ctlData.storageBus,
9318 ctlData.ulInstance,
9319 ctlData.fBootable);
9320 if (FAILED(rc)) return rc;
9321
9322 mStorageControllers->push_back(pCtl);
9323
9324 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9325 if (FAILED(rc)) return rc;
9326
9327 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9328 if (FAILED(rc)) return rc;
9329
9330 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9331 if (FAILED(rc)) return rc;
9332
9333 /* Set IDE emulation settings (only for AHCI controller). */
9334 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9335 {
9336 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9337 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9338 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9339 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9340 )
9341 return rc;
9342 }
9343
9344 /* Load the attached devices now. */
9345 rc = loadStorageDevices(pCtl,
9346 ctlData,
9347 puuidRegistry,
9348 puuidSnapshot);
9349 if (FAILED(rc)) return rc;
9350 }
9351
9352 return S_OK;
9353}
9354
9355/**
9356 * Called from loadStorageControllers for a controller's devices.
9357 *
9358 * @param aStorageController
9359 * @param data
9360 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9361 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9362 * @return
9363 */
9364HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9365 const settings::StorageController &data,
9366 const Guid *puuidRegistry,
9367 const Guid *puuidSnapshot)
9368{
9369 HRESULT rc = S_OK;
9370
9371 /* paranoia: detect duplicate attachments */
9372 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9373 it != data.llAttachedDevices.end();
9374 ++it)
9375 {
9376 const settings::AttachedDevice &ad = *it;
9377
9378 for (settings::AttachedDevicesList::const_iterator it2 = it;
9379 it2 != data.llAttachedDevices.end();
9380 ++it2)
9381 {
9382 if (it == it2)
9383 continue;
9384
9385 const settings::AttachedDevice &ad2 = *it2;
9386
9387 if ( ad.lPort == ad2.lPort
9388 && ad.lDevice == ad2.lDevice)
9389 {
9390 return setError(E_FAIL,
9391 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9392 aStorageController->getName().c_str(),
9393 ad.lPort,
9394 ad.lDevice,
9395 mUserData->s.strName.c_str());
9396 }
9397 }
9398 }
9399
9400 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9401 it != data.llAttachedDevices.end();
9402 ++it)
9403 {
9404 const settings::AttachedDevice &dev = *it;
9405 ComObjPtr<Medium> medium;
9406
9407 switch (dev.deviceType)
9408 {
9409 case DeviceType_Floppy:
9410 case DeviceType_DVD:
9411 if (dev.strHostDriveSrc.isNotEmpty())
9412 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9413 else
9414 rc = mParent->findRemoveableMedium(dev.deviceType,
9415 dev.uuid,
9416 false /* fRefresh */,
9417 false /* aSetError */,
9418 medium);
9419 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9420 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9421 rc = S_OK;
9422 break;
9423
9424 case DeviceType_HardDisk:
9425 {
9426 /* find a hard disk by UUID */
9427 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9428 if (FAILED(rc))
9429 {
9430 if (isSnapshotMachine())
9431 {
9432 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9433 // so the user knows that the bad disk is in a snapshot somewhere
9434 com::ErrorInfo info;
9435 return setError(E_FAIL,
9436 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9437 puuidSnapshot->raw(),
9438 info.getText().raw());
9439 }
9440 else
9441 return rc;
9442 }
9443
9444 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9445
9446 if (medium->getType() == MediumType_Immutable)
9447 {
9448 if (isSnapshotMachine())
9449 return setError(E_FAIL,
9450 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9451 "of the virtual machine '%s' ('%s')"),
9452 medium->getLocationFull().c_str(),
9453 dev.uuid.raw(),
9454 puuidSnapshot->raw(),
9455 mUserData->s.strName.c_str(),
9456 mData->m_strConfigFileFull.c_str());
9457
9458 return setError(E_FAIL,
9459 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9460 medium->getLocationFull().c_str(),
9461 dev.uuid.raw(),
9462 mUserData->s.strName.c_str(),
9463 mData->m_strConfigFileFull.c_str());
9464 }
9465
9466 if (medium->getType() == MediumType_MultiAttach)
9467 {
9468 if (isSnapshotMachine())
9469 return setError(E_FAIL,
9470 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9471 "of the virtual machine '%s' ('%s')"),
9472 medium->getLocationFull().c_str(),
9473 dev.uuid.raw(),
9474 puuidSnapshot->raw(),
9475 mUserData->s.strName.c_str(),
9476 mData->m_strConfigFileFull.c_str());
9477
9478 return setError(E_FAIL,
9479 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9480 medium->getLocationFull().c_str(),
9481 dev.uuid.raw(),
9482 mUserData->s.strName.c_str(),
9483 mData->m_strConfigFileFull.c_str());
9484 }
9485
9486 if ( !isSnapshotMachine()
9487 && medium->getChildren().size() != 0
9488 )
9489 return setError(E_FAIL,
9490 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9491 "because it has %d differencing child hard disks"),
9492 medium->getLocationFull().c_str(),
9493 dev.uuid.raw(),
9494 mUserData->s.strName.c_str(),
9495 mData->m_strConfigFileFull.c_str(),
9496 medium->getChildren().size());
9497
9498 if (findAttachment(mMediaData->mAttachments,
9499 medium))
9500 return setError(E_FAIL,
9501 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9502 medium->getLocationFull().c_str(),
9503 dev.uuid.raw(),
9504 mUserData->s.strName.c_str(),
9505 mData->m_strConfigFileFull.c_str());
9506
9507 break;
9508 }
9509
9510 default:
9511 return setError(E_FAIL,
9512 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9513 medium->getLocationFull().c_str(),
9514 mUserData->s.strName.c_str(),
9515 mData->m_strConfigFileFull.c_str());
9516 }
9517
9518 if (FAILED(rc))
9519 break;
9520
9521 /* Bandwidth groups are loaded at this point. */
9522 ComObjPtr<BandwidthGroup> pBwGroup;
9523
9524 if (!dev.strBwGroup.isEmpty())
9525 {
9526 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9527 if (FAILED(rc))
9528 return setError(E_FAIL,
9529 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9530 medium->getLocationFull().c_str(),
9531 dev.strBwGroup.c_str(),
9532 mUserData->s.strName.c_str(),
9533 mData->m_strConfigFileFull.c_str());
9534 pBwGroup->reference();
9535 }
9536
9537 const Bstr controllerName = aStorageController->getName();
9538 ComObjPtr<MediumAttachment> pAttachment;
9539 pAttachment.createObject();
9540 rc = pAttachment->init(this,
9541 medium,
9542 controllerName,
9543 dev.lPort,
9544 dev.lDevice,
9545 dev.deviceType,
9546 false,
9547 dev.fPassThrough,
9548 dev.fTempEject,
9549 dev.fNonRotational,
9550 dev.fDiscard,
9551 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9552 if (FAILED(rc)) break;
9553
9554 /* associate the medium with this machine and snapshot */
9555 if (!medium.isNull())
9556 {
9557 AutoCaller medCaller(medium);
9558 if (FAILED(medCaller.rc())) return medCaller.rc();
9559 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9560
9561 if (isSnapshotMachine())
9562 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9563 else
9564 rc = medium->addBackReference(mData->mUuid);
9565 /* If the medium->addBackReference fails it sets an appropriate
9566 * error message, so no need to do any guesswork here. */
9567
9568 if (puuidRegistry)
9569 // caller wants registry ID to be set on all attached media (OVF import case)
9570 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9571 }
9572
9573 if (FAILED(rc))
9574 break;
9575
9576 /* back up mMediaData to let registeredInit() properly rollback on failure
9577 * (= limited accessibility) */
9578 setModified(IsModified_Storage);
9579 mMediaData.backup();
9580 mMediaData->mAttachments.push_back(pAttachment);
9581 }
9582
9583 return rc;
9584}
9585
9586/**
9587 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9588 *
9589 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9590 * @param aSnapshot where to return the found snapshot
9591 * @param aSetError true to set extended error info on failure
9592 */
9593HRESULT Machine::findSnapshotById(const Guid &aId,
9594 ComObjPtr<Snapshot> &aSnapshot,
9595 bool aSetError /* = false */)
9596{
9597 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9598
9599 if (!mData->mFirstSnapshot)
9600 {
9601 if (aSetError)
9602 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9603 return E_FAIL;
9604 }
9605
9606 if (aId.isZero())
9607 aSnapshot = mData->mFirstSnapshot;
9608 else
9609 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9610
9611 if (!aSnapshot)
9612 {
9613 if (aSetError)
9614 return setError(E_FAIL,
9615 tr("Could not find a snapshot with UUID {%s}"),
9616 aId.toString().c_str());
9617 return E_FAIL;
9618 }
9619
9620 return S_OK;
9621}
9622
9623/**
9624 * Returns the snapshot with the given name or fails of no such snapshot.
9625 *
9626 * @param aName snapshot name to find
9627 * @param aSnapshot where to return the found snapshot
9628 * @param aSetError true to set extended error info on failure
9629 */
9630HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9631 ComObjPtr<Snapshot> &aSnapshot,
9632 bool aSetError /* = false */)
9633{
9634 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9635
9636 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9637
9638 if (!mData->mFirstSnapshot)
9639 {
9640 if (aSetError)
9641 return setError(VBOX_E_OBJECT_NOT_FOUND,
9642 tr("This machine does not have any snapshots"));
9643 return VBOX_E_OBJECT_NOT_FOUND;
9644 }
9645
9646 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9647
9648 if (!aSnapshot)
9649 {
9650 if (aSetError)
9651 return setError(VBOX_E_OBJECT_NOT_FOUND,
9652 tr("Could not find a snapshot named '%s'"), strName.c_str());
9653 return VBOX_E_OBJECT_NOT_FOUND;
9654 }
9655
9656 return S_OK;
9657}
9658
9659/**
9660 * Returns a storage controller object with the given name.
9661 *
9662 * @param aName storage controller name to find
9663 * @param aStorageController where to return the found storage controller
9664 * @param aSetError true to set extended error info on failure
9665 */
9666HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9667 ComObjPtr<StorageController> &aStorageController,
9668 bool aSetError /* = false */)
9669{
9670 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9671
9672 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9673 it != mStorageControllers->end();
9674 ++it)
9675 {
9676 if ((*it)->getName() == aName)
9677 {
9678 aStorageController = (*it);
9679 return S_OK;
9680 }
9681 }
9682
9683 if (aSetError)
9684 return setError(VBOX_E_OBJECT_NOT_FOUND,
9685 tr("Could not find a storage controller named '%s'"),
9686 aName.c_str());
9687 return VBOX_E_OBJECT_NOT_FOUND;
9688}
9689
9690HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9691 MediaData::AttachmentList &atts)
9692{
9693 AutoCaller autoCaller(this);
9694 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9695
9696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9697
9698 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9699 it != mMediaData->mAttachments.end();
9700 ++it)
9701 {
9702 const ComObjPtr<MediumAttachment> &pAtt = *it;
9703
9704 // should never happen, but deal with NULL pointers in the list.
9705 AssertStmt(!pAtt.isNull(), continue);
9706
9707 // getControllerName() needs caller+read lock
9708 AutoCaller autoAttCaller(pAtt);
9709 if (FAILED(autoAttCaller.rc()))
9710 {
9711 atts.clear();
9712 return autoAttCaller.rc();
9713 }
9714 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9715
9716 if (pAtt->getControllerName() == aName)
9717 atts.push_back(pAtt);
9718 }
9719
9720 return S_OK;
9721}
9722
9723/**
9724 * Helper for #saveSettings. Cares about renaming the settings directory and
9725 * file if the machine name was changed and about creating a new settings file
9726 * if this is a new machine.
9727 *
9728 * @note Must be never called directly but only from #saveSettings().
9729 */
9730HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9731{
9732 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9733
9734 HRESULT rc = S_OK;
9735
9736 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9737
9738 /// @todo need to handle primary group change, too
9739
9740 /* attempt to rename the settings file if machine name is changed */
9741 if ( mUserData->s.fNameSync
9742 && mUserData.isBackedUp()
9743 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9744 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9745 )
9746 {
9747 bool dirRenamed = false;
9748 bool fileRenamed = false;
9749
9750 Utf8Str configFile, newConfigFile;
9751 Utf8Str configFilePrev, newConfigFilePrev;
9752 Utf8Str configDir, newConfigDir;
9753
9754 do
9755 {
9756 int vrc = VINF_SUCCESS;
9757
9758 Utf8Str name = mUserData.backedUpData()->s.strName;
9759 Utf8Str newName = mUserData->s.strName;
9760 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9761 if (group == "/")
9762 group.setNull();
9763 Utf8Str newGroup = mUserData->s.llGroups.front();
9764 if (newGroup == "/")
9765 newGroup.setNull();
9766
9767 configFile = mData->m_strConfigFileFull;
9768
9769 /* first, rename the directory if it matches the group and machine name */
9770 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9771 group.c_str(), RTPATH_DELIMITER, name.c_str());
9772 /** @todo hack, make somehow use of ComposeMachineFilename */
9773 if (mUserData->s.fDirectoryIncludesUUID)
9774 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9775 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9776 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9777 /** @todo hack, make somehow use of ComposeMachineFilename */
9778 if (mUserData->s.fDirectoryIncludesUUID)
9779 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9780 configDir = configFile;
9781 configDir.stripFilename();
9782 newConfigDir = configDir;
9783 if ( configDir.length() >= groupPlusName.length()
9784 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9785 {
9786 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9787 Utf8Str newConfigBaseDir(newConfigDir);
9788 newConfigDir.append(newGroupPlusName);
9789 /* consistency: use \ if appropriate on the platform */
9790 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9791 /* new dir and old dir cannot be equal here because of 'if'
9792 * above and because name != newName */
9793 Assert(configDir != newConfigDir);
9794 if (!fSettingsFileIsNew)
9795 {
9796 /* perform real rename only if the machine is not new */
9797 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9798 if ( vrc == VERR_FILE_NOT_FOUND
9799 || vrc == VERR_PATH_NOT_FOUND)
9800 {
9801 /* create the parent directory, then retry renaming */
9802 Utf8Str parent(newConfigDir);
9803 parent.stripFilename();
9804 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9805 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9806 }
9807 if (RT_FAILURE(vrc))
9808 {
9809 rc = setError(E_FAIL,
9810 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9811 configDir.c_str(),
9812 newConfigDir.c_str(),
9813 vrc);
9814 break;
9815 }
9816 /* delete subdirectories which are no longer needed */
9817 Utf8Str dir(configDir);
9818 dir.stripFilename();
9819 while (dir != newConfigBaseDir && dir != ".")
9820 {
9821 vrc = RTDirRemove(dir.c_str());
9822 if (RT_FAILURE(vrc))
9823 break;
9824 dir.stripFilename();
9825 }
9826 dirRenamed = true;
9827 }
9828 }
9829
9830 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9831 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9832
9833 /* then try to rename the settings file itself */
9834 if (newConfigFile != configFile)
9835 {
9836 /* get the path to old settings file in renamed directory */
9837 configFile = Utf8StrFmt("%s%c%s",
9838 newConfigDir.c_str(),
9839 RTPATH_DELIMITER,
9840 RTPathFilename(configFile.c_str()));
9841 if (!fSettingsFileIsNew)
9842 {
9843 /* perform real rename only if the machine is not new */
9844 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9845 if (RT_FAILURE(vrc))
9846 {
9847 rc = setError(E_FAIL,
9848 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9849 configFile.c_str(),
9850 newConfigFile.c_str(),
9851 vrc);
9852 break;
9853 }
9854 fileRenamed = true;
9855 configFilePrev = configFile;
9856 configFilePrev += "-prev";
9857 newConfigFilePrev = newConfigFile;
9858 newConfigFilePrev += "-prev";
9859 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9860 }
9861 }
9862
9863 // update m_strConfigFileFull amd mConfigFile
9864 mData->m_strConfigFileFull = newConfigFile;
9865 // compute the relative path too
9866 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9867
9868 // store the old and new so that VirtualBox::saveSettings() can update
9869 // the media registry
9870 if ( mData->mRegistered
9871 && configDir != newConfigDir)
9872 {
9873 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9874
9875 if (pfNeedsGlobalSaveSettings)
9876 *pfNeedsGlobalSaveSettings = true;
9877 }
9878
9879 // in the saved state file path, replace the old directory with the new directory
9880 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9881 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9882
9883 // and do the same thing for the saved state file paths of all the online snapshots
9884 if (mData->mFirstSnapshot)
9885 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9886 newConfigDir.c_str());
9887 }
9888 while (0);
9889
9890 if (FAILED(rc))
9891 {
9892 /* silently try to rename everything back */
9893 if (fileRenamed)
9894 {
9895 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9896 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9897 }
9898 if (dirRenamed)
9899 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9900 }
9901
9902 if (FAILED(rc)) return rc;
9903 }
9904
9905 if (fSettingsFileIsNew)
9906 {
9907 /* create a virgin config file */
9908 int vrc = VINF_SUCCESS;
9909
9910 /* ensure the settings directory exists */
9911 Utf8Str path(mData->m_strConfigFileFull);
9912 path.stripFilename();
9913 if (!RTDirExists(path.c_str()))
9914 {
9915 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9916 if (RT_FAILURE(vrc))
9917 {
9918 return setError(E_FAIL,
9919 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9920 path.c_str(),
9921 vrc);
9922 }
9923 }
9924
9925 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9926 path = Utf8Str(mData->m_strConfigFileFull);
9927 RTFILE f = NIL_RTFILE;
9928 vrc = RTFileOpen(&f, path.c_str(),
9929 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9930 if (RT_FAILURE(vrc))
9931 return setError(E_FAIL,
9932 tr("Could not create the settings file '%s' (%Rrc)"),
9933 path.c_str(),
9934 vrc);
9935 RTFileClose(f);
9936 }
9937
9938 return rc;
9939}
9940
9941/**
9942 * Saves and commits machine data, user data and hardware data.
9943 *
9944 * Note that on failure, the data remains uncommitted.
9945 *
9946 * @a aFlags may combine the following flags:
9947 *
9948 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9949 * Used when saving settings after an operation that makes them 100%
9950 * correspond to the settings from the current snapshot.
9951 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9952 * #isReallyModified() returns false. This is necessary for cases when we
9953 * change machine data directly, not through the backup()/commit() mechanism.
9954 * - SaveS_Force: settings will be saved without doing a deep compare of the
9955 * settings structures. This is used when this is called because snapshots
9956 * have changed to avoid the overhead of the deep compare.
9957 *
9958 * @note Must be called from under this object's write lock. Locks children for
9959 * writing.
9960 *
9961 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9962 * initialized to false and that will be set to true by this function if
9963 * the caller must invoke VirtualBox::saveSettings() because the global
9964 * settings have changed. This will happen if a machine rename has been
9965 * saved and the global machine and media registries will therefore need
9966 * updating.
9967 */
9968HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9969 int aFlags /*= 0*/)
9970{
9971 LogFlowThisFuncEnter();
9972
9973 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9974
9975 /* make sure child objects are unable to modify the settings while we are
9976 * saving them */
9977 ensureNoStateDependencies();
9978
9979 AssertReturn(!isSnapshotMachine(),
9980 E_FAIL);
9981
9982 HRESULT rc = S_OK;
9983 bool fNeedsWrite = false;
9984
9985 /* First, prepare to save settings. It will care about renaming the
9986 * settings directory and file if the machine name was changed and about
9987 * creating a new settings file if this is a new machine. */
9988 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9989 if (FAILED(rc)) return rc;
9990
9991 // keep a pointer to the current settings structures
9992 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9993 settings::MachineConfigFile *pNewConfig = NULL;
9994
9995 try
9996 {
9997 // make a fresh one to have everyone write stuff into
9998 pNewConfig = new settings::MachineConfigFile(NULL);
9999 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10000
10001 // now go and copy all the settings data from COM to the settings structures
10002 // (this calles saveSettings() on all the COM objects in the machine)
10003 copyMachineDataToSettings(*pNewConfig);
10004
10005 if (aFlags & SaveS_ResetCurStateModified)
10006 {
10007 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10008 mData->mCurrentStateModified = FALSE;
10009 fNeedsWrite = true; // always, no need to compare
10010 }
10011 else if (aFlags & SaveS_Force)
10012 {
10013 fNeedsWrite = true; // always, no need to compare
10014 }
10015 else
10016 {
10017 if (!mData->mCurrentStateModified)
10018 {
10019 // do a deep compare of the settings that we just saved with the settings
10020 // previously stored in the config file; this invokes MachineConfigFile::operator==
10021 // which does a deep compare of all the settings, which is expensive but less expensive
10022 // than writing out XML in vain
10023 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10024
10025 // could still be modified if any settings changed
10026 mData->mCurrentStateModified = fAnySettingsChanged;
10027
10028 fNeedsWrite = fAnySettingsChanged;
10029 }
10030 else
10031 fNeedsWrite = true;
10032 }
10033
10034 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10035
10036 if (fNeedsWrite)
10037 // now spit it all out!
10038 pNewConfig->write(mData->m_strConfigFileFull);
10039
10040 mData->pMachineConfigFile = pNewConfig;
10041 delete pOldConfig;
10042 commit();
10043
10044 // after saving settings, we are no longer different from the XML on disk
10045 mData->flModifications = 0;
10046 }
10047 catch (HRESULT err)
10048 {
10049 // we assume that error info is set by the thrower
10050 rc = err;
10051
10052 // restore old config
10053 delete pNewConfig;
10054 mData->pMachineConfigFile = pOldConfig;
10055 }
10056 catch (...)
10057 {
10058 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10059 }
10060
10061 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10062 {
10063 /* Fire the data change event, even on failure (since we've already
10064 * committed all data). This is done only for SessionMachines because
10065 * mutable Machine instances are always not registered (i.e. private
10066 * to the client process that creates them) and thus don't need to
10067 * inform callbacks. */
10068 if (isSessionMachine())
10069 mParent->onMachineDataChange(mData->mUuid);
10070 }
10071
10072 LogFlowThisFunc(("rc=%08X\n", rc));
10073 LogFlowThisFuncLeave();
10074 return rc;
10075}
10076
10077/**
10078 * Implementation for saving the machine settings into the given
10079 * settings::MachineConfigFile instance. This copies machine extradata
10080 * from the previous machine config file in the instance data, if any.
10081 *
10082 * This gets called from two locations:
10083 *
10084 * -- Machine::saveSettings(), during the regular XML writing;
10085 *
10086 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10087 * exported to OVF and we write the VirtualBox proprietary XML
10088 * into a <vbox:Machine> tag.
10089 *
10090 * This routine fills all the fields in there, including snapshots, *except*
10091 * for the following:
10092 *
10093 * -- fCurrentStateModified. There is some special logic associated with that.
10094 *
10095 * The caller can then call MachineConfigFile::write() or do something else
10096 * with it.
10097 *
10098 * Caller must hold the machine lock!
10099 *
10100 * This throws XML errors and HRESULT, so the caller must have a catch block!
10101 */
10102void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10103{
10104 // deep copy extradata
10105 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10106
10107 config.uuid = mData->mUuid;
10108
10109 // copy name, description, OS type, teleport, UTC etc.
10110 config.machineUserData = mUserData->s;
10111
10112 // Encode the Icon Override data from Machine and store on config userdata.
10113 com::SafeArray<BYTE> iconByte;
10114 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10115 ssize_t cbData = iconByte.size();
10116 if (cbData > 0)
10117 {
10118 ssize_t cchOut = RTBase64EncodedLength(cbData);
10119 Utf8Str strIconData;
10120 strIconData.reserve(cchOut+1);
10121 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10122 strIconData.mutableRaw(), strIconData.capacity(),
10123 NULL);
10124 if (RT_FAILURE(vrc))
10125 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10126 strIconData.jolt();
10127 config.machineUserData.ovIcon = strIconData;
10128 }
10129 else
10130 config.machineUserData.ovIcon.setNull();
10131
10132 if ( mData->mMachineState == MachineState_Saved
10133 || mData->mMachineState == MachineState_Restoring
10134 // when deleting a snapshot we may or may not have a saved state in the current state,
10135 // so let's not assert here please
10136 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10137 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10138 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10139 && (!mSSData->strStateFilePath.isEmpty())
10140 )
10141 )
10142 {
10143 Assert(!mSSData->strStateFilePath.isEmpty());
10144 /* try to make the file name relative to the settings file dir */
10145 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10146 }
10147 else
10148 {
10149 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10150 config.strStateFile.setNull();
10151 }
10152
10153 if (mData->mCurrentSnapshot)
10154 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10155 else
10156 config.uuidCurrentSnapshot.clear();
10157
10158 config.timeLastStateChange = mData->mLastStateChange;
10159 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10160 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10161
10162 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10163 if (FAILED(rc)) throw rc;
10164
10165 rc = saveStorageControllers(config.storageMachine);
10166 if (FAILED(rc)) throw rc;
10167
10168 // save machine's media registry if this is VirtualBox 4.0 or later
10169 if (config.canHaveOwnMediaRegistry())
10170 {
10171 // determine machine folder
10172 Utf8Str strMachineFolder = getSettingsFileFull();
10173 strMachineFolder.stripFilename();
10174 mParent->saveMediaRegistry(config.mediaRegistry,
10175 getId(), // only media with registry ID == machine UUID
10176 strMachineFolder);
10177 // this throws HRESULT
10178 }
10179
10180 // save snapshots
10181 rc = saveAllSnapshots(config);
10182 if (FAILED(rc)) throw rc;
10183}
10184
10185/**
10186 * Saves all snapshots of the machine into the given machine config file. Called
10187 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10188 * @param config
10189 * @return
10190 */
10191HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10192{
10193 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10194
10195 HRESULT rc = S_OK;
10196
10197 try
10198 {
10199 config.llFirstSnapshot.clear();
10200
10201 if (mData->mFirstSnapshot)
10202 {
10203 settings::Snapshot snapNew;
10204 config.llFirstSnapshot.push_back(snapNew);
10205
10206 // get reference to the fresh copy of the snapshot on the list and
10207 // work on that copy directly to avoid excessive copying later
10208 settings::Snapshot &snap = config.llFirstSnapshot.front();
10209
10210 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10211 if (FAILED(rc)) throw rc;
10212 }
10213
10214// if (mType == IsSessionMachine)
10215// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10216
10217 }
10218 catch (HRESULT err)
10219 {
10220 /* we assume that error info is set by the thrower */
10221 rc = err;
10222 }
10223 catch (...)
10224 {
10225 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10226 }
10227
10228 return rc;
10229}
10230
10231/**
10232 * Saves the VM hardware configuration. It is assumed that the
10233 * given node is empty.
10234 *
10235 * @param data Reference to the settings object for the hardware config.
10236 * @param pDbg Pointer to the settings object for the debugging config
10237 * which happens to live in mHWData.
10238 * @param pAutostart Pointer to the settings object for the autostart config
10239 * which happens to live in mHWData.
10240 */
10241HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10242 settings::Autostart *pAutostart)
10243{
10244 HRESULT rc = S_OK;
10245
10246 try
10247 {
10248 /* The hardware version attribute (optional).
10249 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10250 if ( mHWData->mHWVersion == "1"
10251 && mSSData->strStateFilePath.isEmpty()
10252 )
10253 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. */
10254
10255 data.strVersion = mHWData->mHWVersion;
10256 data.uuid = mHWData->mHardwareUUID;
10257
10258 // CPU
10259 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10260 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10261 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10262 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10263 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10264 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10265 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10266 data.fPAE = !!mHWData->mPAEEnabled;
10267 data.enmLongMode = mHWData->mLongMode;
10268 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10269
10270 /* Standard and Extended CPUID leafs. */
10271 data.llCpuIdLeafs.clear();
10272 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10273 {
10274 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10275 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10276 }
10277 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10278 {
10279 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10280 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10281 }
10282
10283 data.cCPUs = mHWData->mCPUCount;
10284 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10285 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10286
10287 data.llCpus.clear();
10288 if (data.fCpuHotPlug)
10289 {
10290 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10291 {
10292 if (mHWData->mCPUAttached[idx])
10293 {
10294 settings::Cpu cpu;
10295 cpu.ulId = idx;
10296 data.llCpus.push_back(cpu);
10297 }
10298 }
10299 }
10300
10301 // memory
10302 data.ulMemorySizeMB = mHWData->mMemorySize;
10303 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10304
10305 // firmware
10306 data.firmwareType = mHWData->mFirmwareType;
10307
10308 // HID
10309 data.pointingHIDType = mHWData->mPointingHIDType;
10310 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10311
10312 // chipset
10313 data.chipsetType = mHWData->mChipsetType;
10314
10315 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10316 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10317
10318 // HPET
10319 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10320
10321 // boot order
10322 data.mapBootOrder.clear();
10323 for (size_t i = 0;
10324 i < RT_ELEMENTS(mHWData->mBootOrder);
10325 ++i)
10326 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10327
10328 // display
10329 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10330 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10331 data.cMonitors = mHWData->mMonitorCount;
10332 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10333 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10334 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10335 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10336 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10337 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10338 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10339 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10340 {
10341 if (mHWData->maVideoCaptureScreens[i])
10342 ASMBitSet(&data.u64VideoCaptureScreens, i);
10343 else
10344 ASMBitClear(&data.u64VideoCaptureScreens, i);
10345 }
10346 /* store relative video capture file if possible */
10347 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10348
10349 /* VRDEServer settings (optional) */
10350 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10351 if (FAILED(rc)) throw rc;
10352
10353 /* BIOS (required) */
10354 rc = mBIOSSettings->saveSettings(data.biosSettings);
10355 if (FAILED(rc)) throw rc;
10356
10357 /* USB Controller (required) */
10358 rc = mUSBController->saveSettings(data.usbController);
10359 if (FAILED(rc)) throw rc;
10360
10361 /* Network adapters (required) */
10362 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10363 data.llNetworkAdapters.clear();
10364 /* Write out only the nominal number of network adapters for this
10365 * chipset type. Since Machine::commit() hasn't been called there
10366 * may be extra NIC settings in the vector. */
10367 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10368 {
10369 settings::NetworkAdapter nic;
10370 nic.ulSlot = slot;
10371 /* paranoia check... must not be NULL, but must not crash either. */
10372 if (mNetworkAdapters[slot])
10373 {
10374 rc = mNetworkAdapters[slot]->saveSettings(nic);
10375 if (FAILED(rc)) throw rc;
10376
10377 data.llNetworkAdapters.push_back(nic);
10378 }
10379 }
10380
10381 /* Serial ports */
10382 data.llSerialPorts.clear();
10383 for (ULONG slot = 0;
10384 slot < RT_ELEMENTS(mSerialPorts);
10385 ++slot)
10386 {
10387 settings::SerialPort s;
10388 s.ulSlot = slot;
10389 rc = mSerialPorts[slot]->saveSettings(s);
10390 if (FAILED(rc)) return rc;
10391
10392 data.llSerialPorts.push_back(s);
10393 }
10394
10395 /* Parallel ports */
10396 data.llParallelPorts.clear();
10397 for (ULONG slot = 0;
10398 slot < RT_ELEMENTS(mParallelPorts);
10399 ++slot)
10400 {
10401 settings::ParallelPort p;
10402 p.ulSlot = slot;
10403 rc = mParallelPorts[slot]->saveSettings(p);
10404 if (FAILED(rc)) return rc;
10405
10406 data.llParallelPorts.push_back(p);
10407 }
10408
10409 /* Audio adapter */
10410 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10411 if (FAILED(rc)) return rc;
10412
10413 /* Shared folders */
10414 data.llSharedFolders.clear();
10415 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10416 it != mHWData->mSharedFolders.end();
10417 ++it)
10418 {
10419 SharedFolder *pSF = *it;
10420 AutoCaller sfCaller(pSF);
10421 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10422 settings::SharedFolder sf;
10423 sf.strName = pSF->getName();
10424 sf.strHostPath = pSF->getHostPath();
10425 sf.fWritable = !!pSF->isWritable();
10426 sf.fAutoMount = !!pSF->isAutoMounted();
10427
10428 data.llSharedFolders.push_back(sf);
10429 }
10430
10431 // clipboard
10432 data.clipboardMode = mHWData->mClipboardMode;
10433
10434 // drag'n'drop
10435 data.dragAndDropMode = mHWData->mDragAndDropMode;
10436
10437 /* Guest */
10438 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10439
10440 // IO settings
10441 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10442 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10443
10444 /* BandwidthControl (required) */
10445 rc = mBandwidthControl->saveSettings(data.ioSettings);
10446 if (FAILED(rc)) throw rc;
10447
10448 /* Host PCI devices */
10449 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10450 it != mHWData->mPCIDeviceAssignments.end();
10451 ++it)
10452 {
10453 ComObjPtr<PCIDeviceAttachment> pda = *it;
10454 settings::HostPCIDeviceAttachment hpda;
10455
10456 rc = pda->saveSettings(hpda);
10457 if (FAILED(rc)) throw rc;
10458
10459 data.pciAttachments.push_back(hpda);
10460 }
10461
10462
10463 // guest properties
10464 data.llGuestProperties.clear();
10465#ifdef VBOX_WITH_GUEST_PROPS
10466 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10467 it != mHWData->mGuestProperties.end();
10468 ++it)
10469 {
10470 HWData::GuestProperty property = it->second;
10471
10472 /* Remove transient guest properties at shutdown unless we
10473 * are saving state */
10474 if ( ( mData->mMachineState == MachineState_PoweredOff
10475 || mData->mMachineState == MachineState_Aborted
10476 || mData->mMachineState == MachineState_Teleported)
10477 && ( property.mFlags & guestProp::TRANSIENT
10478 || property.mFlags & guestProp::TRANSRESET))
10479 continue;
10480 settings::GuestProperty prop;
10481 prop.strName = it->first;
10482 prop.strValue = property.strValue;
10483 prop.timestamp = property.mTimestamp;
10484 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10485 guestProp::writeFlags(property.mFlags, szFlags);
10486 prop.strFlags = szFlags;
10487
10488 data.llGuestProperties.push_back(prop);
10489 }
10490
10491 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10492 /* I presume this doesn't require a backup(). */
10493 mData->mGuestPropertiesModified = FALSE;
10494#endif /* VBOX_WITH_GUEST_PROPS defined */
10495
10496 *pDbg = mHWData->mDebugging;
10497 *pAutostart = mHWData->mAutostart;
10498
10499 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10500 }
10501 catch(std::bad_alloc &)
10502 {
10503 return E_OUTOFMEMORY;
10504 }
10505
10506 AssertComRC(rc);
10507 return rc;
10508}
10509
10510/**
10511 * Saves the storage controller configuration.
10512 *
10513 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10514 */
10515HRESULT Machine::saveStorageControllers(settings::Storage &data)
10516{
10517 data.llStorageControllers.clear();
10518
10519 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10520 it != mStorageControllers->end();
10521 ++it)
10522 {
10523 HRESULT rc;
10524 ComObjPtr<StorageController> pCtl = *it;
10525
10526 settings::StorageController ctl;
10527 ctl.strName = pCtl->getName();
10528 ctl.controllerType = pCtl->getControllerType();
10529 ctl.storageBus = pCtl->getStorageBus();
10530 ctl.ulInstance = pCtl->getInstance();
10531 ctl.fBootable = pCtl->getBootable();
10532
10533 /* Save the port count. */
10534 ULONG portCount;
10535 rc = pCtl->COMGETTER(PortCount)(&portCount);
10536 ComAssertComRCRet(rc, rc);
10537 ctl.ulPortCount = portCount;
10538
10539 /* Save fUseHostIOCache */
10540 BOOL fUseHostIOCache;
10541 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10542 ComAssertComRCRet(rc, rc);
10543 ctl.fUseHostIOCache = !!fUseHostIOCache;
10544
10545 /* Save IDE emulation settings. */
10546 if (ctl.controllerType == StorageControllerType_IntelAhci)
10547 {
10548 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10549 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10550 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10551 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10552 )
10553 ComAssertComRCRet(rc, rc);
10554 }
10555
10556 /* save the devices now. */
10557 rc = saveStorageDevices(pCtl, ctl);
10558 ComAssertComRCRet(rc, rc);
10559
10560 data.llStorageControllers.push_back(ctl);
10561 }
10562
10563 return S_OK;
10564}
10565
10566/**
10567 * Saves the hard disk configuration.
10568 */
10569HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10570 settings::StorageController &data)
10571{
10572 MediaData::AttachmentList atts;
10573
10574 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10575 if (FAILED(rc)) return rc;
10576
10577 data.llAttachedDevices.clear();
10578 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10579 it != atts.end();
10580 ++it)
10581 {
10582 settings::AttachedDevice dev;
10583
10584 MediumAttachment *pAttach = *it;
10585 Medium *pMedium = pAttach->getMedium();
10586
10587 dev.deviceType = pAttach->getType();
10588 dev.lPort = pAttach->getPort();
10589 dev.lDevice = pAttach->getDevice();
10590 if (pMedium)
10591 {
10592 if (pMedium->isHostDrive())
10593 dev.strHostDriveSrc = pMedium->getLocationFull();
10594 else
10595 dev.uuid = pMedium->getId();
10596 dev.fPassThrough = pAttach->getPassthrough();
10597 dev.fTempEject = pAttach->getTempEject();
10598 dev.fNonRotational = pAttach->getNonRotational();
10599 dev.fDiscard = pAttach->getDiscard();
10600 }
10601
10602 dev.strBwGroup = pAttach->getBandwidthGroup();
10603
10604 data.llAttachedDevices.push_back(dev);
10605 }
10606
10607 return S_OK;
10608}
10609
10610/**
10611 * Saves machine state settings as defined by aFlags
10612 * (SaveSTS_* values).
10613 *
10614 * @param aFlags Combination of SaveSTS_* flags.
10615 *
10616 * @note Locks objects for writing.
10617 */
10618HRESULT Machine::saveStateSettings(int aFlags)
10619{
10620 if (aFlags == 0)
10621 return S_OK;
10622
10623 AutoCaller autoCaller(this);
10624 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10625
10626 /* This object's write lock is also necessary to serialize file access
10627 * (prevent concurrent reads and writes) */
10628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10629
10630 HRESULT rc = S_OK;
10631
10632 Assert(mData->pMachineConfigFile);
10633
10634 try
10635 {
10636 if (aFlags & SaveSTS_CurStateModified)
10637 mData->pMachineConfigFile->fCurrentStateModified = true;
10638
10639 if (aFlags & SaveSTS_StateFilePath)
10640 {
10641 if (!mSSData->strStateFilePath.isEmpty())
10642 /* try to make the file name relative to the settings file dir */
10643 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10644 else
10645 mData->pMachineConfigFile->strStateFile.setNull();
10646 }
10647
10648 if (aFlags & SaveSTS_StateTimeStamp)
10649 {
10650 Assert( mData->mMachineState != MachineState_Aborted
10651 || mSSData->strStateFilePath.isEmpty());
10652
10653 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10654
10655 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10656//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10657 }
10658
10659 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10660 }
10661 catch (...)
10662 {
10663 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10664 }
10665
10666 return rc;
10667}
10668
10669/**
10670 * Ensures that the given medium is added to a media registry. If this machine
10671 * was created with 4.0 or later, then the machine registry is used. Otherwise
10672 * the global VirtualBox media registry is used.
10673 *
10674 * Caller must NOT hold machine lock, media tree or any medium locks!
10675 *
10676 * @param pMedium
10677 */
10678void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10679{
10680 /* Paranoia checks: do not hold machine or media tree locks. */
10681 AssertReturnVoid(!isWriteLockOnCurrentThread());
10682 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10683
10684 ComObjPtr<Medium> pBase;
10685 {
10686 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10687 pBase = pMedium->getBase();
10688 }
10689
10690 /* Paranoia checks: do not hold medium locks. */
10691 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10692 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10693
10694 // decide which medium registry to use now that the medium is attached:
10695 Guid uuid;
10696 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10697 // machine XML is VirtualBox 4.0 or higher:
10698 uuid = getId(); // machine UUID
10699 else
10700 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10701
10702 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10703 mParent->markRegistryModified(uuid);
10704
10705 /* For more complex hard disk structures it can happen that the base
10706 * medium isn't yet associated with any medium registry. Do that now. */
10707 if (pMedium != pBase)
10708 {
10709 if (pBase->addRegistry(uuid, true /* fRecurse */))
10710 mParent->markRegistryModified(uuid);
10711 }
10712}
10713
10714/**
10715 * Creates differencing hard disks for all normal hard disks attached to this
10716 * machine and a new set of attachments to refer to created disks.
10717 *
10718 * Used when taking a snapshot or when deleting the current state. Gets called
10719 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10720 *
10721 * This method assumes that mMediaData contains the original hard disk attachments
10722 * it needs to create diffs for. On success, these attachments will be replaced
10723 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10724 * called to delete created diffs which will also rollback mMediaData and restore
10725 * whatever was backed up before calling this method.
10726 *
10727 * Attachments with non-normal hard disks are left as is.
10728 *
10729 * If @a aOnline is @c false then the original hard disks that require implicit
10730 * diffs will be locked for reading. Otherwise it is assumed that they are
10731 * already locked for writing (when the VM was started). Note that in the latter
10732 * case it is responsibility of the caller to lock the newly created diffs for
10733 * writing if this method succeeds.
10734 *
10735 * @param aProgress Progress object to run (must contain at least as
10736 * many operations left as the number of hard disks
10737 * attached).
10738 * @param aOnline Whether the VM was online prior to this operation.
10739 *
10740 * @note The progress object is not marked as completed, neither on success nor
10741 * on failure. This is a responsibility of the caller.
10742 *
10743 * @note Locks this object and the media tree for writing.
10744 */
10745HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10746 ULONG aWeight,
10747 bool aOnline)
10748{
10749 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10750
10751 AutoCaller autoCaller(this);
10752 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10753
10754 AutoMultiWriteLock2 alock(this->lockHandle(),
10755 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10756
10757 /* must be in a protective state because we release the lock below */
10758 AssertReturn( mData->mMachineState == MachineState_Saving
10759 || mData->mMachineState == MachineState_LiveSnapshotting
10760 || mData->mMachineState == MachineState_RestoringSnapshot
10761 || mData->mMachineState == MachineState_DeletingSnapshot
10762 , E_FAIL);
10763
10764 HRESULT rc = S_OK;
10765
10766 // use appropriate locked media map (online or offline)
10767 MediumLockListMap lockedMediaOffline;
10768 MediumLockListMap *lockedMediaMap;
10769 if (aOnline)
10770 lockedMediaMap = &mData->mSession.mLockedMedia;
10771 else
10772 lockedMediaMap = &lockedMediaOffline;
10773
10774 try
10775 {
10776 if (!aOnline)
10777 {
10778 /* lock all attached hard disks early to detect "in use"
10779 * situations before creating actual diffs */
10780 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10781 it != mMediaData->mAttachments.end();
10782 ++it)
10783 {
10784 MediumAttachment* pAtt = *it;
10785 if (pAtt->getType() == DeviceType_HardDisk)
10786 {
10787 Medium* pMedium = pAtt->getMedium();
10788 Assert(pMedium);
10789
10790 MediumLockList *pMediumLockList(new MediumLockList());
10791 alock.release();
10792 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10793 false /* fMediumLockWrite */,
10794 NULL,
10795 *pMediumLockList);
10796 alock.acquire();
10797 if (FAILED(rc))
10798 {
10799 delete pMediumLockList;
10800 throw rc;
10801 }
10802 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10803 if (FAILED(rc))
10804 {
10805 throw setError(rc,
10806 tr("Collecting locking information for all attached media failed"));
10807 }
10808 }
10809 }
10810
10811 /* Now lock all media. If this fails, nothing is locked. */
10812 alock.release();
10813 rc = lockedMediaMap->Lock();
10814 alock.acquire();
10815 if (FAILED(rc))
10816 {
10817 throw setError(rc,
10818 tr("Locking of attached media failed"));
10819 }
10820 }
10821
10822 /* remember the current list (note that we don't use backup() since
10823 * mMediaData may be already backed up) */
10824 MediaData::AttachmentList atts = mMediaData->mAttachments;
10825
10826 /* start from scratch */
10827 mMediaData->mAttachments.clear();
10828
10829 /* go through remembered attachments and create diffs for normal hard
10830 * disks and attach them */
10831 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10832 it != atts.end();
10833 ++it)
10834 {
10835 MediumAttachment* pAtt = *it;
10836
10837 DeviceType_T devType = pAtt->getType();
10838 Medium* pMedium = pAtt->getMedium();
10839
10840 if ( devType != DeviceType_HardDisk
10841 || pMedium == NULL
10842 || pMedium->getType() != MediumType_Normal)
10843 {
10844 /* copy the attachment as is */
10845
10846 /** @todo the progress object created in Console::TakeSnaphot
10847 * only expects operations for hard disks. Later other
10848 * device types need to show up in the progress as well. */
10849 if (devType == DeviceType_HardDisk)
10850 {
10851 if (pMedium == NULL)
10852 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10853 aWeight); // weight
10854 else
10855 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10856 pMedium->getBase()->getName().c_str()).raw(),
10857 aWeight); // weight
10858 }
10859
10860 mMediaData->mAttachments.push_back(pAtt);
10861 continue;
10862 }
10863
10864 /* need a diff */
10865 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10866 pMedium->getBase()->getName().c_str()).raw(),
10867 aWeight); // weight
10868
10869 Utf8Str strFullSnapshotFolder;
10870 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10871
10872 ComObjPtr<Medium> diff;
10873 diff.createObject();
10874 // store the diff in the same registry as the parent
10875 // (this cannot fail here because we can't create implicit diffs for
10876 // unregistered images)
10877 Guid uuidRegistryParent;
10878 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10879 Assert(fInRegistry); NOREF(fInRegistry);
10880 rc = diff->init(mParent,
10881 pMedium->getPreferredDiffFormat(),
10882 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10883 uuidRegistryParent);
10884 if (FAILED(rc)) throw rc;
10885
10886 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10887 * the push_back? Looks like we're going to release medium with the
10888 * wrong kind of lock (general issue with if we fail anywhere at all)
10889 * and an orphaned VDI in the snapshots folder. */
10890
10891 /* update the appropriate lock list */
10892 MediumLockList *pMediumLockList;
10893 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10894 AssertComRCThrowRC(rc);
10895 if (aOnline)
10896 {
10897 alock.release();
10898 /* The currently attached medium will be read-only, change
10899 * the lock type to read. */
10900 rc = pMediumLockList->Update(pMedium, false);
10901 alock.acquire();
10902 AssertComRCThrowRC(rc);
10903 }
10904
10905 /* release the locks before the potentially lengthy operation */
10906 alock.release();
10907 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10908 pMediumLockList,
10909 NULL /* aProgress */,
10910 true /* aWait */);
10911 alock.acquire();
10912 if (FAILED(rc)) throw rc;
10913
10914 /* actual lock list update is done in Medium::commitMedia */
10915
10916 rc = diff->addBackReference(mData->mUuid);
10917 AssertComRCThrowRC(rc);
10918
10919 /* add a new attachment */
10920 ComObjPtr<MediumAttachment> attachment;
10921 attachment.createObject();
10922 rc = attachment->init(this,
10923 diff,
10924 pAtt->getControllerName(),
10925 pAtt->getPort(),
10926 pAtt->getDevice(),
10927 DeviceType_HardDisk,
10928 true /* aImplicit */,
10929 false /* aPassthrough */,
10930 false /* aTempEject */,
10931 pAtt->getNonRotational(),
10932 pAtt->getDiscard(),
10933 pAtt->getBandwidthGroup());
10934 if (FAILED(rc)) throw rc;
10935
10936 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10937 AssertComRCThrowRC(rc);
10938 mMediaData->mAttachments.push_back(attachment);
10939 }
10940 }
10941 catch (HRESULT aRC) { rc = aRC; }
10942
10943 /* unlock all hard disks we locked when there is no VM */
10944 if (!aOnline)
10945 {
10946 ErrorInfoKeeper eik;
10947
10948 HRESULT rc1 = lockedMediaMap->Clear();
10949 AssertComRC(rc1);
10950 }
10951
10952 return rc;
10953}
10954
10955/**
10956 * Deletes implicit differencing hard disks created either by
10957 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10958 *
10959 * Note that to delete hard disks created by #AttachDevice() this method is
10960 * called from #fixupMedia() when the changes are rolled back.
10961 *
10962 * @note Locks this object and the media tree for writing.
10963 */
10964HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10965{
10966 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10967
10968 AutoCaller autoCaller(this);
10969 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10970
10971 AutoMultiWriteLock2 alock(this->lockHandle(),
10972 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10973
10974 /* We absolutely must have backed up state. */
10975 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10976
10977 /* Check if there are any implicitly created diff images. */
10978 bool fImplicitDiffs = false;
10979 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10980 it != mMediaData->mAttachments.end();
10981 ++it)
10982 {
10983 const ComObjPtr<MediumAttachment> &pAtt = *it;
10984 if (pAtt->isImplicit())
10985 {
10986 fImplicitDiffs = true;
10987 break;
10988 }
10989 }
10990 /* If there is nothing to do, leave early. This saves lots of image locking
10991 * effort. It also avoids a MachineStateChanged event without real reason.
10992 * This is important e.g. when loading a VM config, because there should be
10993 * no events. Otherwise API clients can become thoroughly confused for
10994 * inaccessible VMs (the code for loading VM configs uses this method for
10995 * cleanup if the config makes no sense), as they take such events as an
10996 * indication that the VM is alive, and they would force the VM config to
10997 * be reread, leading to an endless loop. */
10998 if (!fImplicitDiffs)
10999 return S_OK;
11000
11001 HRESULT rc = S_OK;
11002 MachineState_T oldState = mData->mMachineState;
11003
11004 /* will release the lock before the potentially lengthy operation,
11005 * so protect with the special state (unless already protected) */
11006 if ( oldState != MachineState_Saving
11007 && oldState != MachineState_LiveSnapshotting
11008 && oldState != MachineState_RestoringSnapshot
11009 && oldState != MachineState_DeletingSnapshot
11010 && oldState != MachineState_DeletingSnapshotOnline
11011 && oldState != MachineState_DeletingSnapshotPaused
11012 )
11013 setMachineState(MachineState_SettingUp);
11014
11015 // use appropriate locked media map (online or offline)
11016 MediumLockListMap lockedMediaOffline;
11017 MediumLockListMap *lockedMediaMap;
11018 if (aOnline)
11019 lockedMediaMap = &mData->mSession.mLockedMedia;
11020 else
11021 lockedMediaMap = &lockedMediaOffline;
11022
11023 try
11024 {
11025 if (!aOnline)
11026 {
11027 /* lock all attached hard disks early to detect "in use"
11028 * situations before deleting actual diffs */
11029 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11030 it != mMediaData->mAttachments.end();
11031 ++it)
11032 {
11033 MediumAttachment* pAtt = *it;
11034 if (pAtt->getType() == DeviceType_HardDisk)
11035 {
11036 Medium* pMedium = pAtt->getMedium();
11037 Assert(pMedium);
11038
11039 MediumLockList *pMediumLockList(new MediumLockList());
11040 alock.release();
11041 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11042 false /* fMediumLockWrite */,
11043 NULL,
11044 *pMediumLockList);
11045 alock.acquire();
11046
11047 if (FAILED(rc))
11048 {
11049 delete pMediumLockList;
11050 throw rc;
11051 }
11052
11053 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11054 if (FAILED(rc))
11055 throw rc;
11056 }
11057 }
11058
11059 if (FAILED(rc))
11060 throw rc;
11061 } // end of offline
11062
11063 /* Lock lists are now up to date and include implicitly created media */
11064
11065 /* Go through remembered attachments and delete all implicitly created
11066 * diffs and fix up the attachment information */
11067 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11068 MediaData::AttachmentList implicitAtts;
11069 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11070 it != mMediaData->mAttachments.end();
11071 ++it)
11072 {
11073 ComObjPtr<MediumAttachment> pAtt = *it;
11074 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11075 if (pMedium.isNull())
11076 continue;
11077
11078 // Implicit attachments go on the list for deletion and back references are removed.
11079 if (pAtt->isImplicit())
11080 {
11081 /* Deassociate and mark for deletion */
11082 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11083 rc = pMedium->removeBackReference(mData->mUuid);
11084 if (FAILED(rc))
11085 throw rc;
11086 implicitAtts.push_back(pAtt);
11087 continue;
11088 }
11089
11090 /* Was this medium attached before? */
11091 if (!findAttachment(oldAtts, pMedium))
11092 {
11093 /* no: de-associate */
11094 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11095 rc = pMedium->removeBackReference(mData->mUuid);
11096 if (FAILED(rc))
11097 throw rc;
11098 continue;
11099 }
11100 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11101 }
11102
11103 /* If there are implicit attachments to delete, throw away the lock
11104 * map contents (which will unlock all media) since the medium
11105 * attachments will be rolled back. Below we need to completely
11106 * recreate the lock map anyway since it is infinitely complex to
11107 * do this incrementally (would need reconstructing each attachment
11108 * change, which would be extremely hairy). */
11109 if (implicitAtts.size() != 0)
11110 {
11111 ErrorInfoKeeper eik;
11112
11113 HRESULT rc1 = lockedMediaMap->Clear();
11114 AssertComRC(rc1);
11115 }
11116
11117 /* rollback hard disk changes */
11118 mMediaData.rollback();
11119
11120 MultiResult mrc(S_OK);
11121
11122 // Delete unused implicit diffs.
11123 if (implicitAtts.size() != 0)
11124 {
11125 alock.release();
11126
11127 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11128 it != implicitAtts.end();
11129 ++it)
11130 {
11131 // Remove medium associated with this attachment.
11132 ComObjPtr<MediumAttachment> pAtt = *it;
11133 Assert(pAtt);
11134 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11135 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11136 Assert(pMedium);
11137
11138 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11139 // continue on delete failure, just collect error messages
11140 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11141 mrc = rc;
11142 }
11143
11144 alock.acquire();
11145
11146 /* if there is a VM recreate media lock map as mentioned above,
11147 * otherwise it is a waste of time and we leave things unlocked */
11148 if (aOnline)
11149 {
11150 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11151 /* must never be NULL, but better safe than sorry */
11152 if (!pMachine.isNull())
11153 {
11154 alock.release();
11155 rc = mData->mSession.mMachine->lockMedia();
11156 alock.acquire();
11157 if (FAILED(rc))
11158 throw rc;
11159 }
11160 }
11161 }
11162 }
11163 catch (HRESULT aRC) {rc = aRC;}
11164
11165 if (mData->mMachineState == MachineState_SettingUp)
11166 setMachineState(oldState);
11167
11168 /* unlock all hard disks we locked when there is no VM */
11169 if (!aOnline)
11170 {
11171 ErrorInfoKeeper eik;
11172
11173 HRESULT rc1 = lockedMediaMap->Clear();
11174 AssertComRC(rc1);
11175 }
11176
11177 return rc;
11178}
11179
11180
11181/**
11182 * Looks through the given list of media attachments for one with the given parameters
11183 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11184 * can be searched as well if needed.
11185 *
11186 * @param list
11187 * @param aControllerName
11188 * @param aControllerPort
11189 * @param aDevice
11190 * @return
11191 */
11192MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11193 IN_BSTR aControllerName,
11194 LONG aControllerPort,
11195 LONG aDevice)
11196{
11197 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11198 it != ll.end();
11199 ++it)
11200 {
11201 MediumAttachment *pAttach = *it;
11202 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11203 return pAttach;
11204 }
11205
11206 return NULL;
11207}
11208
11209/**
11210 * Looks through the given list of media attachments for one with the given parameters
11211 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11212 * can be searched as well if needed.
11213 *
11214 * @param list
11215 * @param aControllerName
11216 * @param aControllerPort
11217 * @param aDevice
11218 * @return
11219 */
11220MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11221 ComObjPtr<Medium> pMedium)
11222{
11223 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11224 it != ll.end();
11225 ++it)
11226 {
11227 MediumAttachment *pAttach = *it;
11228 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11229 if (pMediumThis == pMedium)
11230 return pAttach;
11231 }
11232
11233 return NULL;
11234}
11235
11236/**
11237 * Looks through the given list of media attachments for one with the given parameters
11238 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11239 * can be searched as well if needed.
11240 *
11241 * @param list
11242 * @param aControllerName
11243 * @param aControllerPort
11244 * @param aDevice
11245 * @return
11246 */
11247MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11248 Guid &id)
11249{
11250 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11251 it != ll.end();
11252 ++it)
11253 {
11254 MediumAttachment *pAttach = *it;
11255 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11256 if (pMediumThis->getId() == id)
11257 return pAttach;
11258 }
11259
11260 return NULL;
11261}
11262
11263/**
11264 * Main implementation for Machine::DetachDevice. This also gets called
11265 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11266 *
11267 * @param pAttach Medium attachment to detach.
11268 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11269 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11270 * @return
11271 */
11272HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11273 AutoWriteLock &writeLock,
11274 Snapshot *pSnapshot)
11275{
11276 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11277 DeviceType_T mediumType = pAttach->getType();
11278
11279 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11280
11281 if (pAttach->isImplicit())
11282 {
11283 /* attempt to implicitly delete the implicitly created diff */
11284
11285 /// @todo move the implicit flag from MediumAttachment to Medium
11286 /// and forbid any hard disk operation when it is implicit. Or maybe
11287 /// a special media state for it to make it even more simple.
11288
11289 Assert(mMediaData.isBackedUp());
11290
11291 /* will release the lock before the potentially lengthy operation, so
11292 * protect with the special state */
11293 MachineState_T oldState = mData->mMachineState;
11294 setMachineState(MachineState_SettingUp);
11295
11296 writeLock.release();
11297
11298 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11299 true /*aWait*/);
11300
11301 writeLock.acquire();
11302
11303 setMachineState(oldState);
11304
11305 if (FAILED(rc)) return rc;
11306 }
11307
11308 setModified(IsModified_Storage);
11309 mMediaData.backup();
11310 mMediaData->mAttachments.remove(pAttach);
11311
11312 if (!oldmedium.isNull())
11313 {
11314 // if this is from a snapshot, do not defer detachment to commitMedia()
11315 if (pSnapshot)
11316 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11317 // else if non-hard disk media, do not defer detachment to commitMedia() either
11318 else if (mediumType != DeviceType_HardDisk)
11319 oldmedium->removeBackReference(mData->mUuid);
11320 }
11321
11322 return S_OK;
11323}
11324
11325/**
11326 * Goes thru all media of the given list and
11327 *
11328 * 1) calls detachDevice() on each of them for this machine and
11329 * 2) adds all Medium objects found in the process to the given list,
11330 * depending on cleanupMode.
11331 *
11332 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11333 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11334 * media to the list.
11335 *
11336 * This gets called from Machine::Unregister, both for the actual Machine and
11337 * the SnapshotMachine objects that might be found in the snapshots.
11338 *
11339 * Requires caller and locking. The machine lock must be passed in because it
11340 * will be passed on to detachDevice which needs it for temporary unlocking.
11341 *
11342 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11343 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11344 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11345 * otherwise no media get added.
11346 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11347 * @return
11348 */
11349HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11350 Snapshot *pSnapshot,
11351 CleanupMode_T cleanupMode,
11352 MediaList &llMedia)
11353{
11354 Assert(isWriteLockOnCurrentThread());
11355
11356 HRESULT rc;
11357
11358 // make a temporary list because detachDevice invalidates iterators into
11359 // mMediaData->mAttachments
11360 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11361
11362 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11363 it != llAttachments2.end();
11364 ++it)
11365 {
11366 ComObjPtr<MediumAttachment> &pAttach = *it;
11367 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11368
11369 if (!pMedium.isNull())
11370 {
11371 AutoCaller mac(pMedium);
11372 if (FAILED(mac.rc())) return mac.rc();
11373 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11374 DeviceType_T devType = pMedium->getDeviceType();
11375 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11376 && devType == DeviceType_HardDisk)
11377 || (cleanupMode == CleanupMode_Full)
11378 )
11379 {
11380 llMedia.push_back(pMedium);
11381 ComObjPtr<Medium> pParent = pMedium->getParent();
11382 /*
11383 * Search for medias which are not attached to any machine, but
11384 * in the chain to an attached disk. Mediums are only consided
11385 * if they are:
11386 * - have only one child
11387 * - no references to any machines
11388 * - are of normal medium type
11389 */
11390 while (!pParent.isNull())
11391 {
11392 AutoCaller mac1(pParent);
11393 if (FAILED(mac1.rc())) return mac1.rc();
11394 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11395 if (pParent->getChildren().size() == 1)
11396 {
11397 if ( pParent->getMachineBackRefCount() == 0
11398 && pParent->getType() == MediumType_Normal
11399 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11400 llMedia.push_back(pParent);
11401 }
11402 else
11403 break;
11404 pParent = pParent->getParent();
11405 }
11406 }
11407 }
11408
11409 // real machine: then we need to use the proper method
11410 rc = detachDevice(pAttach, writeLock, pSnapshot);
11411
11412 if (FAILED(rc))
11413 return rc;
11414 }
11415
11416 return S_OK;
11417}
11418
11419/**
11420 * Perform deferred hard disk detachments.
11421 *
11422 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11423 * backed up).
11424 *
11425 * If @a aOnline is @c true then this method will also unlock the old hard disks
11426 * for which the new implicit diffs were created and will lock these new diffs for
11427 * writing.
11428 *
11429 * @param aOnline Whether the VM was online prior to this operation.
11430 *
11431 * @note Locks this object for writing!
11432 */
11433void Machine::commitMedia(bool aOnline /*= false*/)
11434{
11435 AutoCaller autoCaller(this);
11436 AssertComRCReturnVoid(autoCaller.rc());
11437
11438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11439
11440 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11441
11442 HRESULT rc = S_OK;
11443
11444 /* no attach/detach operations -- nothing to do */
11445 if (!mMediaData.isBackedUp())
11446 return;
11447
11448 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11449 bool fMediaNeedsLocking = false;
11450
11451 /* enumerate new attachments */
11452 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11453 it != mMediaData->mAttachments.end();
11454 ++it)
11455 {
11456 MediumAttachment *pAttach = *it;
11457
11458 pAttach->commit();
11459
11460 Medium* pMedium = pAttach->getMedium();
11461 bool fImplicit = pAttach->isImplicit();
11462
11463 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11464 (pMedium) ? pMedium->getName().c_str() : "NULL",
11465 fImplicit));
11466
11467 /** @todo convert all this Machine-based voodoo to MediumAttachment
11468 * based commit logic. */
11469 if (fImplicit)
11470 {
11471 /* convert implicit attachment to normal */
11472 pAttach->setImplicit(false);
11473
11474 if ( aOnline
11475 && pMedium
11476 && pAttach->getType() == DeviceType_HardDisk
11477 )
11478 {
11479 ComObjPtr<Medium> parent = pMedium->getParent();
11480 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11481
11482 /* update the appropriate lock list */
11483 MediumLockList *pMediumLockList;
11484 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11485 AssertComRC(rc);
11486 if (pMediumLockList)
11487 {
11488 /* unlock if there's a need to change the locking */
11489 if (!fMediaNeedsLocking)
11490 {
11491 rc = mData->mSession.mLockedMedia.Unlock();
11492 AssertComRC(rc);
11493 fMediaNeedsLocking = true;
11494 }
11495 rc = pMediumLockList->Update(parent, false);
11496 AssertComRC(rc);
11497 rc = pMediumLockList->Append(pMedium, true);
11498 AssertComRC(rc);
11499 }
11500 }
11501
11502 continue;
11503 }
11504
11505 if (pMedium)
11506 {
11507 /* was this medium attached before? */
11508 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11509 oldIt != oldAtts.end();
11510 ++oldIt)
11511 {
11512 MediumAttachment *pOldAttach = *oldIt;
11513 if (pOldAttach->getMedium() == pMedium)
11514 {
11515 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11516
11517 /* yes: remove from old to avoid de-association */
11518 oldAtts.erase(oldIt);
11519 break;
11520 }
11521 }
11522 }
11523 }
11524
11525 /* enumerate remaining old attachments and de-associate from the
11526 * current machine state */
11527 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11528 it != oldAtts.end();
11529 ++it)
11530 {
11531 MediumAttachment *pAttach = *it;
11532 Medium* pMedium = pAttach->getMedium();
11533
11534 /* Detach only hard disks, since DVD/floppy media is detached
11535 * instantly in MountMedium. */
11536 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11537 {
11538 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11539
11540 /* now de-associate from the current machine state */
11541 rc = pMedium->removeBackReference(mData->mUuid);
11542 AssertComRC(rc);
11543
11544 if (aOnline)
11545 {
11546 /* unlock since medium is not used anymore */
11547 MediumLockList *pMediumLockList;
11548 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11549 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11550 {
11551 /* this happens for online snapshots, there the attachment
11552 * is changing, but only to a diff image created under
11553 * the old one, so there is no separate lock list */
11554 Assert(!pMediumLockList);
11555 }
11556 else
11557 {
11558 AssertComRC(rc);
11559 if (pMediumLockList)
11560 {
11561 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11562 AssertComRC(rc);
11563 }
11564 }
11565 }
11566 }
11567 }
11568
11569 /* take media locks again so that the locking state is consistent */
11570 if (fMediaNeedsLocking)
11571 {
11572 Assert(aOnline);
11573 rc = mData->mSession.mLockedMedia.Lock();
11574 AssertComRC(rc);
11575 }
11576
11577 /* commit the hard disk changes */
11578 mMediaData.commit();
11579
11580 if (isSessionMachine())
11581 {
11582 /*
11583 * Update the parent machine to point to the new owner.
11584 * This is necessary because the stored parent will point to the
11585 * session machine otherwise and cause crashes or errors later
11586 * when the session machine gets invalid.
11587 */
11588 /** @todo Change the MediumAttachment class to behave like any other
11589 * class in this regard by creating peer MediumAttachment
11590 * objects for session machines and share the data with the peer
11591 * machine.
11592 */
11593 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11594 it != mMediaData->mAttachments.end();
11595 ++it)
11596 {
11597 (*it)->updateParentMachine(mPeer);
11598 }
11599
11600 /* attach new data to the primary machine and reshare it */
11601 mPeer->mMediaData.attach(mMediaData);
11602 }
11603
11604 return;
11605}
11606
11607/**
11608 * Perform deferred deletion of implicitly created diffs.
11609 *
11610 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11611 * backed up).
11612 *
11613 * @note Locks this object for writing!
11614 */
11615void Machine::rollbackMedia()
11616{
11617 AutoCaller autoCaller(this);
11618 AssertComRCReturnVoid(autoCaller.rc());
11619
11620 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11621 LogFlowThisFunc(("Entering rollbackMedia\n"));
11622
11623 HRESULT rc = S_OK;
11624
11625 /* no attach/detach operations -- nothing to do */
11626 if (!mMediaData.isBackedUp())
11627 return;
11628
11629 /* enumerate new attachments */
11630 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11631 it != mMediaData->mAttachments.end();
11632 ++it)
11633 {
11634 MediumAttachment *pAttach = *it;
11635 /* Fix up the backrefs for DVD/floppy media. */
11636 if (pAttach->getType() != DeviceType_HardDisk)
11637 {
11638 Medium* pMedium = pAttach->getMedium();
11639 if (pMedium)
11640 {
11641 rc = pMedium->removeBackReference(mData->mUuid);
11642 AssertComRC(rc);
11643 }
11644 }
11645
11646 (*it)->rollback();
11647
11648 pAttach = *it;
11649 /* Fix up the backrefs for DVD/floppy media. */
11650 if (pAttach->getType() != DeviceType_HardDisk)
11651 {
11652 Medium* pMedium = pAttach->getMedium();
11653 if (pMedium)
11654 {
11655 rc = pMedium->addBackReference(mData->mUuid);
11656 AssertComRC(rc);
11657 }
11658 }
11659 }
11660
11661 /** @todo convert all this Machine-based voodoo to MediumAttachment
11662 * based rollback logic. */
11663 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11664
11665 return;
11666}
11667
11668/**
11669 * Returns true if the settings file is located in the directory named exactly
11670 * as the machine; this means, among other things, that the machine directory
11671 * should be auto-renamed.
11672 *
11673 * @param aSettingsDir if not NULL, the full machine settings file directory
11674 * name will be assigned there.
11675 *
11676 * @note Doesn't lock anything.
11677 * @note Not thread safe (must be called from this object's lock).
11678 */
11679bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11680{
11681 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11682 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11683 if (aSettingsDir)
11684 *aSettingsDir = strMachineDirName;
11685 strMachineDirName.stripPath(); // vmname
11686 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11687 strConfigFileOnly.stripPath() // vmname.vbox
11688 .stripExt(); // vmname
11689 /** @todo hack, make somehow use of ComposeMachineFilename */
11690 if (mUserData->s.fDirectoryIncludesUUID)
11691 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11692
11693 AssertReturn(!strMachineDirName.isEmpty(), false);
11694 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11695
11696 return strMachineDirName == strConfigFileOnly;
11697}
11698
11699/**
11700 * Discards all changes to machine settings.
11701 *
11702 * @param aNotify Whether to notify the direct session about changes or not.
11703 *
11704 * @note Locks objects for writing!
11705 */
11706void Machine::rollback(bool aNotify)
11707{
11708 AutoCaller autoCaller(this);
11709 AssertComRCReturn(autoCaller.rc(), (void)0);
11710
11711 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11712
11713 if (!mStorageControllers.isNull())
11714 {
11715 if (mStorageControllers.isBackedUp())
11716 {
11717 /* unitialize all new devices (absent in the backed up list). */
11718 StorageControllerList::const_iterator it = mStorageControllers->begin();
11719 StorageControllerList *backedList = mStorageControllers.backedUpData();
11720 while (it != mStorageControllers->end())
11721 {
11722 if ( std::find(backedList->begin(), backedList->end(), *it)
11723 == backedList->end()
11724 )
11725 {
11726 (*it)->uninit();
11727 }
11728 ++it;
11729 }
11730
11731 /* restore the list */
11732 mStorageControllers.rollback();
11733 }
11734
11735 /* rollback any changes to devices after restoring the list */
11736 if (mData->flModifications & IsModified_Storage)
11737 {
11738 StorageControllerList::const_iterator it = mStorageControllers->begin();
11739 while (it != mStorageControllers->end())
11740 {
11741 (*it)->rollback();
11742 ++it;
11743 }
11744 }
11745 }
11746
11747 mUserData.rollback();
11748
11749 mHWData.rollback();
11750
11751 if (mData->flModifications & IsModified_Storage)
11752 rollbackMedia();
11753
11754 if (mBIOSSettings)
11755 mBIOSSettings->rollback();
11756
11757 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11758 mVRDEServer->rollback();
11759
11760 if (mAudioAdapter)
11761 mAudioAdapter->rollback();
11762
11763 if (mUSBController && (mData->flModifications & IsModified_USB))
11764 mUSBController->rollback();
11765
11766 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11767 mBandwidthControl->rollback();
11768
11769 if (!mHWData.isNull())
11770 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11771 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11772 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11773 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11774
11775 if (mData->flModifications & IsModified_NetworkAdapters)
11776 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11777 if ( mNetworkAdapters[slot]
11778 && mNetworkAdapters[slot]->isModified())
11779 {
11780 mNetworkAdapters[slot]->rollback();
11781 networkAdapters[slot] = mNetworkAdapters[slot];
11782 }
11783
11784 if (mData->flModifications & IsModified_SerialPorts)
11785 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11786 if ( mSerialPorts[slot]
11787 && mSerialPorts[slot]->isModified())
11788 {
11789 mSerialPorts[slot]->rollback();
11790 serialPorts[slot] = mSerialPorts[slot];
11791 }
11792
11793 if (mData->flModifications & IsModified_ParallelPorts)
11794 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11795 if ( mParallelPorts[slot]
11796 && mParallelPorts[slot]->isModified())
11797 {
11798 mParallelPorts[slot]->rollback();
11799 parallelPorts[slot] = mParallelPorts[slot];
11800 }
11801
11802 if (aNotify)
11803 {
11804 /* inform the direct session about changes */
11805
11806 ComObjPtr<Machine> that = this;
11807 uint32_t flModifications = mData->flModifications;
11808 alock.release();
11809
11810 if (flModifications & IsModified_SharedFolders)
11811 that->onSharedFolderChange();
11812
11813 if (flModifications & IsModified_VRDEServer)
11814 that->onVRDEServerChange(/* aRestart */ TRUE);
11815 if (flModifications & IsModified_USB)
11816 that->onUSBControllerChange();
11817
11818 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11819 if (networkAdapters[slot])
11820 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11821 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11822 if (serialPorts[slot])
11823 that->onSerialPortChange(serialPorts[slot]);
11824 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11825 if (parallelPorts[slot])
11826 that->onParallelPortChange(parallelPorts[slot]);
11827
11828 if (flModifications & IsModified_Storage)
11829 that->onStorageControllerChange();
11830
11831#if 0
11832 if (flModifications & IsModified_BandwidthControl)
11833 that->onBandwidthControlChange();
11834#endif
11835 }
11836}
11837
11838/**
11839 * Commits all the changes to machine settings.
11840 *
11841 * Note that this operation is supposed to never fail.
11842 *
11843 * @note Locks this object and children for writing.
11844 */
11845void Machine::commit()
11846{
11847 AutoCaller autoCaller(this);
11848 AssertComRCReturnVoid(autoCaller.rc());
11849
11850 AutoCaller peerCaller(mPeer);
11851 AssertComRCReturnVoid(peerCaller.rc());
11852
11853 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11854
11855 /*
11856 * use safe commit to ensure Snapshot machines (that share mUserData)
11857 * will still refer to a valid memory location
11858 */
11859 mUserData.commitCopy();
11860
11861 mHWData.commit();
11862
11863 if (mMediaData.isBackedUp())
11864 commitMedia(Global::IsOnline(mData->mMachineState));
11865
11866 mBIOSSettings->commit();
11867 mVRDEServer->commit();
11868 mAudioAdapter->commit();
11869 mUSBController->commit();
11870 mBandwidthControl->commit();
11871
11872 /* Since mNetworkAdapters is a list which might have been changed (resized)
11873 * without using the Backupable<> template we need to handle the copying
11874 * of the list entries manually, including the creation of peers for the
11875 * new objects. */
11876 bool commitNetworkAdapters = false;
11877 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11878 if (mPeer)
11879 {
11880 /* commit everything, even the ones which will go away */
11881 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11882 mNetworkAdapters[slot]->commit();
11883 /* copy over the new entries, creating a peer and uninit the original */
11884 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11885 for (size_t slot = 0; slot < newSize; slot++)
11886 {
11887 /* look if this adapter has a peer device */
11888 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11889 if (!peer)
11890 {
11891 /* no peer means the adapter is a newly created one;
11892 * create a peer owning data this data share it with */
11893 peer.createObject();
11894 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11895 }
11896 mPeer->mNetworkAdapters[slot] = peer;
11897 }
11898 /* uninit any no longer needed network adapters */
11899 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11900 mNetworkAdapters[slot]->uninit();
11901 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11902 {
11903 if (mPeer->mNetworkAdapters[slot])
11904 mPeer->mNetworkAdapters[slot]->uninit();
11905 }
11906 /* Keep the original network adapter count until this point, so that
11907 * discarding a chipset type change will not lose settings. */
11908 mNetworkAdapters.resize(newSize);
11909 mPeer->mNetworkAdapters.resize(newSize);
11910 }
11911 else
11912 {
11913 /* we have no peer (our parent is the newly created machine);
11914 * just commit changes to the network adapters */
11915 commitNetworkAdapters = true;
11916 }
11917 if (commitNetworkAdapters)
11918 {
11919 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11920 mNetworkAdapters[slot]->commit();
11921 }
11922
11923 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11924 mSerialPorts[slot]->commit();
11925 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11926 mParallelPorts[slot]->commit();
11927
11928 bool commitStorageControllers = false;
11929
11930 if (mStorageControllers.isBackedUp())
11931 {
11932 mStorageControllers.commit();
11933
11934 if (mPeer)
11935 {
11936 /* Commit all changes to new controllers (this will reshare data with
11937 * peers for those who have peers) */
11938 StorageControllerList *newList = new StorageControllerList();
11939 StorageControllerList::const_iterator it = mStorageControllers->begin();
11940 while (it != mStorageControllers->end())
11941 {
11942 (*it)->commit();
11943
11944 /* look if this controller has a peer device */
11945 ComObjPtr<StorageController> peer = (*it)->getPeer();
11946 if (!peer)
11947 {
11948 /* no peer means the device is a newly created one;
11949 * create a peer owning data this device share it with */
11950 peer.createObject();
11951 peer->init(mPeer, *it, true /* aReshare */);
11952 }
11953 else
11954 {
11955 /* remove peer from the old list */
11956 mPeer->mStorageControllers->remove(peer);
11957 }
11958 /* and add it to the new list */
11959 newList->push_back(peer);
11960
11961 ++it;
11962 }
11963
11964 /* uninit old peer's controllers that are left */
11965 it = mPeer->mStorageControllers->begin();
11966 while (it != mPeer->mStorageControllers->end())
11967 {
11968 (*it)->uninit();
11969 ++it;
11970 }
11971
11972 /* attach new list of controllers to our peer */
11973 mPeer->mStorageControllers.attach(newList);
11974 }
11975 else
11976 {
11977 /* we have no peer (our parent is the newly created machine);
11978 * just commit changes to devices */
11979 commitStorageControllers = true;
11980 }
11981 }
11982 else
11983 {
11984 /* the list of controllers itself is not changed,
11985 * just commit changes to controllers themselves */
11986 commitStorageControllers = true;
11987 }
11988
11989 if (commitStorageControllers)
11990 {
11991 StorageControllerList::const_iterator it = mStorageControllers->begin();
11992 while (it != mStorageControllers->end())
11993 {
11994 (*it)->commit();
11995 ++it;
11996 }
11997 }
11998
11999 if (isSessionMachine())
12000 {
12001 /* attach new data to the primary machine and reshare it */
12002 mPeer->mUserData.attach(mUserData);
12003 mPeer->mHWData.attach(mHWData);
12004 /* mMediaData is reshared by fixupMedia */
12005 // mPeer->mMediaData.attach(mMediaData);
12006 Assert(mPeer->mMediaData.data() == mMediaData.data());
12007 }
12008}
12009
12010/**
12011 * Copies all the hardware data from the given machine.
12012 *
12013 * Currently, only called when the VM is being restored from a snapshot. In
12014 * particular, this implies that the VM is not running during this method's
12015 * call.
12016 *
12017 * @note This method must be called from under this object's lock.
12018 *
12019 * @note This method doesn't call #commit(), so all data remains backed up and
12020 * unsaved.
12021 */
12022void Machine::copyFrom(Machine *aThat)
12023{
12024 AssertReturnVoid(!isSnapshotMachine());
12025 AssertReturnVoid(aThat->isSnapshotMachine());
12026
12027 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12028
12029 mHWData.assignCopy(aThat->mHWData);
12030
12031 // create copies of all shared folders (mHWData after attaching a copy
12032 // contains just references to original objects)
12033 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12034 it != mHWData->mSharedFolders.end();
12035 ++it)
12036 {
12037 ComObjPtr<SharedFolder> folder;
12038 folder.createObject();
12039 HRESULT rc = folder->initCopy(getMachine(), *it);
12040 AssertComRC(rc);
12041 *it = folder;
12042 }
12043
12044 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12045 mVRDEServer->copyFrom(aThat->mVRDEServer);
12046 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12047 mUSBController->copyFrom(aThat->mUSBController);
12048 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12049
12050 /* create private copies of all controllers */
12051 mStorageControllers.backup();
12052 mStorageControllers->clear();
12053 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12054 it != aThat->mStorageControllers->end();
12055 ++it)
12056 {
12057 ComObjPtr<StorageController> ctrl;
12058 ctrl.createObject();
12059 ctrl->initCopy(this, *it);
12060 mStorageControllers->push_back(ctrl);
12061 }
12062
12063 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12064 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12065 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12066 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12067 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12068 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12069 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12070}
12071
12072/**
12073 * Returns whether the given storage controller is hotplug capable.
12074 *
12075 * @returns true if the controller supports hotplugging
12076 * false otherwise.
12077 * @param enmCtrlType The controller type to check for.
12078 */
12079bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12080{
12081 switch (enmCtrlType)
12082 {
12083 case StorageControllerType_IntelAhci:
12084 return true;
12085 case StorageControllerType_LsiLogic:
12086 case StorageControllerType_LsiLogicSas:
12087 case StorageControllerType_BusLogic:
12088 case StorageControllerType_PIIX3:
12089 case StorageControllerType_PIIX4:
12090 case StorageControllerType_ICH6:
12091 case StorageControllerType_I82078:
12092 default:
12093 return false;
12094 }
12095}
12096
12097#ifdef VBOX_WITH_RESOURCE_USAGE_API
12098
12099void Machine::getDiskList(MediaList &list)
12100{
12101 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12102 it != mMediaData->mAttachments.end();
12103 ++it)
12104 {
12105 MediumAttachment* pAttach = *it;
12106 /* just in case */
12107 AssertStmt(pAttach, continue);
12108
12109 AutoCaller localAutoCallerA(pAttach);
12110 if (FAILED(localAutoCallerA.rc())) continue;
12111
12112 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12113
12114 if (pAttach->getType() == DeviceType_HardDisk)
12115 list.push_back(pAttach->getMedium());
12116 }
12117}
12118
12119void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12120{
12121 AssertReturnVoid(isWriteLockOnCurrentThread());
12122 AssertPtrReturnVoid(aCollector);
12123
12124 pm::CollectorHAL *hal = aCollector->getHAL();
12125 /* Create sub metrics */
12126 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12127 "Percentage of processor time spent in user mode by the VM process.");
12128 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12129 "Percentage of processor time spent in kernel mode by the VM process.");
12130 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12131 "Size of resident portion of VM process in memory.");
12132 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12133 "Actual size of all VM disks combined.");
12134 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12135 "Network receive rate.");
12136 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12137 "Network transmit rate.");
12138 /* Create and register base metrics */
12139 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12140 cpuLoadUser, cpuLoadKernel);
12141 aCollector->registerBaseMetric(cpuLoad);
12142 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12143 ramUsageUsed);
12144 aCollector->registerBaseMetric(ramUsage);
12145 MediaList disks;
12146 getDiskList(disks);
12147 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12148 diskUsageUsed);
12149 aCollector->registerBaseMetric(diskUsage);
12150
12151 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12152 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12153 new pm::AggregateAvg()));
12154 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12155 new pm::AggregateMin()));
12156 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12157 new pm::AggregateMax()));
12158 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12159 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12160 new pm::AggregateAvg()));
12161 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12162 new pm::AggregateMin()));
12163 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12164 new pm::AggregateMax()));
12165
12166 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12167 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12168 new pm::AggregateAvg()));
12169 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12170 new pm::AggregateMin()));
12171 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12172 new pm::AggregateMax()));
12173
12174 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12175 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12176 new pm::AggregateAvg()));
12177 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12178 new pm::AggregateMin()));
12179 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12180 new pm::AggregateMax()));
12181
12182
12183 /* Guest metrics collector */
12184 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12185 aCollector->registerGuest(mCollectorGuest);
12186 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12187 this, __PRETTY_FUNCTION__, mCollectorGuest));
12188
12189 /* Create sub metrics */
12190 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12191 "Percentage of processor time spent in user mode as seen by the guest.");
12192 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12193 "Percentage of processor time spent in kernel mode as seen by the guest.");
12194 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12195 "Percentage of processor time spent idling as seen by the guest.");
12196
12197 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12198 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12199 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12200 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12201 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12202 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12203
12204 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12205
12206 /* Create and register base metrics */
12207 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12208 machineNetRx, machineNetTx);
12209 aCollector->registerBaseMetric(machineNetRate);
12210
12211 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12212 guestLoadUser, guestLoadKernel, guestLoadIdle);
12213 aCollector->registerBaseMetric(guestCpuLoad);
12214
12215 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12216 guestMemTotal, guestMemFree,
12217 guestMemBalloon, guestMemShared,
12218 guestMemCache, guestPagedTotal);
12219 aCollector->registerBaseMetric(guestCpuMem);
12220
12221 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12222 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12223 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12224 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12225
12226 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12227 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12228 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12229 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12230
12231 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12232 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12233 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12234 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12235
12236 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12237 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12238 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12239 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12240
12241 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12242 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12243 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12244 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12245
12246 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12248 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12249 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12250
12251 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12252 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12253 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12255
12256 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12260
12261 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12262 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12263 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12264 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12265
12266 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12267 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12268 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12269 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12270
12271 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12272 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12273 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12274 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12275}
12276
12277void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12278{
12279 AssertReturnVoid(isWriteLockOnCurrentThread());
12280
12281 if (aCollector)
12282 {
12283 aCollector->unregisterMetricsFor(aMachine);
12284 aCollector->unregisterBaseMetricsFor(aMachine);
12285 }
12286}
12287
12288#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12289
12290
12291////////////////////////////////////////////////////////////////////////////////
12292
12293DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12294
12295HRESULT SessionMachine::FinalConstruct()
12296{
12297 LogFlowThisFunc(("\n"));
12298
12299#if defined(RT_OS_WINDOWS)
12300 mIPCSem = NULL;
12301#elif defined(RT_OS_OS2)
12302 mIPCSem = NULLHANDLE;
12303#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12304 mIPCSem = -1;
12305#else
12306# error "Port me!"
12307#endif
12308
12309 return BaseFinalConstruct();
12310}
12311
12312void SessionMachine::FinalRelease()
12313{
12314 LogFlowThisFunc(("\n"));
12315
12316 uninit(Uninit::Unexpected);
12317
12318 BaseFinalRelease();
12319}
12320
12321/**
12322 * @note Must be called only by Machine::openSession() from its own write lock.
12323 */
12324HRESULT SessionMachine::init(Machine *aMachine)
12325{
12326 LogFlowThisFuncEnter();
12327 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12328
12329 AssertReturn(aMachine, E_INVALIDARG);
12330
12331 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12332
12333 /* Enclose the state transition NotReady->InInit->Ready */
12334 AutoInitSpan autoInitSpan(this);
12335 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12336
12337 /* create the interprocess semaphore */
12338#if defined(RT_OS_WINDOWS)
12339 mIPCSemName = aMachine->mData->m_strConfigFileFull;
12340 for (size_t i = 0; i < mIPCSemName.length(); i++)
12341 if (mIPCSemName.raw()[i] == '\\')
12342 mIPCSemName.raw()[i] = '/';
12343 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12344 ComAssertMsgRet(mIPCSem,
12345 ("Cannot create IPC mutex '%ls', err=%d",
12346 mIPCSemName.raw(), ::GetLastError()),
12347 E_FAIL);
12348#elif defined(RT_OS_OS2)
12349 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12350 aMachine->mData->mUuid.raw());
12351 mIPCSemName = ipcSem;
12352 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12353 ComAssertMsgRet(arc == NO_ERROR,
12354 ("Cannot create IPC mutex '%s', arc=%ld",
12355 ipcSem.c_str(), arc),
12356 E_FAIL);
12357#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12358# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12359# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12360 /** @todo Check that this still works correctly. */
12361 AssertCompileSize(key_t, 8);
12362# else
12363 AssertCompileSize(key_t, 4);
12364# endif
12365 key_t key;
12366 mIPCSem = -1;
12367 mIPCKey = "0";
12368 for (uint32_t i = 0; i < 1 << 24; i++)
12369 {
12370 key = ((uint32_t)'V' << 24) | i;
12371 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12372 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12373 {
12374 mIPCSem = sem;
12375 if (sem >= 0)
12376 mIPCKey = BstrFmt("%u", key);
12377 break;
12378 }
12379 }
12380# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12381 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12382 char *pszSemName = NULL;
12383 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12384 key_t key = ::ftok(pszSemName, 'V');
12385 RTStrFree(pszSemName);
12386
12387 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12388# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12389
12390 int errnoSave = errno;
12391 if (mIPCSem < 0 && errnoSave == ENOSYS)
12392 {
12393 setError(E_FAIL,
12394 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12395 "support for SysV IPC. Check the host kernel configuration for "
12396 "CONFIG_SYSVIPC=y"));
12397 return E_FAIL;
12398 }
12399 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12400 * the IPC semaphores */
12401 if (mIPCSem < 0 && errnoSave == ENOSPC)
12402 {
12403#ifdef RT_OS_LINUX
12404 setError(E_FAIL,
12405 tr("Cannot create IPC semaphore because the system limit for the "
12406 "maximum number of semaphore sets (SEMMNI), or the system wide "
12407 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12408 "current set of SysV IPC semaphores can be determined from "
12409 "the file /proc/sysvipc/sem"));
12410#else
12411 setError(E_FAIL,
12412 tr("Cannot create IPC semaphore because the system-imposed limit "
12413 "on the maximum number of allowed semaphores or semaphore "
12414 "identifiers system-wide would be exceeded"));
12415#endif
12416 return E_FAIL;
12417 }
12418 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12419 E_FAIL);
12420 /* set the initial value to 1 */
12421 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12422 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12423 E_FAIL);
12424#else
12425# error "Port me!"
12426#endif
12427
12428 /* memorize the peer Machine */
12429 unconst(mPeer) = aMachine;
12430 /* share the parent pointer */
12431 unconst(mParent) = aMachine->mParent;
12432
12433 /* take the pointers to data to share */
12434 mData.share(aMachine->mData);
12435 mSSData.share(aMachine->mSSData);
12436
12437 mUserData.share(aMachine->mUserData);
12438 mHWData.share(aMachine->mHWData);
12439 mMediaData.share(aMachine->mMediaData);
12440
12441 mStorageControllers.allocate();
12442 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12443 it != aMachine->mStorageControllers->end();
12444 ++it)
12445 {
12446 ComObjPtr<StorageController> ctl;
12447 ctl.createObject();
12448 ctl->init(this, *it);
12449 mStorageControllers->push_back(ctl);
12450 }
12451
12452 unconst(mBIOSSettings).createObject();
12453 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12454 /* create another VRDEServer object that will be mutable */
12455 unconst(mVRDEServer).createObject();
12456 mVRDEServer->init(this, aMachine->mVRDEServer);
12457 /* create another audio adapter object that will be mutable */
12458 unconst(mAudioAdapter).createObject();
12459 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12460 /* create a list of serial ports that will be mutable */
12461 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12462 {
12463 unconst(mSerialPorts[slot]).createObject();
12464 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12465 }
12466 /* create a list of parallel ports that will be mutable */
12467 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12468 {
12469 unconst(mParallelPorts[slot]).createObject();
12470 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12471 }
12472 /* create another USB controller object that will be mutable */
12473 unconst(mUSBController).createObject();
12474 mUSBController->init(this, aMachine->mUSBController);
12475
12476 /* create a list of network adapters that will be mutable */
12477 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12478 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12479 {
12480 unconst(mNetworkAdapters[slot]).createObject();
12481 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12482 }
12483
12484 /* create another bandwidth control object that will be mutable */
12485 unconst(mBandwidthControl).createObject();
12486 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12487
12488 /* default is to delete saved state on Saved -> PoweredOff transition */
12489 mRemoveSavedState = true;
12490
12491 /* Confirm a successful initialization when it's the case */
12492 autoInitSpan.setSucceeded();
12493
12494 LogFlowThisFuncLeave();
12495 return S_OK;
12496}
12497
12498/**
12499 * Uninitializes this session object. If the reason is other than
12500 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12501 *
12502 * @param aReason uninitialization reason
12503 *
12504 * @note Locks mParent + this object for writing.
12505 */
12506void SessionMachine::uninit(Uninit::Reason aReason)
12507{
12508 LogFlowThisFuncEnter();
12509 LogFlowThisFunc(("reason=%d\n", aReason));
12510
12511 /*
12512 * Strongly reference ourselves to prevent this object deletion after
12513 * mData->mSession.mMachine.setNull() below (which can release the last
12514 * reference and call the destructor). Important: this must be done before
12515 * accessing any members (and before AutoUninitSpan that does it as well).
12516 * This self reference will be released as the very last step on return.
12517 */
12518 ComObjPtr<SessionMachine> selfRef = this;
12519
12520 /* Enclose the state transition Ready->InUninit->NotReady */
12521 AutoUninitSpan autoUninitSpan(this);
12522 if (autoUninitSpan.uninitDone())
12523 {
12524 LogFlowThisFunc(("Already uninitialized\n"));
12525 LogFlowThisFuncLeave();
12526 return;
12527 }
12528
12529 if (autoUninitSpan.initFailed())
12530 {
12531 /* We've been called by init() because it's failed. It's not really
12532 * necessary (nor it's safe) to perform the regular uninit sequence
12533 * below, the following is enough.
12534 */
12535 LogFlowThisFunc(("Initialization failed.\n"));
12536#if defined(RT_OS_WINDOWS)
12537 if (mIPCSem)
12538 ::CloseHandle(mIPCSem);
12539 mIPCSem = NULL;
12540#elif defined(RT_OS_OS2)
12541 if (mIPCSem != NULLHANDLE)
12542 ::DosCloseMutexSem(mIPCSem);
12543 mIPCSem = NULLHANDLE;
12544#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12545 if (mIPCSem >= 0)
12546 ::semctl(mIPCSem, 0, IPC_RMID);
12547 mIPCSem = -1;
12548# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12549 mIPCKey = "0";
12550# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12551#else
12552# error "Port me!"
12553#endif
12554 uninitDataAndChildObjects();
12555 mData.free();
12556 unconst(mParent) = NULL;
12557 unconst(mPeer) = NULL;
12558 LogFlowThisFuncLeave();
12559 return;
12560 }
12561
12562 MachineState_T lastState;
12563 {
12564 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12565 lastState = mData->mMachineState;
12566 }
12567 NOREF(lastState);
12568
12569#ifdef VBOX_WITH_USB
12570 // release all captured USB devices, but do this before requesting the locks below
12571 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12572 {
12573 /* Console::captureUSBDevices() is called in the VM process only after
12574 * setting the machine state to Starting or Restoring.
12575 * Console::detachAllUSBDevices() will be called upon successful
12576 * termination. So, we need to release USB devices only if there was
12577 * an abnormal termination of a running VM.
12578 *
12579 * This is identical to SessionMachine::DetachAllUSBDevices except
12580 * for the aAbnormal argument. */
12581 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12582 AssertComRC(rc);
12583 NOREF(rc);
12584
12585 USBProxyService *service = mParent->host()->usbProxyService();
12586 if (service)
12587 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12588 }
12589#endif /* VBOX_WITH_USB */
12590
12591 // we need to lock this object in uninit() because the lock is shared
12592 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12593 // and others need mParent lock, and USB needs host lock.
12594 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12595
12596#ifdef VBOX_WITH_RESOURCE_USAGE_API
12597 /*
12598 * It is safe to call Machine::unregisterMetrics() here because
12599 * PerformanceCollector::samplerCallback no longer accesses guest methods
12600 * holding the lock.
12601 */
12602 unregisterMetrics(mParent->performanceCollector(), mPeer);
12603 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12604 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12605 this, __PRETTY_FUNCTION__, mCollectorGuest));
12606 if (mCollectorGuest)
12607 {
12608 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12609 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12610 mCollectorGuest = NULL;
12611 }
12612#endif
12613
12614 if (aReason == Uninit::Abnormal)
12615 {
12616 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12617 Global::IsOnlineOrTransient(lastState)));
12618
12619 /* reset the state to Aborted */
12620 if (mData->mMachineState != MachineState_Aborted)
12621 setMachineState(MachineState_Aborted);
12622 }
12623
12624 // any machine settings modified?
12625 if (mData->flModifications)
12626 {
12627 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12628 rollback(false /* aNotify */);
12629 }
12630
12631 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12632 || !mConsoleTaskData.mSnapshot);
12633 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12634 {
12635 LogWarningThisFunc(("canceling failed save state request!\n"));
12636 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12637 }
12638 else if (!mConsoleTaskData.mSnapshot.isNull())
12639 {
12640 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12641
12642 /* delete all differencing hard disks created (this will also attach
12643 * their parents back by rolling back mMediaData) */
12644 rollbackMedia();
12645
12646 // delete the saved state file (it might have been already created)
12647 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12648 // think it's still in use
12649 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12650 mConsoleTaskData.mSnapshot->uninit();
12651 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12652 }
12653
12654 if (!mData->mSession.mType.isEmpty())
12655 {
12656 /* mType is not null when this machine's process has been started by
12657 * Machine::LaunchVMProcess(), therefore it is our child. We
12658 * need to queue the PID to reap the process (and avoid zombies on
12659 * Linux). */
12660 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12661 mParent->addProcessToReap(mData->mSession.mPID);
12662 }
12663
12664 mData->mSession.mPID = NIL_RTPROCESS;
12665
12666 if (aReason == Uninit::Unexpected)
12667 {
12668 /* Uninitialization didn't come from #checkForDeath(), so tell the
12669 * client watcher thread to update the set of machines that have open
12670 * sessions. */
12671 mParent->updateClientWatcher();
12672 }
12673
12674 /* uninitialize all remote controls */
12675 if (mData->mSession.mRemoteControls.size())
12676 {
12677 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12678 mData->mSession.mRemoteControls.size()));
12679
12680 Data::Session::RemoteControlList::iterator it =
12681 mData->mSession.mRemoteControls.begin();
12682 while (it != mData->mSession.mRemoteControls.end())
12683 {
12684 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12685 HRESULT rc = (*it)->Uninitialize();
12686 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12687 if (FAILED(rc))
12688 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12689 ++it;
12690 }
12691 mData->mSession.mRemoteControls.clear();
12692 }
12693
12694 /*
12695 * An expected uninitialization can come only from #checkForDeath().
12696 * Otherwise it means that something's gone really wrong (for example,
12697 * the Session implementation has released the VirtualBox reference
12698 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12699 * etc). However, it's also possible, that the client releases the IPC
12700 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12701 * but the VirtualBox release event comes first to the server process.
12702 * This case is practically possible, so we should not assert on an
12703 * unexpected uninit, just log a warning.
12704 */
12705
12706 if ((aReason == Uninit::Unexpected))
12707 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12708
12709 if (aReason != Uninit::Normal)
12710 {
12711 mData->mSession.mDirectControl.setNull();
12712 }
12713 else
12714 {
12715 /* this must be null here (see #OnSessionEnd()) */
12716 Assert(mData->mSession.mDirectControl.isNull());
12717 Assert(mData->mSession.mState == SessionState_Unlocking);
12718 Assert(!mData->mSession.mProgress.isNull());
12719 }
12720 if (mData->mSession.mProgress)
12721 {
12722 if (aReason == Uninit::Normal)
12723 mData->mSession.mProgress->notifyComplete(S_OK);
12724 else
12725 mData->mSession.mProgress->notifyComplete(E_FAIL,
12726 COM_IIDOF(ISession),
12727 getComponentName(),
12728 tr("The VM session was aborted"));
12729 mData->mSession.mProgress.setNull();
12730 }
12731
12732 /* remove the association between the peer machine and this session machine */
12733 Assert( (SessionMachine*)mData->mSession.mMachine == this
12734 || aReason == Uninit::Unexpected);
12735
12736 /* reset the rest of session data */
12737 mData->mSession.mMachine.setNull();
12738 mData->mSession.mState = SessionState_Unlocked;
12739 mData->mSession.mType.setNull();
12740
12741 /* close the interprocess semaphore before leaving the exclusive lock */
12742#if defined(RT_OS_WINDOWS)
12743 if (mIPCSem)
12744 ::CloseHandle(mIPCSem);
12745 mIPCSem = NULL;
12746#elif defined(RT_OS_OS2)
12747 if (mIPCSem != NULLHANDLE)
12748 ::DosCloseMutexSem(mIPCSem);
12749 mIPCSem = NULLHANDLE;
12750#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12751 if (mIPCSem >= 0)
12752 ::semctl(mIPCSem, 0, IPC_RMID);
12753 mIPCSem = -1;
12754# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12755 mIPCKey = "0";
12756# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12757#else
12758# error "Port me!"
12759#endif
12760
12761 /* fire an event */
12762 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12763
12764 uninitDataAndChildObjects();
12765
12766 /* free the essential data structure last */
12767 mData.free();
12768
12769 /* release the exclusive lock before setting the below two to NULL */
12770 multilock.release();
12771
12772 unconst(mParent) = NULL;
12773 unconst(mPeer) = NULL;
12774
12775 LogFlowThisFuncLeave();
12776}
12777
12778// util::Lockable interface
12779////////////////////////////////////////////////////////////////////////////////
12780
12781/**
12782 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12783 * with the primary Machine instance (mPeer).
12784 */
12785RWLockHandle *SessionMachine::lockHandle() const
12786{
12787 AssertReturn(mPeer != NULL, NULL);
12788 return mPeer->lockHandle();
12789}
12790
12791// IInternalMachineControl methods
12792////////////////////////////////////////////////////////////////////////////////
12793
12794/**
12795 * Passes collected guest statistics to performance collector object
12796 */
12797STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12798 ULONG aCpuKernel, ULONG aCpuIdle,
12799 ULONG aMemTotal, ULONG aMemFree,
12800 ULONG aMemBalloon, ULONG aMemShared,
12801 ULONG aMemCache, ULONG aPageTotal,
12802 ULONG aAllocVMM, ULONG aFreeVMM,
12803 ULONG aBalloonedVMM, ULONG aSharedVMM,
12804 ULONG aVmNetRx, ULONG aVmNetTx)
12805{
12806#ifdef VBOX_WITH_RESOURCE_USAGE_API
12807 if (mCollectorGuest)
12808 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12809 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12810 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12811 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12812
12813 return S_OK;
12814#else
12815 NOREF(aValidStats);
12816 NOREF(aCpuUser);
12817 NOREF(aCpuKernel);
12818 NOREF(aCpuIdle);
12819 NOREF(aMemTotal);
12820 NOREF(aMemFree);
12821 NOREF(aMemBalloon);
12822 NOREF(aMemShared);
12823 NOREF(aMemCache);
12824 NOREF(aPageTotal);
12825 NOREF(aAllocVMM);
12826 NOREF(aFreeVMM);
12827 NOREF(aBalloonedVMM);
12828 NOREF(aSharedVMM);
12829 NOREF(aVmNetRx);
12830 NOREF(aVmNetTx);
12831 return E_NOTIMPL;
12832#endif
12833}
12834
12835/**
12836 * @note Locks this object for writing.
12837 */
12838STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12839{
12840 AutoCaller autoCaller(this);
12841 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12842
12843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12844
12845 mRemoveSavedState = aRemove;
12846
12847 return S_OK;
12848}
12849
12850/**
12851 * @note Locks the same as #setMachineState() does.
12852 */
12853STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12854{
12855 return setMachineState(aMachineState);
12856}
12857
12858/**
12859 * @note Locks this object for reading.
12860 */
12861STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12862{
12863 AutoCaller autoCaller(this);
12864 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12865
12866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12867
12868#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12869 mIPCSemName.cloneTo(aId);
12870 return S_OK;
12871#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12872# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12873 mIPCKey.cloneTo(aId);
12874# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12875 mData->m_strConfigFileFull.cloneTo(aId);
12876# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12877 return S_OK;
12878#else
12879# error "Port me!"
12880#endif
12881}
12882
12883/**
12884 * @note Locks this object for writing.
12885 */
12886STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12887{
12888 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12889 AutoCaller autoCaller(this);
12890 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12891
12892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12893
12894 if (mData->mSession.mState != SessionState_Locked)
12895 return VBOX_E_INVALID_OBJECT_STATE;
12896
12897 if (!mData->mSession.mProgress.isNull())
12898 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12899
12900 LogFlowThisFunc(("returns S_OK.\n"));
12901 return S_OK;
12902}
12903
12904/**
12905 * @note Locks this object for writing.
12906 */
12907STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12908{
12909 AutoCaller autoCaller(this);
12910 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12911
12912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12913
12914 if (mData->mSession.mState != SessionState_Locked)
12915 return VBOX_E_INVALID_OBJECT_STATE;
12916
12917 /* Finalize the LaunchVMProcess progress object. */
12918 if (mData->mSession.mProgress)
12919 {
12920 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12921 mData->mSession.mProgress.setNull();
12922 }
12923
12924 if (SUCCEEDED((HRESULT)iResult))
12925 {
12926#ifdef VBOX_WITH_RESOURCE_USAGE_API
12927 /* The VM has been powered up successfully, so it makes sense
12928 * now to offer the performance metrics for a running machine
12929 * object. Doing it earlier wouldn't be safe. */
12930 registerMetrics(mParent->performanceCollector(), mPeer,
12931 mData->mSession.mPID);
12932#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12933 }
12934
12935 return S_OK;
12936}
12937
12938/**
12939 * @note Locks this object for writing.
12940 */
12941STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12942{
12943 LogFlowThisFuncEnter();
12944
12945 CheckComArgOutPointerValid(aProgress);
12946
12947 AutoCaller autoCaller(this);
12948 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12949
12950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12951
12952 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12953 E_FAIL);
12954
12955 /* create a progress object to track operation completion */
12956 ComObjPtr<Progress> pProgress;
12957 pProgress.createObject();
12958 pProgress->init(getVirtualBox(),
12959 static_cast<IMachine *>(this) /* aInitiator */,
12960 Bstr(tr("Stopping the virtual machine")).raw(),
12961 FALSE /* aCancelable */);
12962
12963 /* fill in the console task data */
12964 mConsoleTaskData.mLastState = mData->mMachineState;
12965 mConsoleTaskData.mProgress = pProgress;
12966
12967 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12968 setMachineState(MachineState_Stopping);
12969
12970 pProgress.queryInterfaceTo(aProgress);
12971
12972 return S_OK;
12973}
12974
12975/**
12976 * @note Locks this object for writing.
12977 */
12978STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12979{
12980 LogFlowThisFuncEnter();
12981
12982 AutoCaller autoCaller(this);
12983 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12984
12985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12986
12987 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12988 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12989 && mConsoleTaskData.mLastState != MachineState_Null,
12990 E_FAIL);
12991
12992 /*
12993 * On failure, set the state to the state we had when BeginPoweringDown()
12994 * was called (this is expected by Console::PowerDown() and the associated
12995 * task). On success the VM process already changed the state to
12996 * MachineState_PoweredOff, so no need to do anything.
12997 */
12998 if (FAILED(iResult))
12999 setMachineState(mConsoleTaskData.mLastState);
13000
13001 /* notify the progress object about operation completion */
13002 Assert(mConsoleTaskData.mProgress);
13003 if (SUCCEEDED(iResult))
13004 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13005 else
13006 {
13007 Utf8Str strErrMsg(aErrMsg);
13008 if (strErrMsg.length())
13009 mConsoleTaskData.mProgress->notifyComplete(iResult,
13010 COM_IIDOF(ISession),
13011 getComponentName(),
13012 strErrMsg.c_str());
13013 else
13014 mConsoleTaskData.mProgress->notifyComplete(iResult);
13015 }
13016
13017 /* clear out the temporary saved state data */
13018 mConsoleTaskData.mLastState = MachineState_Null;
13019 mConsoleTaskData.mProgress.setNull();
13020
13021 LogFlowThisFuncLeave();
13022 return S_OK;
13023}
13024
13025
13026/**
13027 * Goes through the USB filters of the given machine to see if the given
13028 * device matches any filter or not.
13029 *
13030 * @note Locks the same as USBController::hasMatchingFilter() does.
13031 */
13032STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13033 BOOL *aMatched,
13034 ULONG *aMaskedIfs)
13035{
13036 LogFlowThisFunc(("\n"));
13037
13038 CheckComArgNotNull(aUSBDevice);
13039 CheckComArgOutPointerValid(aMatched);
13040
13041 AutoCaller autoCaller(this);
13042 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13043
13044#ifdef VBOX_WITH_USB
13045 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13046#else
13047 NOREF(aUSBDevice);
13048 NOREF(aMaskedIfs);
13049 *aMatched = FALSE;
13050#endif
13051
13052 return S_OK;
13053}
13054
13055/**
13056 * @note Locks the same as Host::captureUSBDevice() does.
13057 */
13058STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13059{
13060 LogFlowThisFunc(("\n"));
13061
13062 AutoCaller autoCaller(this);
13063 AssertComRCReturnRC(autoCaller.rc());
13064
13065#ifdef VBOX_WITH_USB
13066 /* if captureDeviceForVM() fails, it must have set extended error info */
13067 clearError();
13068 MultiResult rc = mParent->host()->checkUSBProxyService();
13069 if (FAILED(rc)) return rc;
13070
13071 USBProxyService *service = mParent->host()->usbProxyService();
13072 AssertReturn(service, E_FAIL);
13073 return service->captureDeviceForVM(this, Guid(aId).ref());
13074#else
13075 NOREF(aId);
13076 return E_NOTIMPL;
13077#endif
13078}
13079
13080/**
13081 * @note Locks the same as Host::detachUSBDevice() does.
13082 */
13083STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13084{
13085 LogFlowThisFunc(("\n"));
13086
13087 AutoCaller autoCaller(this);
13088 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13089
13090#ifdef VBOX_WITH_USB
13091 USBProxyService *service = mParent->host()->usbProxyService();
13092 AssertReturn(service, E_FAIL);
13093 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13094#else
13095 NOREF(aId);
13096 NOREF(aDone);
13097 return E_NOTIMPL;
13098#endif
13099}
13100
13101/**
13102 * Inserts all machine filters to the USB proxy service and then calls
13103 * Host::autoCaptureUSBDevices().
13104 *
13105 * Called by Console from the VM process upon VM startup.
13106 *
13107 * @note Locks what called methods lock.
13108 */
13109STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13110{
13111 LogFlowThisFunc(("\n"));
13112
13113 AutoCaller autoCaller(this);
13114 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13115
13116#ifdef VBOX_WITH_USB
13117 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
13118 AssertComRC(rc);
13119 NOREF(rc);
13120
13121 USBProxyService *service = mParent->host()->usbProxyService();
13122 AssertReturn(service, E_FAIL);
13123 return service->autoCaptureDevicesForVM(this);
13124#else
13125 return S_OK;
13126#endif
13127}
13128
13129/**
13130 * Removes all machine filters from the USB proxy service and then calls
13131 * Host::detachAllUSBDevices().
13132 *
13133 * Called by Console from the VM process upon normal VM termination or by
13134 * SessionMachine::uninit() upon abnormal VM termination (from under the
13135 * Machine/SessionMachine lock).
13136 *
13137 * @note Locks what called methods lock.
13138 */
13139STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13140{
13141 LogFlowThisFunc(("\n"));
13142
13143 AutoCaller autoCaller(this);
13144 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13145
13146#ifdef VBOX_WITH_USB
13147 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
13148 AssertComRC(rc);
13149 NOREF(rc);
13150
13151 USBProxyService *service = mParent->host()->usbProxyService();
13152 AssertReturn(service, E_FAIL);
13153 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13154#else
13155 NOREF(aDone);
13156 return S_OK;
13157#endif
13158}
13159
13160/**
13161 * @note Locks this object for writing.
13162 */
13163STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13164 IProgress **aProgress)
13165{
13166 LogFlowThisFuncEnter();
13167
13168 AssertReturn(aSession, E_INVALIDARG);
13169 AssertReturn(aProgress, E_INVALIDARG);
13170
13171 AutoCaller autoCaller(this);
13172
13173 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13174 /*
13175 * We don't assert below because it might happen that a non-direct session
13176 * informs us it is closed right after we've been uninitialized -- it's ok.
13177 */
13178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13179
13180 /* get IInternalSessionControl interface */
13181 ComPtr<IInternalSessionControl> control(aSession);
13182
13183 ComAssertRet(!control.isNull(), E_INVALIDARG);
13184
13185 /* Creating a Progress object requires the VirtualBox lock, and
13186 * thus locking it here is required by the lock order rules. */
13187 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13188
13189 if (control == mData->mSession.mDirectControl)
13190 {
13191 ComAssertRet(aProgress, E_POINTER);
13192
13193 /* The direct session is being normally closed by the client process
13194 * ----------------------------------------------------------------- */
13195
13196 /* go to the closing state (essential for all open*Session() calls and
13197 * for #checkForDeath()) */
13198 Assert(mData->mSession.mState == SessionState_Locked);
13199 mData->mSession.mState = SessionState_Unlocking;
13200
13201 /* set direct control to NULL to release the remote instance */
13202 mData->mSession.mDirectControl.setNull();
13203 LogFlowThisFunc(("Direct control is set to NULL\n"));
13204
13205 if (mData->mSession.mProgress)
13206 {
13207 /* finalize the progress, someone might wait if a frontend
13208 * closes the session before powering on the VM. */
13209 mData->mSession.mProgress->notifyComplete(E_FAIL,
13210 COM_IIDOF(ISession),
13211 getComponentName(),
13212 tr("The VM session was closed before any attempt to power it on"));
13213 mData->mSession.mProgress.setNull();
13214 }
13215
13216 /* Create the progress object the client will use to wait until
13217 * #checkForDeath() is called to uninitialize this session object after
13218 * it releases the IPC semaphore.
13219 * Note! Because we're "reusing" mProgress here, this must be a proxy
13220 * object just like for LaunchVMProcess. */
13221 Assert(mData->mSession.mProgress.isNull());
13222 ComObjPtr<ProgressProxy> progress;
13223 progress.createObject();
13224 ComPtr<IUnknown> pPeer(mPeer);
13225 progress->init(mParent, pPeer,
13226 Bstr(tr("Closing session")).raw(),
13227 FALSE /* aCancelable */);
13228 progress.queryInterfaceTo(aProgress);
13229 mData->mSession.mProgress = progress;
13230 }
13231 else
13232 {
13233 /* the remote session is being normally closed */
13234 Data::Session::RemoteControlList::iterator it =
13235 mData->mSession.mRemoteControls.begin();
13236 while (it != mData->mSession.mRemoteControls.end())
13237 {
13238 if (control == *it)
13239 break;
13240 ++it;
13241 }
13242 BOOL found = it != mData->mSession.mRemoteControls.end();
13243 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13244 E_INVALIDARG);
13245 // This MUST be erase(it), not remove(*it) as the latter triggers a
13246 // very nasty use after free due to the place where the value "lives".
13247 mData->mSession.mRemoteControls.erase(it);
13248 }
13249
13250 /* signal the client watcher thread, because the client is going away */
13251 mParent->updateClientWatcher();
13252
13253 LogFlowThisFuncLeave();
13254 return S_OK;
13255}
13256
13257/**
13258 * @note Locks this object for writing.
13259 */
13260STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13261{
13262 LogFlowThisFuncEnter();
13263
13264 CheckComArgOutPointerValid(aProgress);
13265 CheckComArgOutPointerValid(aStateFilePath);
13266
13267 AutoCaller autoCaller(this);
13268 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13269
13270 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13271
13272 AssertReturn( mData->mMachineState == MachineState_Paused
13273 && mConsoleTaskData.mLastState == MachineState_Null
13274 && mConsoleTaskData.strStateFilePath.isEmpty(),
13275 E_FAIL);
13276
13277 /* create a progress object to track operation completion */
13278 ComObjPtr<Progress> pProgress;
13279 pProgress.createObject();
13280 pProgress->init(getVirtualBox(),
13281 static_cast<IMachine *>(this) /* aInitiator */,
13282 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13283 FALSE /* aCancelable */);
13284
13285 Utf8Str strStateFilePath;
13286 /* stateFilePath is null when the machine is not running */
13287 if (mData->mMachineState == MachineState_Paused)
13288 composeSavedStateFilename(strStateFilePath);
13289
13290 /* fill in the console task data */
13291 mConsoleTaskData.mLastState = mData->mMachineState;
13292 mConsoleTaskData.strStateFilePath = strStateFilePath;
13293 mConsoleTaskData.mProgress = pProgress;
13294
13295 /* set the state to Saving (this is expected by Console::SaveState()) */
13296 setMachineState(MachineState_Saving);
13297
13298 strStateFilePath.cloneTo(aStateFilePath);
13299 pProgress.queryInterfaceTo(aProgress);
13300
13301 return S_OK;
13302}
13303
13304/**
13305 * @note Locks mParent + this object for writing.
13306 */
13307STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13308{
13309 LogFlowThisFunc(("\n"));
13310
13311 AutoCaller autoCaller(this);
13312 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13313
13314 /* endSavingState() need mParent lock */
13315 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13316
13317 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13318 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13319 && mConsoleTaskData.mLastState != MachineState_Null
13320 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13321 E_FAIL);
13322
13323 /*
13324 * On failure, set the state to the state we had when BeginSavingState()
13325 * was called (this is expected by Console::SaveState() and the associated
13326 * task). On success the VM process already changed the state to
13327 * MachineState_Saved, so no need to do anything.
13328 */
13329 if (FAILED(iResult))
13330 setMachineState(mConsoleTaskData.mLastState);
13331
13332 return endSavingState(iResult, aErrMsg);
13333}
13334
13335/**
13336 * @note Locks this object for writing.
13337 */
13338STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13339{
13340 LogFlowThisFunc(("\n"));
13341
13342 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13343
13344 AutoCaller autoCaller(this);
13345 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13346
13347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13348
13349 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13350 || mData->mMachineState == MachineState_Teleported
13351 || mData->mMachineState == MachineState_Aborted
13352 , E_FAIL); /** @todo setError. */
13353
13354 Utf8Str stateFilePathFull = aSavedStateFile;
13355 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13356 if (RT_FAILURE(vrc))
13357 return setError(VBOX_E_FILE_ERROR,
13358 tr("Invalid saved state file path '%ls' (%Rrc)"),
13359 aSavedStateFile,
13360 vrc);
13361
13362 mSSData->strStateFilePath = stateFilePathFull;
13363
13364 /* The below setMachineState() will detect the state transition and will
13365 * update the settings file */
13366
13367 return setMachineState(MachineState_Saved);
13368}
13369
13370STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13371 ComSafeArrayOut(BSTR, aValues),
13372 ComSafeArrayOut(LONG64, aTimestamps),
13373 ComSafeArrayOut(BSTR, aFlags))
13374{
13375 LogFlowThisFunc(("\n"));
13376
13377#ifdef VBOX_WITH_GUEST_PROPS
13378 using namespace guestProp;
13379
13380 AutoCaller autoCaller(this);
13381 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13382
13383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13384
13385 CheckComArgOutSafeArrayPointerValid(aNames);
13386 CheckComArgOutSafeArrayPointerValid(aValues);
13387 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13388 CheckComArgOutSafeArrayPointerValid(aFlags);
13389
13390 size_t cEntries = mHWData->mGuestProperties.size();
13391 com::SafeArray<BSTR> names(cEntries);
13392 com::SafeArray<BSTR> values(cEntries);
13393 com::SafeArray<LONG64> timestamps(cEntries);
13394 com::SafeArray<BSTR> flags(cEntries);
13395 unsigned i = 0;
13396 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13397 it != mHWData->mGuestProperties.end();
13398 ++it)
13399 {
13400 char szFlags[MAX_FLAGS_LEN + 1];
13401 it->first.cloneTo(&names[i]);
13402 it->second.strValue.cloneTo(&values[i]);
13403 timestamps[i] = it->second.mTimestamp;
13404 /* If it is NULL, keep it NULL. */
13405 if (it->second.mFlags)
13406 {
13407 writeFlags(it->second.mFlags, szFlags);
13408 Bstr(szFlags).cloneTo(&flags[i]);
13409 }
13410 else
13411 flags[i] = NULL;
13412 ++i;
13413 }
13414 names.detachTo(ComSafeArrayOutArg(aNames));
13415 values.detachTo(ComSafeArrayOutArg(aValues));
13416 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13417 flags.detachTo(ComSafeArrayOutArg(aFlags));
13418 return S_OK;
13419#else
13420 ReturnComNotImplemented();
13421#endif
13422}
13423
13424STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13425 IN_BSTR aValue,
13426 LONG64 aTimestamp,
13427 IN_BSTR aFlags)
13428{
13429 LogFlowThisFunc(("\n"));
13430
13431#ifdef VBOX_WITH_GUEST_PROPS
13432 using namespace guestProp;
13433
13434 CheckComArgStrNotEmptyOrNull(aName);
13435 CheckComArgNotNull(aValue);
13436 CheckComArgNotNull(aFlags);
13437
13438 try
13439 {
13440 /*
13441 * Convert input up front.
13442 */
13443 Utf8Str utf8Name(aName);
13444 uint32_t fFlags = NILFLAG;
13445 if (aFlags)
13446 {
13447 Utf8Str utf8Flags(aFlags);
13448 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13449 AssertRCReturn(vrc, E_INVALIDARG);
13450 }
13451
13452 /*
13453 * Now grab the object lock, validate the state and do the update.
13454 */
13455 AutoCaller autoCaller(this);
13456 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13457
13458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13459
13460 switch (mData->mMachineState)
13461 {
13462 case MachineState_Paused:
13463 case MachineState_Running:
13464 case MachineState_Teleporting:
13465 case MachineState_TeleportingPausedVM:
13466 case MachineState_LiveSnapshotting:
13467 case MachineState_DeletingSnapshotOnline:
13468 case MachineState_DeletingSnapshotPaused:
13469 case MachineState_Saving:
13470 case MachineState_Stopping:
13471 break;
13472
13473 default:
13474 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13475 VBOX_E_INVALID_VM_STATE);
13476 }
13477
13478 setModified(IsModified_MachineData);
13479 mHWData.backup();
13480
13481 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13482 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13483 if (it != mHWData->mGuestProperties.end())
13484 {
13485 if (!fDelete)
13486 {
13487 it->second.strValue = aValue;
13488 it->second.mTimestamp = aTimestamp;
13489 it->second.mFlags = fFlags;
13490 }
13491 else
13492 mHWData->mGuestProperties.erase(it);
13493
13494 mData->mGuestPropertiesModified = TRUE;
13495 }
13496 else if (!fDelete)
13497 {
13498 HWData::GuestProperty prop;
13499 prop.strValue = aValue;
13500 prop.mTimestamp = aTimestamp;
13501 prop.mFlags = fFlags;
13502
13503 mHWData->mGuestProperties[utf8Name] = prop;
13504 mData->mGuestPropertiesModified = TRUE;
13505 }
13506
13507 /*
13508 * Send a callback notification if appropriate
13509 */
13510 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13511 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13512 RTSTR_MAX,
13513 utf8Name.c_str(),
13514 RTSTR_MAX, NULL)
13515 )
13516 {
13517 alock.release();
13518
13519 mParent->onGuestPropertyChange(mData->mUuid,
13520 aName,
13521 aValue,
13522 aFlags);
13523 }
13524 }
13525 catch (...)
13526 {
13527 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13528 }
13529 return S_OK;
13530#else
13531 ReturnComNotImplemented();
13532#endif
13533}
13534
13535STDMETHODIMP SessionMachine::LockMedia()
13536{
13537 AutoCaller autoCaller(this);
13538 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13539
13540 AutoMultiWriteLock2 alock(this->lockHandle(),
13541 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13542
13543 AssertReturn( mData->mMachineState == MachineState_Starting
13544 || mData->mMachineState == MachineState_Restoring
13545 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13546
13547 clearError();
13548 alock.release();
13549 return lockMedia();
13550}
13551
13552STDMETHODIMP SessionMachine::UnlockMedia()
13553{
13554 unlockMedia();
13555 return S_OK;
13556}
13557
13558STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13559 IMediumAttachment **aNewAttachment)
13560{
13561 CheckComArgNotNull(aAttachment);
13562 CheckComArgOutPointerValid(aNewAttachment);
13563
13564 AutoCaller autoCaller(this);
13565 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13566
13567 // request the host lock first, since might be calling Host methods for getting host drives;
13568 // next, protect the media tree all the while we're in here, as well as our member variables
13569 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13570 this->lockHandle(),
13571 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13572
13573 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13574
13575 Bstr ctrlName;
13576 LONG lPort;
13577 LONG lDevice;
13578 bool fTempEject;
13579 {
13580 AutoCaller autoAttachCaller(this);
13581 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13582
13583 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13584
13585 /* Need to query the details first, as the IMediumAttachment reference
13586 * might be to the original settings, which we are going to change. */
13587 ctrlName = pAttach->getControllerName();
13588 lPort = pAttach->getPort();
13589 lDevice = pAttach->getDevice();
13590 fTempEject = pAttach->getTempEject();
13591 }
13592
13593 if (!fTempEject)
13594 {
13595 /* Remember previously mounted medium. The medium before taking the
13596 * backup is not necessarily the same thing. */
13597 ComObjPtr<Medium> oldmedium;
13598 oldmedium = pAttach->getMedium();
13599
13600 setModified(IsModified_Storage);
13601 mMediaData.backup();
13602
13603 // The backup operation makes the pAttach reference point to the
13604 // old settings. Re-get the correct reference.
13605 pAttach = findAttachment(mMediaData->mAttachments,
13606 ctrlName.raw(),
13607 lPort,
13608 lDevice);
13609
13610 {
13611 AutoCaller autoAttachCaller(this);
13612 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13613
13614 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13615 if (!oldmedium.isNull())
13616 oldmedium->removeBackReference(mData->mUuid);
13617
13618 pAttach->updateMedium(NULL);
13619 pAttach->updateEjected();
13620 }
13621
13622 setModified(IsModified_Storage);
13623 }
13624 else
13625 {
13626 {
13627 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13628 pAttach->updateEjected();
13629 }
13630 }
13631
13632 pAttach.queryInterfaceTo(aNewAttachment);
13633
13634 return S_OK;
13635}
13636
13637// public methods only for internal purposes
13638/////////////////////////////////////////////////////////////////////////////
13639
13640/**
13641 * Called from the client watcher thread to check for expected or unexpected
13642 * death of the client process that has a direct session to this machine.
13643 *
13644 * On Win32 and on OS/2, this method is called only when we've got the
13645 * mutex (i.e. the client has either died or terminated normally) so it always
13646 * returns @c true (the client is terminated, the session machine is
13647 * uninitialized).
13648 *
13649 * On other platforms, the method returns @c true if the client process has
13650 * terminated normally or abnormally and the session machine was uninitialized,
13651 * and @c false if the client process is still alive.
13652 *
13653 * @note Locks this object for writing.
13654 */
13655bool SessionMachine::checkForDeath()
13656{
13657 Uninit::Reason reason;
13658 bool terminated = false;
13659
13660 /* Enclose autoCaller with a block because calling uninit() from under it
13661 * will deadlock. */
13662 {
13663 AutoCaller autoCaller(this);
13664 if (!autoCaller.isOk())
13665 {
13666 /* return true if not ready, to cause the client watcher to exclude
13667 * the corresponding session from watching */
13668 LogFlowThisFunc(("Already uninitialized!\n"));
13669 return true;
13670 }
13671
13672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13673
13674 /* Determine the reason of death: if the session state is Closing here,
13675 * everything is fine. Otherwise it means that the client did not call
13676 * OnSessionEnd() before it released the IPC semaphore. This may happen
13677 * either because the client process has abnormally terminated, or
13678 * because it simply forgot to call ISession::Close() before exiting. We
13679 * threat the latter also as an abnormal termination (see
13680 * Session::uninit() for details). */
13681 reason = mData->mSession.mState == SessionState_Unlocking ?
13682 Uninit::Normal :
13683 Uninit::Abnormal;
13684
13685#if defined(RT_OS_WINDOWS)
13686
13687 AssertMsg(mIPCSem, ("semaphore must be created"));
13688
13689 /* release the IPC mutex */
13690 ::ReleaseMutex(mIPCSem);
13691
13692 terminated = true;
13693
13694#elif defined(RT_OS_OS2)
13695
13696 AssertMsg(mIPCSem, ("semaphore must be created"));
13697
13698 /* release the IPC mutex */
13699 ::DosReleaseMutexSem(mIPCSem);
13700
13701 terminated = true;
13702
13703#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13704
13705 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13706
13707 int val = ::semctl(mIPCSem, 0, GETVAL);
13708 if (val > 0)
13709 {
13710 /* the semaphore is signaled, meaning the session is terminated */
13711 terminated = true;
13712 }
13713
13714#else
13715# error "Port me!"
13716#endif
13717
13718 } /* AutoCaller block */
13719
13720 if (terminated)
13721 uninit(reason);
13722
13723 return terminated;
13724}
13725
13726/**
13727 * @note Locks this object for reading.
13728 */
13729HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13730{
13731 LogFlowThisFunc(("\n"));
13732
13733 AutoCaller autoCaller(this);
13734 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13735
13736 ComPtr<IInternalSessionControl> directControl;
13737 {
13738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13739 directControl = mData->mSession.mDirectControl;
13740 }
13741
13742 /* ignore notifications sent after #OnSessionEnd() is called */
13743 if (!directControl)
13744 return S_OK;
13745
13746 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13747}
13748
13749/**
13750 * @note Locks this object for reading.
13751 */
13752HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13753 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
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 * instead acting like callback we ask IVirtualBox deliver corresponding event
13771 */
13772
13773 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13774 return S_OK;
13775}
13776
13777/**
13778 * @note Locks this object for reading.
13779 */
13780HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13781{
13782 LogFlowThisFunc(("\n"));
13783
13784 AutoCaller autoCaller(this);
13785 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13786
13787 ComPtr<IInternalSessionControl> directControl;
13788 {
13789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13790 directControl = mData->mSession.mDirectControl;
13791 }
13792
13793 /* ignore notifications sent after #OnSessionEnd() is called */
13794 if (!directControl)
13795 return S_OK;
13796
13797 return directControl->OnSerialPortChange(serialPort);
13798}
13799
13800/**
13801 * @note Locks this object for reading.
13802 */
13803HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13804{
13805 LogFlowThisFunc(("\n"));
13806
13807 AutoCaller autoCaller(this);
13808 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13809
13810 ComPtr<IInternalSessionControl> directControl;
13811 {
13812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13813 directControl = mData->mSession.mDirectControl;
13814 }
13815
13816 /* ignore notifications sent after #OnSessionEnd() is called */
13817 if (!directControl)
13818 return S_OK;
13819
13820 return directControl->OnParallelPortChange(parallelPort);
13821}
13822
13823/**
13824 * @note Locks this object for reading.
13825 */
13826HRESULT SessionMachine::onStorageControllerChange()
13827{
13828 LogFlowThisFunc(("\n"));
13829
13830 AutoCaller autoCaller(this);
13831 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13832
13833 ComPtr<IInternalSessionControl> directControl;
13834 {
13835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13836 directControl = mData->mSession.mDirectControl;
13837 }
13838
13839 /* ignore notifications sent after #OnSessionEnd() is called */
13840 if (!directControl)
13841 return S_OK;
13842
13843 return directControl->OnStorageControllerChange();
13844}
13845
13846/**
13847 * @note Locks this object for reading.
13848 */
13849HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13850{
13851 LogFlowThisFunc(("\n"));
13852
13853 AutoCaller autoCaller(this);
13854 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13855
13856 ComPtr<IInternalSessionControl> directControl;
13857 {
13858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13859 directControl = mData->mSession.mDirectControl;
13860 }
13861
13862 /* ignore notifications sent after #OnSessionEnd() is called */
13863 if (!directControl)
13864 return S_OK;
13865
13866 return directControl->OnMediumChange(aAttachment, aForce);
13867}
13868
13869/**
13870 * @note Locks this object for reading.
13871 */
13872HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13873{
13874 LogFlowThisFunc(("\n"));
13875
13876 AutoCaller autoCaller(this);
13877 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13878
13879 ComPtr<IInternalSessionControl> directControl;
13880 {
13881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13882 directControl = mData->mSession.mDirectControl;
13883 }
13884
13885 /* ignore notifications sent after #OnSessionEnd() is called */
13886 if (!directControl)
13887 return S_OK;
13888
13889 return directControl->OnCPUChange(aCPU, aRemove);
13890}
13891
13892HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13893{
13894 LogFlowThisFunc(("\n"));
13895
13896 AutoCaller autoCaller(this);
13897 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13898
13899 ComPtr<IInternalSessionControl> directControl;
13900 {
13901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13902 directControl = mData->mSession.mDirectControl;
13903 }
13904
13905 /* ignore notifications sent after #OnSessionEnd() is called */
13906 if (!directControl)
13907 return S_OK;
13908
13909 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13910}
13911
13912/**
13913 * @note Locks this object for reading.
13914 */
13915HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13916{
13917 LogFlowThisFunc(("\n"));
13918
13919 AutoCaller autoCaller(this);
13920 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13921
13922 ComPtr<IInternalSessionControl> directControl;
13923 {
13924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13925 directControl = mData->mSession.mDirectControl;
13926 }
13927
13928 /* ignore notifications sent after #OnSessionEnd() is called */
13929 if (!directControl)
13930 return S_OK;
13931
13932 return directControl->OnVRDEServerChange(aRestart);
13933}
13934
13935/**
13936 * @note Locks this object for reading.
13937 */
13938HRESULT SessionMachine::onVideoCaptureChange()
13939{
13940 LogFlowThisFunc(("\n"));
13941
13942 AutoCaller autoCaller(this);
13943 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13944
13945 ComPtr<IInternalSessionControl> directControl;
13946 {
13947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13948 directControl = mData->mSession.mDirectControl;
13949 }
13950
13951 /* ignore notifications sent after #OnSessionEnd() is called */
13952 if (!directControl)
13953 return S_OK;
13954
13955 return directControl->OnVideoCaptureChange();
13956}
13957
13958/**
13959 * @note Locks this object for reading.
13960 */
13961HRESULT SessionMachine::onUSBControllerChange()
13962{
13963 LogFlowThisFunc(("\n"));
13964
13965 AutoCaller autoCaller(this);
13966 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13967
13968 ComPtr<IInternalSessionControl> directControl;
13969 {
13970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13971 directControl = mData->mSession.mDirectControl;
13972 }
13973
13974 /* ignore notifications sent after #OnSessionEnd() is called */
13975 if (!directControl)
13976 return S_OK;
13977
13978 return directControl->OnUSBControllerChange();
13979}
13980
13981/**
13982 * @note Locks this object for reading.
13983 */
13984HRESULT SessionMachine::onSharedFolderChange()
13985{
13986 LogFlowThisFunc(("\n"));
13987
13988 AutoCaller autoCaller(this);
13989 AssertComRCReturnRC(autoCaller.rc());
13990
13991 ComPtr<IInternalSessionControl> directControl;
13992 {
13993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13994 directControl = mData->mSession.mDirectControl;
13995 }
13996
13997 /* ignore notifications sent after #OnSessionEnd() is called */
13998 if (!directControl)
13999 return S_OK;
14000
14001 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14002}
14003
14004/**
14005 * @note Locks this object for reading.
14006 */
14007HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14008{
14009 LogFlowThisFunc(("\n"));
14010
14011 AutoCaller autoCaller(this);
14012 AssertComRCReturnRC(autoCaller.rc());
14013
14014 ComPtr<IInternalSessionControl> directControl;
14015 {
14016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14017 directControl = mData->mSession.mDirectControl;
14018 }
14019
14020 /* ignore notifications sent after #OnSessionEnd() is called */
14021 if (!directControl)
14022 return S_OK;
14023
14024 return directControl->OnClipboardModeChange(aClipboardMode);
14025}
14026
14027/**
14028 * @note Locks this object for reading.
14029 */
14030HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14031{
14032 LogFlowThisFunc(("\n"));
14033
14034 AutoCaller autoCaller(this);
14035 AssertComRCReturnRC(autoCaller.rc());
14036
14037 ComPtr<IInternalSessionControl> directControl;
14038 {
14039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14040 directControl = mData->mSession.mDirectControl;
14041 }
14042
14043 /* ignore notifications sent after #OnSessionEnd() is called */
14044 if (!directControl)
14045 return S_OK;
14046
14047 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14048}
14049
14050/**
14051 * @note Locks this object for reading.
14052 */
14053HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14054{
14055 LogFlowThisFunc(("\n"));
14056
14057 AutoCaller autoCaller(this);
14058 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14059
14060 ComPtr<IInternalSessionControl> directControl;
14061 {
14062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14063 directControl = mData->mSession.mDirectControl;
14064 }
14065
14066 /* ignore notifications sent after #OnSessionEnd() is called */
14067 if (!directControl)
14068 return S_OK;
14069
14070 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14071}
14072
14073/**
14074 * @note Locks this object for reading.
14075 */
14076HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14077{
14078 LogFlowThisFunc(("\n"));
14079
14080 AutoCaller autoCaller(this);
14081 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14082
14083 ComPtr<IInternalSessionControl> directControl;
14084 {
14085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14086 directControl = mData->mSession.mDirectControl;
14087 }
14088
14089 /* ignore notifications sent after #OnSessionEnd() is called */
14090 if (!directControl)
14091 return S_OK;
14092
14093 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14094}
14095
14096/**
14097 * Returns @c true if this machine's USB controller reports it has a matching
14098 * filter for the given USB device and @c false otherwise.
14099 *
14100 * @note locks this object for reading.
14101 */
14102bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14103{
14104 AutoCaller autoCaller(this);
14105 /* silently return if not ready -- this method may be called after the
14106 * direct machine session has been called */
14107 if (!autoCaller.isOk())
14108 return false;
14109
14110#ifdef VBOX_WITH_USB
14111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14112
14113 switch (mData->mMachineState)
14114 {
14115 case MachineState_Starting:
14116 case MachineState_Restoring:
14117 case MachineState_TeleportingIn:
14118 case MachineState_Paused:
14119 case MachineState_Running:
14120 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14121 * elsewhere... */
14122 alock.release();
14123 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
14124 default: break;
14125 }
14126#else
14127 NOREF(aDevice);
14128 NOREF(aMaskedIfs);
14129#endif
14130 return false;
14131}
14132
14133/**
14134 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14135 */
14136HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14137 IVirtualBoxErrorInfo *aError,
14138 ULONG aMaskedIfs)
14139{
14140 LogFlowThisFunc(("\n"));
14141
14142 AutoCaller autoCaller(this);
14143
14144 /* This notification may happen after the machine object has been
14145 * uninitialized (the session was closed), so don't assert. */
14146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14147
14148 ComPtr<IInternalSessionControl> directControl;
14149 {
14150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14151 directControl = mData->mSession.mDirectControl;
14152 }
14153
14154 /* fail on notifications sent after #OnSessionEnd() is called, it is
14155 * expected by the caller */
14156 if (!directControl)
14157 return E_FAIL;
14158
14159 /* No locks should be held at this point. */
14160 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14161 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14162
14163 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14164}
14165
14166/**
14167 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14168 */
14169HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14170 IVirtualBoxErrorInfo *aError)
14171{
14172 LogFlowThisFunc(("\n"));
14173
14174 AutoCaller autoCaller(this);
14175
14176 /* This notification may happen after the machine object has been
14177 * uninitialized (the session was closed), so don't assert. */
14178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14179
14180 ComPtr<IInternalSessionControl> directControl;
14181 {
14182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14183 directControl = mData->mSession.mDirectControl;
14184 }
14185
14186 /* fail on notifications sent after #OnSessionEnd() is called, it is
14187 * expected by the caller */
14188 if (!directControl)
14189 return E_FAIL;
14190
14191 /* No locks should be held at this point. */
14192 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14193 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14194
14195 return directControl->OnUSBDeviceDetach(aId, aError);
14196}
14197
14198// protected methods
14199/////////////////////////////////////////////////////////////////////////////
14200
14201/**
14202 * Helper method to finalize saving the state.
14203 *
14204 * @note Must be called from under this object's lock.
14205 *
14206 * @param aRc S_OK if the snapshot has been taken successfully
14207 * @param aErrMsg human readable error message for failure
14208 *
14209 * @note Locks mParent + this objects for writing.
14210 */
14211HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14212{
14213 LogFlowThisFuncEnter();
14214
14215 AutoCaller autoCaller(this);
14216 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14217
14218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14219
14220 HRESULT rc = S_OK;
14221
14222 if (SUCCEEDED(aRc))
14223 {
14224 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14225
14226 /* save all VM settings */
14227 rc = saveSettings(NULL);
14228 // no need to check whether VirtualBox.xml needs saving also since
14229 // we can't have a name change pending at this point
14230 }
14231 else
14232 {
14233 // delete the saved state file (it might have been already created);
14234 // we need not check whether this is shared with a snapshot here because
14235 // we certainly created this saved state file here anew
14236 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14237 }
14238
14239 /* notify the progress object about operation completion */
14240 Assert(mConsoleTaskData.mProgress);
14241 if (SUCCEEDED(aRc))
14242 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14243 else
14244 {
14245 if (aErrMsg.length())
14246 mConsoleTaskData.mProgress->notifyComplete(aRc,
14247 COM_IIDOF(ISession),
14248 getComponentName(),
14249 aErrMsg.c_str());
14250 else
14251 mConsoleTaskData.mProgress->notifyComplete(aRc);
14252 }
14253
14254 /* clear out the temporary saved state data */
14255 mConsoleTaskData.mLastState = MachineState_Null;
14256 mConsoleTaskData.strStateFilePath.setNull();
14257 mConsoleTaskData.mProgress.setNull();
14258
14259 LogFlowThisFuncLeave();
14260 return rc;
14261}
14262
14263/**
14264 * Deletes the given file if it is no longer in use by either the current machine state
14265 * (if the machine is "saved") or any of the machine's snapshots.
14266 *
14267 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14268 * but is different for each SnapshotMachine. When calling this, the order of calling this
14269 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14270 * is therefore critical. I know, it's all rather messy.
14271 *
14272 * @param strStateFile
14273 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14274 */
14275void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14276 Snapshot *pSnapshotToIgnore)
14277{
14278 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14279 if ( (strStateFile.isNotEmpty())
14280 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14281 )
14282 // ... and it must also not be shared with other snapshots
14283 if ( !mData->mFirstSnapshot
14284 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14285 // this checks the SnapshotMachine's state file paths
14286 )
14287 RTFileDelete(strStateFile.c_str());
14288}
14289
14290/**
14291 * Locks the attached media.
14292 *
14293 * All attached hard disks are locked for writing and DVD/floppy are locked for
14294 * reading. Parents of attached hard disks (if any) are locked for reading.
14295 *
14296 * This method also performs accessibility check of all media it locks: if some
14297 * media is inaccessible, the method will return a failure and a bunch of
14298 * extended error info objects per each inaccessible medium.
14299 *
14300 * Note that this method is atomic: if it returns a success, all media are
14301 * locked as described above; on failure no media is locked at all (all
14302 * succeeded individual locks will be undone).
14303 *
14304 * The caller is responsible for doing the necessary state sanity checks.
14305 *
14306 * The locks made by this method must be undone by calling #unlockMedia() when
14307 * no more needed.
14308 */
14309HRESULT SessionMachine::lockMedia()
14310{
14311 AutoCaller autoCaller(this);
14312 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14313
14314 AutoMultiWriteLock2 alock(this->lockHandle(),
14315 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14316
14317 /* bail out if trying to lock things with already set up locking */
14318 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14319
14320 MultiResult mrc(S_OK);
14321
14322 /* Collect locking information for all medium objects attached to the VM. */
14323 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14324 it != mMediaData->mAttachments.end();
14325 ++it)
14326 {
14327 MediumAttachment* pAtt = *it;
14328 DeviceType_T devType = pAtt->getType();
14329 Medium *pMedium = pAtt->getMedium();
14330
14331 MediumLockList *pMediumLockList(new MediumLockList());
14332 // There can be attachments without a medium (floppy/dvd), and thus
14333 // it's impossible to create a medium lock list. It still makes sense
14334 // to have the empty medium lock list in the map in case a medium is
14335 // attached later.
14336 if (pMedium != NULL)
14337 {
14338 MediumType_T mediumType = pMedium->getType();
14339 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14340 || mediumType == MediumType_Shareable;
14341 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14342
14343 alock.release();
14344 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14345 !fIsReadOnlyLock /* fMediumLockWrite */,
14346 NULL,
14347 *pMediumLockList);
14348 alock.acquire();
14349 if (FAILED(mrc))
14350 {
14351 delete pMediumLockList;
14352 mData->mSession.mLockedMedia.Clear();
14353 break;
14354 }
14355 }
14356
14357 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14358 if (FAILED(rc))
14359 {
14360 mData->mSession.mLockedMedia.Clear();
14361 mrc = setError(rc,
14362 tr("Collecting locking information for all attached media failed"));
14363 break;
14364 }
14365 }
14366
14367 if (SUCCEEDED(mrc))
14368 {
14369 /* Now lock all media. If this fails, nothing is locked. */
14370 alock.release();
14371 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14372 alock.acquire();
14373 if (FAILED(rc))
14374 {
14375 mrc = setError(rc,
14376 tr("Locking of attached media failed"));
14377 }
14378 }
14379
14380 return mrc;
14381}
14382
14383/**
14384 * Undoes the locks made by by #lockMedia().
14385 */
14386void SessionMachine::unlockMedia()
14387{
14388 AutoCaller autoCaller(this);
14389 AssertComRCReturnVoid(autoCaller.rc());
14390
14391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14392
14393 /* we may be holding important error info on the current thread;
14394 * preserve it */
14395 ErrorInfoKeeper eik;
14396
14397 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14398 AssertComRC(rc);
14399}
14400
14401/**
14402 * Helper to change the machine state (reimplementation).
14403 *
14404 * @note Locks this object for writing.
14405 * @note This method must not call saveSettings or SaveSettings, otherwise
14406 * it can cause crashes in random places due to unexpectedly committing
14407 * the current settings. The caller is responsible for that. The call
14408 * to saveStateSettings is fine, because this method does not commit.
14409 */
14410HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14411{
14412 LogFlowThisFuncEnter();
14413 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14414
14415 AutoCaller autoCaller(this);
14416 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14417
14418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14419
14420 MachineState_T oldMachineState = mData->mMachineState;
14421
14422 AssertMsgReturn(oldMachineState != aMachineState,
14423 ("oldMachineState=%s, aMachineState=%s\n",
14424 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14425 E_FAIL);
14426
14427 HRESULT rc = S_OK;
14428
14429 int stsFlags = 0;
14430 bool deleteSavedState = false;
14431
14432 /* detect some state transitions */
14433
14434 if ( ( oldMachineState == MachineState_Saved
14435 && aMachineState == MachineState_Restoring)
14436 || ( ( oldMachineState == MachineState_PoweredOff
14437 || oldMachineState == MachineState_Teleported
14438 || oldMachineState == MachineState_Aborted
14439 )
14440 && ( aMachineState == MachineState_TeleportingIn
14441 || aMachineState == MachineState_Starting
14442 )
14443 )
14444 )
14445 {
14446 /* The EMT thread is about to start */
14447
14448 /* Nothing to do here for now... */
14449
14450 /// @todo NEWMEDIA don't let mDVDDrive and other children
14451 /// change anything when in the Starting/Restoring state
14452 }
14453 else if ( ( oldMachineState == MachineState_Running
14454 || oldMachineState == MachineState_Paused
14455 || oldMachineState == MachineState_Teleporting
14456 || oldMachineState == MachineState_LiveSnapshotting
14457 || oldMachineState == MachineState_Stuck
14458 || oldMachineState == MachineState_Starting
14459 || oldMachineState == MachineState_Stopping
14460 || oldMachineState == MachineState_Saving
14461 || oldMachineState == MachineState_Restoring
14462 || oldMachineState == MachineState_TeleportingPausedVM
14463 || oldMachineState == MachineState_TeleportingIn
14464 )
14465 && ( aMachineState == MachineState_PoweredOff
14466 || aMachineState == MachineState_Saved
14467 || aMachineState == MachineState_Teleported
14468 || aMachineState == MachineState_Aborted
14469 )
14470 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14471 * snapshot */
14472 && ( mConsoleTaskData.mSnapshot.isNull()
14473 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14474 )
14475 )
14476 {
14477 /* The EMT thread has just stopped, unlock attached media. Note that as
14478 * opposed to locking that is done from Console, we do unlocking here
14479 * because the VM process may have aborted before having a chance to
14480 * properly unlock all media it locked. */
14481
14482 unlockMedia();
14483 }
14484
14485 if (oldMachineState == MachineState_Restoring)
14486 {
14487 if (aMachineState != MachineState_Saved)
14488 {
14489 /*
14490 * delete the saved state file once the machine has finished
14491 * restoring from it (note that Console sets the state from
14492 * Restoring to Saved if the VM couldn't restore successfully,
14493 * to give the user an ability to fix an error and retry --
14494 * we keep the saved state file in this case)
14495 */
14496 deleteSavedState = true;
14497 }
14498 }
14499 else if ( oldMachineState == MachineState_Saved
14500 && ( aMachineState == MachineState_PoweredOff
14501 || aMachineState == MachineState_Aborted
14502 || aMachineState == MachineState_Teleported
14503 )
14504 )
14505 {
14506 /*
14507 * delete the saved state after Console::ForgetSavedState() is called
14508 * or if the VM process (owning a direct VM session) crashed while the
14509 * VM was Saved
14510 */
14511
14512 /// @todo (dmik)
14513 // Not sure that deleting the saved state file just because of the
14514 // client death before it attempted to restore the VM is a good
14515 // thing. But when it crashes we need to go to the Aborted state
14516 // which cannot have the saved state file associated... The only
14517 // way to fix this is to make the Aborted condition not a VM state
14518 // but a bool flag: i.e., when a crash occurs, set it to true and
14519 // change the state to PoweredOff or Saved depending on the
14520 // saved state presence.
14521
14522 deleteSavedState = true;
14523 mData->mCurrentStateModified = TRUE;
14524 stsFlags |= SaveSTS_CurStateModified;
14525 }
14526
14527 if ( aMachineState == MachineState_Starting
14528 || aMachineState == MachineState_Restoring
14529 || aMachineState == MachineState_TeleportingIn
14530 )
14531 {
14532 /* set the current state modified flag to indicate that the current
14533 * state is no more identical to the state in the
14534 * current snapshot */
14535 if (!mData->mCurrentSnapshot.isNull())
14536 {
14537 mData->mCurrentStateModified = TRUE;
14538 stsFlags |= SaveSTS_CurStateModified;
14539 }
14540 }
14541
14542 if (deleteSavedState)
14543 {
14544 if (mRemoveSavedState)
14545 {
14546 Assert(!mSSData->strStateFilePath.isEmpty());
14547
14548 // it is safe to delete the saved state file if ...
14549 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14550 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14551 // ... none of the snapshots share the saved state file
14552 )
14553 RTFileDelete(mSSData->strStateFilePath.c_str());
14554 }
14555
14556 mSSData->strStateFilePath.setNull();
14557 stsFlags |= SaveSTS_StateFilePath;
14558 }
14559
14560 /* redirect to the underlying peer machine */
14561 mPeer->setMachineState(aMachineState);
14562
14563 if ( aMachineState == MachineState_PoweredOff
14564 || aMachineState == MachineState_Teleported
14565 || aMachineState == MachineState_Aborted
14566 || aMachineState == MachineState_Saved)
14567 {
14568 /* the machine has stopped execution
14569 * (or the saved state file was adopted) */
14570 stsFlags |= SaveSTS_StateTimeStamp;
14571 }
14572
14573 if ( ( oldMachineState == MachineState_PoweredOff
14574 || oldMachineState == MachineState_Aborted
14575 || oldMachineState == MachineState_Teleported
14576 )
14577 && aMachineState == MachineState_Saved)
14578 {
14579 /* the saved state file was adopted */
14580 Assert(!mSSData->strStateFilePath.isEmpty());
14581 stsFlags |= SaveSTS_StateFilePath;
14582 }
14583
14584#ifdef VBOX_WITH_GUEST_PROPS
14585 if ( aMachineState == MachineState_PoweredOff
14586 || aMachineState == MachineState_Aborted
14587 || aMachineState == MachineState_Teleported)
14588 {
14589 /* Make sure any transient guest properties get removed from the
14590 * property store on shutdown. */
14591
14592 HWData::GuestPropertyMap::const_iterator it;
14593 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14594 if (!fNeedsSaving)
14595 for (it = mHWData->mGuestProperties.begin();
14596 it != mHWData->mGuestProperties.end(); ++it)
14597 if ( (it->second.mFlags & guestProp::TRANSIENT)
14598 || (it->second.mFlags & guestProp::TRANSRESET))
14599 {
14600 fNeedsSaving = true;
14601 break;
14602 }
14603 if (fNeedsSaving)
14604 {
14605 mData->mCurrentStateModified = TRUE;
14606 stsFlags |= SaveSTS_CurStateModified;
14607 }
14608 }
14609#endif
14610
14611 rc = saveStateSettings(stsFlags);
14612
14613 if ( ( oldMachineState != MachineState_PoweredOff
14614 && oldMachineState != MachineState_Aborted
14615 && oldMachineState != MachineState_Teleported
14616 )
14617 && ( aMachineState == MachineState_PoweredOff
14618 || aMachineState == MachineState_Aborted
14619 || aMachineState == MachineState_Teleported
14620 )
14621 )
14622 {
14623 /* we've been shut down for any reason */
14624 /* no special action so far */
14625 }
14626
14627 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14628 LogFlowThisFuncLeave();
14629 return rc;
14630}
14631
14632/**
14633 * Sends the current machine state value to the VM process.
14634 *
14635 * @note Locks this object for reading, then calls a client process.
14636 */
14637HRESULT SessionMachine::updateMachineStateOnClient()
14638{
14639 AutoCaller autoCaller(this);
14640 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14641
14642 ComPtr<IInternalSessionControl> directControl;
14643 {
14644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14645 AssertReturn(!!mData, E_FAIL);
14646 directControl = mData->mSession.mDirectControl;
14647
14648 /* directControl may be already set to NULL here in #OnSessionEnd()
14649 * called too early by the direct session process while there is still
14650 * some operation (like deleting the snapshot) in progress. The client
14651 * process in this case is waiting inside Session::close() for the
14652 * "end session" process object to complete, while #uninit() called by
14653 * #checkForDeath() on the Watcher thread is waiting for the pending
14654 * operation to complete. For now, we accept this inconsistent behavior
14655 * and simply do nothing here. */
14656
14657 if (mData->mSession.mState == SessionState_Unlocking)
14658 return S_OK;
14659
14660 AssertReturn(!directControl.isNull(), E_FAIL);
14661 }
14662
14663 return directControl->UpdateMachineState(mData->mMachineState);
14664}
Note: See TracBrowser for help on using the repository browser.

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