VirtualBox

source: vbox/trunk/src/VBox/Main/MachineImpl.cpp@ 29455

Last change on this file since 29455 was 29422, checked in by vboxsync, 15 years ago

Main/OVF: fix IDE channel confusion introduced with earlier OVF two-channel IDE fix (trunk regression)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 364.0 KB
Line 
1/* $Id: MachineImpl.cpp 29422 2010-05-12 14:08:52Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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 "MediumAttachmentImpl.h"
39#include "MediumImpl.h"
40#include "MediumLock.h"
41#include "USBControllerImpl.h"
42#include "HostImpl.h"
43#include "SharedFolderImpl.h"
44#include "GuestOSTypeImpl.h"
45#include "VirtualBoxErrorInfoImpl.h"
46#include "GuestImpl.h"
47#include "StorageControllerImpl.h"
48
49#ifdef VBOX_WITH_USB
50# include "USBProxyService.h"
51#endif
52
53#include "AutoCaller.h"
54#include "Performance.h"
55
56#include <iprt/asm.h>
57#include <iprt/path.h>
58#include <iprt/dir.h>
59#include <iprt/env.h>
60#include <iprt/lockvalidator.h>
61#include <iprt/process.h>
62#include <iprt/cpp/utils.h>
63#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
64#include <iprt/string.h>
65
66#include <VBox/com/array.h>
67
68#include <VBox/err.h>
69#include <VBox/param.h>
70#include <VBox/settings.h>
71#include <VBox/ssm.h>
72
73#ifdef VBOX_WITH_GUEST_PROPS
74# include <VBox/HostServices/GuestPropertySvc.h>
75# include <VBox/com/array.h>
76#endif
77
78#include <algorithm>
79
80#include <typeinfo>
81
82#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
83# define HOSTSUFF_EXE ".exe"
84#else /* !RT_OS_WINDOWS */
85# define HOSTSUFF_EXE ""
86#endif /* !RT_OS_WINDOWS */
87
88// defines / prototypes
89/////////////////////////////////////////////////////////////////////////////
90
91/////////////////////////////////////////////////////////////////////////////
92// Machine::Data structure
93/////////////////////////////////////////////////////////////////////////////
94
95Machine::Data::Data()
96{
97 mRegistered = FALSE;
98 pMachineConfigFile = NULL;
99 flModifications = 0;
100 mAccessible = FALSE;
101 /* mUuid is initialized in Machine::init() */
102
103 mMachineState = MachineState_PoweredOff;
104 RTTimeNow(&mLastStateChange);
105
106 mMachineStateDeps = 0;
107 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
108 mMachineStateChangePending = 0;
109
110 mCurrentStateModified = TRUE;
111 mGuestPropertiesModified = FALSE;
112
113 mSession.mPid = NIL_RTPROCESS;
114 mSession.mState = SessionState_Closed;
115}
116
117Machine::Data::~Data()
118{
119 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
120 {
121 RTSemEventMultiDestroy(mMachineStateDepsSem);
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 }
124 if (pMachineConfigFile)
125 {
126 delete pMachineConfigFile;
127 pMachineConfigFile = NULL;
128 }
129}
130
131/////////////////////////////////////////////////////////////////////////////
132// Machine::UserData structure
133/////////////////////////////////////////////////////////////////////////////
134
135Machine::UserData::UserData()
136{
137 /* default values for a newly created machine */
138
139 mNameSync = TRUE;
140 mTeleporterEnabled = FALSE;
141 mTeleporterPort = 0;
142 mRTCUseUTC = FALSE;
143
144 /* mName, mOSTypeId, mSnapshotFolder, mSnapshotFolderFull are initialized in
145 * Machine::init() */
146}
147
148Machine::UserData::~UserData()
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 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mHWVirtExEnabled = true;
169 mHWVirtExNestedPagingEnabled = true;
170#if HC_ARCH_BITS == 64
171 /* Default value decision pending. */
172 mHWVirtExLargePagesEnabled = false;
173#else
174 /* Not supported on 32 bits hosts. */
175 mHWVirtExLargePagesEnabled = false;
176#endif
177 mHWVirtExVPIDEnabled = true;
178#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
179 mHWVirtExExclusive = false;
180#else
181 mHWVirtExExclusive = true;
182#endif
183#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mSyntheticCpu = false;
189 mHpetEnabled = false;
190
191 /* default boot order: floppy - DVD - HDD */
192 mBootOrder[0] = DeviceType_Floppy;
193 mBootOrder[1] = DeviceType_DVD;
194 mBootOrder[2] = DeviceType_HardDisk;
195 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
196 mBootOrder[i] = DeviceType_Null;
197
198 mClipboardMode = ClipboardMode_Bidirectional;
199 mGuestPropertyNotificationPatterns = "";
200
201 mFirmwareType = FirmwareType_BIOS;
202 mKeyboardHidType = KeyboardHidType_PS2Keyboard;
203 mPointingHidType = PointingHidType_PS2Mouse;
204
205 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
206 mCPUAttached[i] = false;
207
208 mIoCacheEnabled = true;
209 mIoCacheSize = 5; /* 5MB */
210 mIoBandwidthMax = 0; /* Unlimited */
211}
212
213Machine::HWData::~HWData()
214{
215}
216
217/////////////////////////////////////////////////////////////////////////////
218// Machine::HDData structure
219/////////////////////////////////////////////////////////////////////////////
220
221Machine::MediaData::MediaData()
222{
223}
224
225Machine::MediaData::~MediaData()
226{
227}
228
229/////////////////////////////////////////////////////////////////////////////
230// Machine class
231/////////////////////////////////////////////////////////////////////////////
232
233// constructor / destructor
234/////////////////////////////////////////////////////////////////////////////
235
236Machine::Machine()
237 : mGuestHAL(NULL),
238 mPeer(NULL),
239 mParent(NULL)
240{}
241
242Machine::~Machine()
243{}
244
245HRESULT Machine::FinalConstruct()
246{
247 LogFlowThisFunc(("\n"));
248 return S_OK;
249}
250
251void Machine::FinalRelease()
252{
253 LogFlowThisFunc(("\n"));
254 uninit();
255}
256
257/**
258 * Initializes a new machine instance; this init() variant creates a new, empty machine.
259 * This gets called from VirtualBox::CreateMachine() or VirtualBox::CreateLegacyMachine().
260 *
261 * @param aParent Associated parent object
262 * @param strConfigFile Local file system path to the VM settings file (can
263 * be relative to the VirtualBox config directory).
264 * @param strName name for the machine
265 * @param aId UUID for the new machine.
266 * @param aOsType Optional OS Type of this machine.
267 * @param aOverride |TRUE| to override VM config file existence checks.
268 * |FALSE| refuses to overwrite existing VM configs.
269 * @param aNameSync |TRUE| to automatically sync settings dir and file
270 * name with the machine name. |FALSE| is used for legacy
271 * machines where the file name is specified by the
272 * user and should never change.
273 *
274 * @return Success indicator. if not S_OK, the machine object is invalid
275 */
276HRESULT Machine::init(VirtualBox *aParent,
277 const Utf8Str &strConfigFile,
278 const Utf8Str &strName,
279 const Guid &aId,
280 GuestOSType *aOsType /* = NULL */,
281 BOOL aOverride /* = FALSE */,
282 BOOL aNameSync /* = TRUE */)
283{
284 LogFlowThisFuncEnter();
285 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.raw()));
286
287 /* Enclose the state transition NotReady->InInit->Ready */
288 AutoInitSpan autoInitSpan(this);
289 AssertReturn(autoInitSpan.isOk(), E_FAIL);
290
291 HRESULT rc = initImpl(aParent, strConfigFile);
292 if (FAILED(rc)) return rc;
293
294 rc = tryCreateMachineConfigFile(aOverride);
295 if (FAILED(rc)) return rc;
296
297 if (SUCCEEDED(rc))
298 {
299 // create an empty machine config
300 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
301
302 rc = initDataAndChildObjects();
303 }
304
305 if (SUCCEEDED(rc))
306 {
307 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
308 mData->mAccessible = TRUE;
309
310 unconst(mData->mUuid) = aId;
311
312 mUserData->mName = strName;
313 mUserData->mNameSync = aNameSync;
314
315 /* initialize the default snapshots folder
316 * (note: depends on the name value set above!) */
317 rc = COMSETTER(SnapshotFolder)(NULL);
318 AssertComRC(rc);
319
320 if (aOsType)
321 {
322 /* Store OS type */
323 mUserData->mOSTypeId = aOsType->id();
324
325 /* Apply BIOS defaults */
326 mBIOSSettings->applyDefaults(aOsType);
327
328 /* Apply network adapters defaults */
329 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); ++slot)
330 mNetworkAdapters[slot]->applyDefaults(aOsType);
331
332 /* Apply serial port defaults */
333 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
334 mSerialPorts[slot]->applyDefaults(aOsType);
335 }
336
337 /* commit all changes made during the initialization */
338 commit();
339 }
340
341 /* Confirm a successful initialization when it's the case */
342 if (SUCCEEDED(rc))
343 {
344 if (mData->mAccessible)
345 autoInitSpan.setSucceeded();
346 else
347 autoInitSpan.setLimited();
348 }
349
350 LogFlowThisFunc(("mName='%ls', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
351 !!mUserData ? mUserData->mName.raw() : NULL,
352 mData->mRegistered,
353 mData->mAccessible,
354 rc));
355
356 LogFlowThisFuncLeave();
357
358 return rc;
359}
360
361/**
362 * Initializes a new instance with data from machine XML (formerly Init_Registered).
363 * Gets called in two modes:
364 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
365 * UUID is specified and we mark the machine as "registered";
366 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
367 * and the machine remains unregistered until RegisterMachine() is called.
368 *
369 * @param aParent Associated parent object
370 * @param aConfigFile Local file system path to the VM settings file (can
371 * be relative to the VirtualBox config directory).
372 * @param aId UUID of the machine or NULL (see above).
373 *
374 * @return Success indicator. if not S_OK, the machine object is invalid
375 */
376HRESULT Machine::init(VirtualBox *aParent,
377 const Utf8Str &strConfigFile,
378 const Guid *aId)
379{
380 LogFlowThisFuncEnter();
381 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.raw()));
382
383 /* Enclose the state transition NotReady->InInit->Ready */
384 AutoInitSpan autoInitSpan(this);
385 AssertReturn(autoInitSpan.isOk(), E_FAIL);
386
387 HRESULT rc = initImpl(aParent, strConfigFile);
388 if (FAILED(rc)) return rc;
389
390 if (aId)
391 {
392 // loading a registered VM:
393 unconst(mData->mUuid) = *aId;
394 mData->mRegistered = TRUE;
395 // now load the settings from XML:
396 rc = registeredInit();
397 // this calls initDataAndChildObjects() and loadSettings()
398 }
399 else
400 {
401 // opening an unregistered VM (VirtualBox::OpenMachine()):
402 rc = initDataAndChildObjects();
403
404 if (SUCCEEDED(rc))
405 {
406 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
407 mData->mAccessible = TRUE;
408
409 try
410 {
411 // load and parse machine XML; this will throw on XML or logic errors
412 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
413
414 // use UUID from machine config
415 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
416
417 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile);
418 if (FAILED(rc)) throw rc;
419
420 commit();
421 }
422 catch (HRESULT err)
423 {
424 /* we assume that error info is set by the thrower */
425 rc = err;
426 }
427 catch (...)
428 {
429 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
430 }
431 }
432 }
433
434 /* Confirm a successful initialization when it's the case */
435 if (SUCCEEDED(rc))
436 {
437 if (mData->mAccessible)
438 autoInitSpan.setSucceeded();
439 else
440 autoInitSpan.setLimited();
441 }
442
443 LogFlowThisFunc(("mName='%ls', mRegistered=%RTbool, mAccessible=%RTbool "
444 "rc=%08X\n",
445 !!mUserData ? mUserData->mName.raw() : NULL,
446 mData->mRegistered, mData->mAccessible, rc));
447
448 LogFlowThisFuncLeave();
449
450 return rc;
451}
452
453/**
454 * Initializes a new instance from a machine config that is already in memory
455 * (import OVF import case). Since we are importing, the UUID in the machine
456 * config is ignored and we always generate a fresh one.
457 *
458 * @param strName Name for the new machine; this overrides what is specified in config and is used
459 * for the settings file as well.
460 * @param config Machine configuration loaded and parsed from XML.
461 *
462 * @return Success indicator. if not S_OK, the machine object is invalid
463 */
464HRESULT Machine::init(VirtualBox *aParent,
465 const Utf8Str &strName,
466 const settings::MachineConfigFile &config)
467{
468 LogFlowThisFuncEnter();
469
470 /* Enclose the state transition NotReady->InInit->Ready */
471 AutoInitSpan autoInitSpan(this);
472 AssertReturn(autoInitSpan.isOk(), E_FAIL);
473
474 Utf8Str strConfigFile(aParent->getDefaultMachineFolder());
475 strConfigFile.append(Utf8StrFmt("%c%s%c%s.xml",
476 RTPATH_DELIMITER,
477 strName.c_str(),
478 RTPATH_DELIMITER,
479 strName.c_str()));
480
481 HRESULT rc = initImpl(aParent, strConfigFile);
482 if (FAILED(rc)) return rc;
483
484 rc = tryCreateMachineConfigFile(FALSE /* aOverride */);
485 if (FAILED(rc)) return rc;
486
487 rc = initDataAndChildObjects();
488
489 if (SUCCEEDED(rc))
490 {
491 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
492 mData->mAccessible = TRUE;
493
494 // create empty machine config for instance data
495 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
496
497 // generate fresh UUID, ignore machine config
498 unconst(mData->mUuid).create();
499
500 rc = loadMachineDataFromSettings(config);
501
502 // override VM name as well, it may be different
503 mUserData->mName = strName;
504
505 /* commit all changes made during the initialization */
506 if (SUCCEEDED(rc))
507 commit();
508 }
509
510 /* Confirm a successful initialization when it's the case */
511 if (SUCCEEDED(rc))
512 {
513 if (mData->mAccessible)
514 autoInitSpan.setSucceeded();
515 else
516 autoInitSpan.setLimited();
517 }
518
519 LogFlowThisFunc(("mName='%ls', mRegistered=%RTbool, mAccessible=%RTbool "
520 "rc=%08X\n",
521 !!mUserData ? mUserData->mName.raw() : NULL,
522 mData->mRegistered, mData->mAccessible, rc));
523
524 LogFlowThisFuncLeave();
525
526 return rc;
527}
528
529/**
530 * Shared code between the various init() implementations.
531 * @param aParent
532 * @return
533 */
534HRESULT Machine::initImpl(VirtualBox *aParent,
535 const Utf8Str &strConfigFile)
536{
537 LogFlowThisFuncEnter();
538
539 AssertReturn(aParent, E_INVALIDARG);
540 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
541
542 HRESULT rc = S_OK;
543
544 /* share the parent weakly */
545 unconst(mParent) = aParent;
546
547 /* allocate the essential machine data structure (the rest will be
548 * allocated later by initDataAndChildObjects() */
549 mData.allocate();
550
551 /* memorize the config file name (as provided) */
552 mData->m_strConfigFile = strConfigFile;
553
554 /* get the full file name */
555 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
556 if (RT_FAILURE(vrc1))
557 return setError(VBOX_E_FILE_ERROR,
558 tr("Invalid machine settings file name '%s' (%Rrc)"),
559 strConfigFile.raw(),
560 vrc1);
561
562 LogFlowThisFuncLeave();
563
564 return rc;
565}
566
567/**
568 * Tries to create a machine settings file in the path stored in the machine
569 * instance data. Used when a new machine is created to fail gracefully if
570 * the settings file could not be written (e.g. because machine dir is read-only).
571 * @return
572 */
573HRESULT Machine::tryCreateMachineConfigFile(BOOL aOverride)
574{
575 HRESULT rc = S_OK;
576
577 // when we create a new machine, we must be able to create the settings file
578 RTFILE f = NIL_RTFILE;
579 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
580 if ( RT_SUCCESS(vrc)
581 || vrc == VERR_SHARING_VIOLATION
582 )
583 {
584 if (RT_SUCCESS(vrc))
585 RTFileClose(f);
586 if (!aOverride)
587 rc = setError(VBOX_E_FILE_ERROR,
588 tr("Machine settings file '%s' already exists"),
589 mData->m_strConfigFileFull.raw());
590 else
591 {
592 /* try to delete the config file, as otherwise the creation
593 * of a new settings file will fail. */
594 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
595 if (RT_FAILURE(vrc2))
596 rc = setError(VBOX_E_FILE_ERROR,
597 tr("Could not delete the existing settings file '%s' (%Rrc)"),
598 mData->m_strConfigFileFull.raw(), vrc2);
599 }
600 }
601 else if ( vrc != VERR_FILE_NOT_FOUND
602 && vrc != VERR_PATH_NOT_FOUND
603 )
604 rc = setError(VBOX_E_FILE_ERROR,
605 tr("Invalid machine settings file name '%s' (%Rrc)"),
606 mData->m_strConfigFileFull.raw(),
607 vrc);
608 return rc;
609}
610
611/**
612 * Initializes the registered machine by loading the settings file.
613 * This method is separated from #init() in order to make it possible to
614 * retry the operation after VirtualBox startup instead of refusing to
615 * startup the whole VirtualBox server in case if the settings file of some
616 * registered VM is invalid or inaccessible.
617 *
618 * @note Must be always called from this object's write lock
619 * (unless called from #init() that doesn't need any locking).
620 * @note Locks the mUSBController method for writing.
621 * @note Subclasses must not call this method.
622 */
623HRESULT Machine::registeredInit()
624{
625 AssertReturn(getClassID() == clsidMachine, E_FAIL);
626 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
627 AssertReturn(!mData->mAccessible, E_FAIL);
628
629 HRESULT rc = initDataAndChildObjects();
630
631 if (SUCCEEDED(rc))
632 {
633 /* Temporarily reset the registered flag in order to let setters
634 * potentially called from loadSettings() succeed (isMutable() used in
635 * all setters will return FALSE for a Machine instance if mRegistered
636 * is TRUE). */
637 mData->mRegistered = FALSE;
638
639 try
640 {
641 // load and parse machine XML; this will throw on XML or logic errors
642 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
643
644 if (mData->mUuid != mData->pMachineConfigFile->uuid)
645 throw setError(E_FAIL,
646 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
647 mData->pMachineConfigFile->uuid.raw(),
648 mData->m_strConfigFileFull.raw(),
649 mData->mUuid.toString().raw(),
650 mParent->settingsFilePath().raw());
651
652 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile);
653 if (FAILED(rc)) throw rc;
654 }
655 catch (HRESULT err)
656 {
657 /* we assume that error info is set by the thrower */
658 rc = err;
659 }
660 catch (...)
661 {
662 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
663 }
664
665 /* Restore the registered flag (even on failure) */
666 mData->mRegistered = TRUE;
667 }
668
669 if (SUCCEEDED(rc))
670 {
671 /* Set mAccessible to TRUE only if we successfully locked and loaded
672 * the settings file */
673 mData->mAccessible = TRUE;
674
675 /* commit all changes made during loading the settings file */
676 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
677 }
678 else
679 {
680 /* If the machine is registered, then, instead of returning a
681 * failure, we mark it as inaccessible and set the result to
682 * success to give it a try later */
683
684 /* fetch the current error info */
685 mData->mAccessError = com::ErrorInfo();
686 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
687 mData->mUuid.raw(),
688 mData->mAccessError.getText().raw()));
689
690 /* rollback all changes */
691 rollback(false /* aNotify */);
692
693 /* uninitialize the common part to make sure all data is reset to
694 * default (null) values */
695 uninitDataAndChildObjects();
696
697 rc = S_OK;
698 }
699
700 return rc;
701}
702
703/**
704 * Uninitializes the instance.
705 * Called either from FinalRelease() or by the parent when it gets destroyed.
706 *
707 * @note The caller of this method must make sure that this object
708 * a) doesn't have active callers on the current thread and b) is not locked
709 * by the current thread; otherwise uninit() will hang either a) due to
710 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
711 * a dead-lock caused by this thread waiting for all callers on the other
712 * threads are done but preventing them from doing so by holding a lock.
713 */
714void Machine::uninit()
715{
716 LogFlowThisFuncEnter();
717
718 Assert(!isWriteLockOnCurrentThread());
719
720 /* Enclose the state transition Ready->InUninit->NotReady */
721 AutoUninitSpan autoUninitSpan(this);
722 if (autoUninitSpan.uninitDone())
723 return;
724
725 Assert(getClassID() == clsidMachine);
726 Assert(!!mData);
727
728 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
729 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
730
731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
732
733 if (!mData->mSession.mMachine.isNull())
734 {
735 /* Theoretically, this can only happen if the VirtualBox server has been
736 * terminated while there were clients running that owned open direct
737 * sessions. Since in this case we are definitely called by
738 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
739 * won't happen on the client watcher thread (because it does
740 * VirtualBox::addCaller() for the duration of the
741 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
742 * cannot happen until the VirtualBox caller is released). This is
743 * important, because SessionMachine::uninit() cannot correctly operate
744 * after we return from this method (it expects the Machine instance is
745 * still valid). We'll call it ourselves below.
746 */
747 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
748 (SessionMachine*)mData->mSession.mMachine));
749
750 if (Global::IsOnlineOrTransient(mData->mMachineState))
751 {
752 LogWarningThisFunc(("Setting state to Aborted!\n"));
753 /* set machine state using SessionMachine reimplementation */
754 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
755 }
756
757 /*
758 * Uninitialize SessionMachine using public uninit() to indicate
759 * an unexpected uninitialization.
760 */
761 mData->mSession.mMachine->uninit();
762 /* SessionMachine::uninit() must set mSession.mMachine to null */
763 Assert(mData->mSession.mMachine.isNull());
764 }
765
766 /* the lock is no more necessary (SessionMachine is uninitialized) */
767 alock.leave();
768
769 // has machine been modified?
770 if (mData->flModifications)
771 {
772 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
773 rollback(false /* aNotify */);
774 }
775
776 if (mData->mAccessible)
777 uninitDataAndChildObjects();
778
779 /* free the essential data structure last */
780 mData.free();
781
782 LogFlowThisFuncLeave();
783}
784
785// IMachine properties
786/////////////////////////////////////////////////////////////////////////////
787
788STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
789{
790 CheckComArgOutPointerValid(aParent);
791
792 AutoLimitedCaller autoCaller(this);
793 if (FAILED(autoCaller.rc())) return autoCaller.rc();
794
795 /* mParent is constant during life time, no need to lock */
796 ComObjPtr<VirtualBox> pVirtualBox(mParent);
797 pVirtualBox.queryInterfaceTo(aParent);
798
799 return S_OK;
800}
801
802STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
803{
804 CheckComArgOutPointerValid(aAccessible);
805
806 AutoLimitedCaller autoCaller(this);
807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
808
809 LogFlowThisFunc(("ENTER\n"));
810
811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
812
813 HRESULT rc = S_OK;
814
815 if (!mData->mAccessible)
816 {
817 /* try to initialize the VM once more if not accessible */
818
819 AutoReinitSpan autoReinitSpan(this);
820 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
821
822#ifdef DEBUG
823 LogFlowThisFunc(("Dumping media backreferences\n"));
824 mParent->dumpAllBackRefs();
825#endif
826
827 if (mData->pMachineConfigFile)
828 {
829 // reset the XML file to force loadSettings() (called from registeredInit())
830 // to parse it again; the file might have changed
831 delete mData->pMachineConfigFile;
832 mData->pMachineConfigFile = NULL;
833 }
834
835 rc = registeredInit();
836
837 if (SUCCEEDED(rc) && mData->mAccessible)
838 {
839 autoReinitSpan.setSucceeded();
840
841 /* make sure interesting parties will notice the accessibility
842 * state change */
843 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
844 mParent->onMachineDataChange(mData->mUuid);
845 }
846 }
847
848 if (SUCCEEDED(rc))
849 *aAccessible = mData->mAccessible;
850
851 LogFlowThisFuncLeave();
852
853 return rc;
854}
855
856STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
857{
858 CheckComArgOutPointerValid(aAccessError);
859
860 AutoLimitedCaller autoCaller(this);
861 if (FAILED(autoCaller.rc())) return autoCaller.rc();
862
863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
864
865 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
866 {
867 /* return shortly */
868 aAccessError = NULL;
869 return S_OK;
870 }
871
872 HRESULT rc = S_OK;
873
874 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
875 rc = errorInfo.createObject();
876 if (SUCCEEDED(rc))
877 {
878 errorInfo->init(mData->mAccessError.getResultCode(),
879 mData->mAccessError.getInterfaceID(),
880 mData->mAccessError.getComponent(),
881 mData->mAccessError.getText());
882 rc = errorInfo.queryInterfaceTo(aAccessError);
883 }
884
885 return rc;
886}
887
888STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
889{
890 CheckComArgOutPointerValid(aName);
891
892 AutoCaller autoCaller(this);
893 if (FAILED(autoCaller.rc())) return autoCaller.rc();
894
895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
896
897 mUserData->mName.cloneTo(aName);
898
899 return S_OK;
900}
901
902STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
903{
904 CheckComArgStrNotEmptyOrNull(aName);
905
906 AutoCaller autoCaller(this);
907 if (FAILED(autoCaller.rc())) return autoCaller.rc();
908
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = checkStateDependency(MutableStateDep);
912 if (FAILED(rc)) return rc;
913
914 setModified(IsModified_MachineData);
915 mUserData.backup();
916 mUserData->mName = aName;
917
918 return S_OK;
919}
920
921STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
922{
923 CheckComArgOutPointerValid(aDescription);
924
925 AutoCaller autoCaller(this);
926 if (FAILED(autoCaller.rc())) return autoCaller.rc();
927
928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
929
930 mUserData->mDescription.cloneTo(aDescription);
931
932 return S_OK;
933}
934
935STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
936{
937 AutoCaller autoCaller(this);
938 if (FAILED(autoCaller.rc())) return autoCaller.rc();
939
940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
941
942 HRESULT rc = checkStateDependency(MutableStateDep);
943 if (FAILED(rc)) return rc;
944
945 setModified(IsModified_MachineData);
946 mUserData.backup();
947 mUserData->mDescription = aDescription;
948
949 return S_OK;
950}
951
952STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
953{
954 CheckComArgOutPointerValid(aId);
955
956 AutoLimitedCaller autoCaller(this);
957 if (FAILED(autoCaller.rc())) return autoCaller.rc();
958
959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
960
961 mData->mUuid.toUtf16().cloneTo(aId);
962
963 return S_OK;
964}
965
966STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
967{
968 CheckComArgOutPointerValid(aOSTypeId);
969
970 AutoCaller autoCaller(this);
971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
972
973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
974
975 mUserData->mOSTypeId.cloneTo(aOSTypeId);
976
977 return S_OK;
978}
979
980STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
981{
982 CheckComArgStrNotEmptyOrNull(aOSTypeId);
983
984 AutoCaller autoCaller(this);
985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
986
987 /* look up the object by Id to check it is valid */
988 ComPtr<IGuestOSType> guestOSType;
989 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
990 if (FAILED(rc)) return rc;
991
992 /* when setting, always use the "etalon" value for consistency -- lookup
993 * by ID is case-insensitive and the input value may have different case */
994 Bstr osTypeId;
995 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
996 if (FAILED(rc)) return rc;
997
998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 rc = checkStateDependency(MutableStateDep);
1001 if (FAILED(rc)) return rc;
1002
1003 setModified(IsModified_MachineData);
1004 mUserData.backup();
1005 mUserData->mOSTypeId = osTypeId;
1006
1007 return S_OK;
1008}
1009
1010
1011STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1012{
1013 CheckComArgOutPointerValid(aFirmwareType);
1014
1015 AutoCaller autoCaller(this);
1016 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1017
1018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 *aFirmwareType = mHWData->mFirmwareType;
1021
1022 return S_OK;
1023}
1024
1025STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1026{
1027 AutoCaller autoCaller(this);
1028 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1030
1031 int rc = checkStateDependency(MutableStateDep);
1032 if (FAILED(rc)) return rc;
1033
1034 setModified(IsModified_MachineData);
1035 mHWData.backup();
1036 mHWData->mFirmwareType = aFirmwareType;
1037
1038 return S_OK;
1039}
1040
1041STDMETHODIMP Machine::COMGETTER(KeyboardHidType)(KeyboardHidType_T *aKeyboardHidType)
1042{
1043 CheckComArgOutPointerValid(aKeyboardHidType);
1044
1045 AutoCaller autoCaller(this);
1046 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1047
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049
1050 *aKeyboardHidType = mHWData->mKeyboardHidType;
1051
1052 return S_OK;
1053}
1054
1055STDMETHODIMP Machine::COMSETTER(KeyboardHidType)(KeyboardHidType_T aKeyboardHidType)
1056{
1057 AutoCaller autoCaller(this);
1058 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1060
1061 int rc = checkStateDependency(MutableStateDep);
1062 if (FAILED(rc)) return rc;
1063
1064 setModified(IsModified_MachineData);
1065 mHWData.backup();
1066 mHWData->mKeyboardHidType = aKeyboardHidType;
1067
1068 return S_OK;
1069}
1070
1071STDMETHODIMP Machine::COMGETTER(PointingHidType)(PointingHidType_T *aPointingHidType)
1072{
1073 CheckComArgOutPointerValid(aPointingHidType);
1074
1075 AutoCaller autoCaller(this);
1076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1077
1078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1079
1080 *aPointingHidType = mHWData->mPointingHidType;
1081
1082 return S_OK;
1083}
1084
1085STDMETHODIMP Machine::COMSETTER(PointingHidType)(PointingHidType_T aPointingHidType)
1086{
1087 AutoCaller autoCaller(this);
1088 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1090
1091 int rc = checkStateDependency(MutableStateDep);
1092 if (FAILED(rc)) return rc;
1093
1094 setModified(IsModified_MachineData);
1095 mHWData.backup();
1096 mHWData->mPointingHidType = aPointingHidType;
1097
1098 return S_OK;
1099}
1100
1101STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1102{
1103 if (!aHWVersion)
1104 return E_POINTER;
1105
1106 AutoCaller autoCaller(this);
1107 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1108
1109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1110
1111 mHWData->mHWVersion.cloneTo(aHWVersion);
1112
1113 return S_OK;
1114}
1115
1116STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1117{
1118 /* check known version */
1119 Utf8Str hwVersion = aHWVersion;
1120 if ( hwVersion.compare("1") != 0
1121 && hwVersion.compare("2") != 0)
1122 return setError(E_INVALIDARG,
1123 tr("Invalid hardware version: %ls\n"), aHWVersion);
1124
1125 AutoCaller autoCaller(this);
1126 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1127
1128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1129
1130 HRESULT rc = checkStateDependency(MutableStateDep);
1131 if (FAILED(rc)) return rc;
1132
1133 setModified(IsModified_MachineData);
1134 mHWData.backup();
1135 mHWData->mHWVersion = hwVersion;
1136
1137 return S_OK;
1138}
1139
1140STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1141{
1142 CheckComArgOutPointerValid(aUUID);
1143
1144 AutoCaller autoCaller(this);
1145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1146
1147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1148
1149 if (!mHWData->mHardwareUUID.isEmpty())
1150 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1151 else
1152 mData->mUuid.toUtf16().cloneTo(aUUID);
1153
1154 return S_OK;
1155}
1156
1157STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1158{
1159 Guid hardwareUUID(aUUID);
1160 if (hardwareUUID.isEmpty())
1161 return E_INVALIDARG;
1162
1163 AutoCaller autoCaller(this);
1164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1165
1166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1167
1168 HRESULT rc = checkStateDependency(MutableStateDep);
1169 if (FAILED(rc)) return rc;
1170
1171 setModified(IsModified_MachineData);
1172 mHWData.backup();
1173 if (hardwareUUID == mData->mUuid)
1174 mHWData->mHardwareUUID.clear();
1175 else
1176 mHWData->mHardwareUUID = hardwareUUID;
1177
1178 return S_OK;
1179}
1180
1181STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1182{
1183 if (!memorySize)
1184 return E_POINTER;
1185
1186 AutoCaller autoCaller(this);
1187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1188
1189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1190
1191 *memorySize = mHWData->mMemorySize;
1192
1193 return S_OK;
1194}
1195
1196STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1197{
1198 /* check RAM limits */
1199 if ( memorySize < MM_RAM_MIN_IN_MB
1200 || memorySize > MM_RAM_MAX_IN_MB
1201 )
1202 return setError(E_INVALIDARG,
1203 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1204 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1205
1206 AutoCaller autoCaller(this);
1207 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1208
1209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1210
1211 HRESULT rc = checkStateDependency(MutableStateDep);
1212 if (FAILED(rc)) return rc;
1213
1214 setModified(IsModified_MachineData);
1215 mHWData.backup();
1216 mHWData->mMemorySize = memorySize;
1217
1218 return S_OK;
1219}
1220
1221STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1222{
1223 if (!CPUCount)
1224 return E_POINTER;
1225
1226 AutoCaller autoCaller(this);
1227 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1228
1229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 *CPUCount = mHWData->mCPUCount;
1232
1233 return S_OK;
1234}
1235
1236STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1237{
1238 /* check CPU limits */
1239 if ( CPUCount < SchemaDefs::MinCPUCount
1240 || CPUCount > SchemaDefs::MaxCPUCount
1241 )
1242 return setError(E_INVALIDARG,
1243 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1244 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1245
1246 AutoCaller autoCaller(this);
1247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1248
1249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1250
1251 /* We cant go below the current number of CPUs if hotplug is enabled*/
1252 if (mHWData->mCPUHotPlugEnabled)
1253 {
1254 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1255 {
1256 if (mHWData->mCPUAttached[idx])
1257 return setError(E_INVALIDARG,
1258 tr(": %lu (must be higher than or equal to %lu)"),
1259 CPUCount, idx+1);
1260 }
1261 }
1262
1263 HRESULT rc = checkStateDependency(MutableStateDep);
1264 if (FAILED(rc)) return rc;
1265
1266 setModified(IsModified_MachineData);
1267 mHWData.backup();
1268 mHWData->mCPUCount = CPUCount;
1269
1270 return S_OK;
1271}
1272
1273STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1274{
1275 if (!enabled)
1276 return E_POINTER;
1277
1278 AutoCaller autoCaller(this);
1279 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1280
1281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1282
1283 *enabled = mHWData->mCPUHotPlugEnabled;
1284
1285 return S_OK;
1286}
1287
1288STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1289{
1290 HRESULT rc = S_OK;
1291
1292 AutoCaller autoCaller(this);
1293 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1294
1295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1296
1297 rc = checkStateDependency(MutableStateDep);
1298 if (FAILED(rc)) return rc;
1299
1300 if (mHWData->mCPUHotPlugEnabled != enabled)
1301 {
1302 if (enabled)
1303 {
1304 setModified(IsModified_MachineData);
1305 mHWData.backup();
1306
1307 /* Add the amount of CPUs currently attached */
1308 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1309 {
1310 mHWData->mCPUAttached[i] = true;
1311 }
1312 }
1313 else
1314 {
1315 /*
1316 * We can disable hotplug only if the amount of maximum CPUs is equal
1317 * to the amount of attached CPUs
1318 */
1319 unsigned cCpusAttached = 0;
1320 unsigned iHighestId = 0;
1321
1322 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1323 {
1324 if (mHWData->mCPUAttached[i])
1325 {
1326 cCpusAttached++;
1327 iHighestId = i;
1328 }
1329 }
1330
1331 if ( (cCpusAttached != mHWData->mCPUCount)
1332 || (iHighestId >= mHWData->mCPUCount))
1333 return setError(E_INVALIDARG,
1334 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached\n"));
1335
1336 setModified(IsModified_MachineData);
1337 mHWData.backup();
1338 }
1339 }
1340
1341 mHWData->mCPUHotPlugEnabled = enabled;
1342
1343 return rc;
1344}
1345
1346STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled)
1347{
1348 CheckComArgOutPointerValid(enabled);
1349
1350 AutoCaller autoCaller(this);
1351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1353
1354 *enabled = mHWData->mHpetEnabled;
1355
1356 return S_OK;
1357}
1358
1359STDMETHODIMP Machine::COMSETTER(HpetEnabled)(BOOL enabled)
1360{
1361 HRESULT rc = S_OK;
1362
1363 AutoCaller autoCaller(this);
1364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1366
1367 rc = checkStateDependency(MutableStateDep);
1368 if (FAILED(rc)) return rc;
1369
1370 setModified(IsModified_MachineData);
1371 mHWData.backup();
1372
1373 mHWData->mHpetEnabled = enabled;
1374
1375 return rc;
1376}
1377
1378STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1379{
1380 if (!memorySize)
1381 return E_POINTER;
1382
1383 AutoCaller autoCaller(this);
1384 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1385
1386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1387
1388 *memorySize = mHWData->mVRAMSize;
1389
1390 return S_OK;
1391}
1392
1393STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1394{
1395 /* check VRAM limits */
1396 if (memorySize < SchemaDefs::MinGuestVRAM ||
1397 memorySize > SchemaDefs::MaxGuestVRAM)
1398 return setError(E_INVALIDARG,
1399 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1400 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1401
1402 AutoCaller autoCaller(this);
1403 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1404
1405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1406
1407 HRESULT rc = checkStateDependency(MutableStateDep);
1408 if (FAILED(rc)) return rc;
1409
1410 setModified(IsModified_MachineData);
1411 mHWData.backup();
1412 mHWData->mVRAMSize = memorySize;
1413
1414 return S_OK;
1415}
1416
1417/** @todo this method should not be public */
1418STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1419{
1420 if (!memoryBalloonSize)
1421 return E_POINTER;
1422
1423 AutoCaller autoCaller(this);
1424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1425
1426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1427
1428 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1429
1430 return S_OK;
1431}
1432
1433/**
1434 * Set the memory balloon size.
1435 *
1436 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1437 * we have to make sure that we never call IGuest from here.
1438 */
1439STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1440{
1441 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1442#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1443 /* check limits */
1444 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1445 return setError(E_INVALIDARG,
1446 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1447 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1448
1449 AutoCaller autoCaller(this);
1450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1451
1452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 setModified(IsModified_MachineData);
1455 mHWData.backup();
1456 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1457
1458 return S_OK;
1459#else
1460 NOREF(memoryBalloonSize);
1461 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1462#endif
1463}
1464
1465STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1466{
1467 NOREF(enabled);
1468 return E_NOTIMPL;
1469}
1470
1471STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1472{
1473 NOREF(enabled);
1474 return E_NOTIMPL;
1475}
1476
1477STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1478{
1479 if (!enabled)
1480 return E_POINTER;
1481
1482 AutoCaller autoCaller(this);
1483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1484
1485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 *enabled = mHWData->mAccelerate3DEnabled;
1488
1489 return S_OK;
1490}
1491
1492STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1493{
1494 AutoCaller autoCaller(this);
1495 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1496
1497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1498
1499 HRESULT rc = checkStateDependency(MutableStateDep);
1500 if (FAILED(rc)) return rc;
1501
1502 /** @todo check validity! */
1503
1504 setModified(IsModified_MachineData);
1505 mHWData.backup();
1506 mHWData->mAccelerate3DEnabled = enable;
1507
1508 return S_OK;
1509}
1510
1511
1512STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1513{
1514 if (!enabled)
1515 return E_POINTER;
1516
1517 AutoCaller autoCaller(this);
1518 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1519
1520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1521
1522 *enabled = mHWData->mAccelerate2DVideoEnabled;
1523
1524 return S_OK;
1525}
1526
1527STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1528{
1529 AutoCaller autoCaller(this);
1530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1531
1532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 HRESULT rc = checkStateDependency(MutableStateDep);
1535 if (FAILED(rc)) return rc;
1536
1537 /** @todo check validity! */
1538
1539 setModified(IsModified_MachineData);
1540 mHWData.backup();
1541 mHWData->mAccelerate2DVideoEnabled = enable;
1542
1543 return S_OK;
1544}
1545
1546STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1547{
1548 if (!monitorCount)
1549 return E_POINTER;
1550
1551 AutoCaller autoCaller(this);
1552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1553
1554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 *monitorCount = mHWData->mMonitorCount;
1557
1558 return S_OK;
1559}
1560
1561STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1562{
1563 /* make sure monitor count is a sensible number */
1564 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1565 return setError(E_INVALIDARG,
1566 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1567 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1568
1569 AutoCaller autoCaller(this);
1570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1571
1572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1573
1574 HRESULT rc = checkStateDependency(MutableStateDep);
1575 if (FAILED(rc)) return rc;
1576
1577 setModified(IsModified_MachineData);
1578 mHWData.backup();
1579 mHWData->mMonitorCount = monitorCount;
1580
1581 return S_OK;
1582}
1583
1584STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1585{
1586 if (!biosSettings)
1587 return E_POINTER;
1588
1589 AutoCaller autoCaller(this);
1590 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1591
1592 /* mBIOSSettings is constant during life time, no need to lock */
1593 mBIOSSettings.queryInterfaceTo(biosSettings);
1594
1595 return S_OK;
1596}
1597
1598STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1599{
1600 if (!aVal)
1601 return E_POINTER;
1602
1603 AutoCaller autoCaller(this);
1604 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1605
1606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1607
1608 switch(property)
1609 {
1610 case CPUPropertyType_PAE:
1611 *aVal = mHWData->mPAEEnabled;
1612 break;
1613
1614 case CPUPropertyType_Synthetic:
1615 *aVal = mHWData->mSyntheticCpu;
1616 break;
1617
1618 default:
1619 return E_INVALIDARG;
1620 }
1621 return S_OK;
1622}
1623
1624STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
1625{
1626 AutoCaller autoCaller(this);
1627 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1628
1629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1630
1631 HRESULT rc = checkStateDependency(MutableStateDep);
1632 if (FAILED(rc)) return rc;
1633
1634 switch(property)
1635 {
1636 case CPUPropertyType_PAE:
1637 setModified(IsModified_MachineData);
1638 mHWData.backup();
1639 mHWData->mPAEEnabled = !!aVal;
1640 break;
1641
1642 case CPUPropertyType_Synthetic:
1643 setModified(IsModified_MachineData);
1644 mHWData.backup();
1645 mHWData->mSyntheticCpu = !!aVal;
1646 break;
1647
1648 default:
1649 return E_INVALIDARG;
1650 }
1651 return S_OK;
1652}
1653
1654STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
1655{
1656 CheckComArgOutPointerValid(aValEax);
1657 CheckComArgOutPointerValid(aValEbx);
1658 CheckComArgOutPointerValid(aValEcx);
1659 CheckComArgOutPointerValid(aValEdx);
1660
1661 AutoCaller autoCaller(this);
1662 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1663
1664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1665
1666 switch(aId)
1667 {
1668 case 0x0:
1669 case 0x1:
1670 case 0x2:
1671 case 0x3:
1672 case 0x4:
1673 case 0x5:
1674 case 0x6:
1675 case 0x7:
1676 case 0x8:
1677 case 0x9:
1678 case 0xA:
1679 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
1680 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
1681
1682 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
1683 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
1684 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
1685 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
1686 break;
1687
1688 case 0x80000000:
1689 case 0x80000001:
1690 case 0x80000002:
1691 case 0x80000003:
1692 case 0x80000004:
1693 case 0x80000005:
1694 case 0x80000006:
1695 case 0x80000007:
1696 case 0x80000008:
1697 case 0x80000009:
1698 case 0x8000000A:
1699 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
1700 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
1701
1702 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
1703 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
1704 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
1705 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
1706 break;
1707
1708 default:
1709 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1710 }
1711 return S_OK;
1712}
1713
1714STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
1715{
1716 AutoCaller autoCaller(this);
1717 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1718
1719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 HRESULT rc = checkStateDependency(MutableStateDep);
1722 if (FAILED(rc)) return rc;
1723
1724 switch(aId)
1725 {
1726 case 0x0:
1727 case 0x1:
1728 case 0x2:
1729 case 0x3:
1730 case 0x4:
1731 case 0x5:
1732 case 0x6:
1733 case 0x7:
1734 case 0x8:
1735 case 0x9:
1736 case 0xA:
1737 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
1738 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1739 setModified(IsModified_MachineData);
1740 mHWData.backup();
1741 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
1742 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
1743 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
1744 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
1745 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
1746 break;
1747
1748 case 0x80000000:
1749 case 0x80000001:
1750 case 0x80000002:
1751 case 0x80000003:
1752 case 0x80000004:
1753 case 0x80000005:
1754 case 0x80000006:
1755 case 0x80000007:
1756 case 0x80000008:
1757 case 0x80000009:
1758 case 0x8000000A:
1759 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
1760 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
1761 setModified(IsModified_MachineData);
1762 mHWData.backup();
1763 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
1764 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
1765 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
1766 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
1767 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
1768 break;
1769
1770 default:
1771 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1772 }
1773 return S_OK;
1774}
1775
1776STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
1777{
1778 AutoCaller autoCaller(this);
1779 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1780
1781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1782
1783 HRESULT rc = checkStateDependency(MutableStateDep);
1784 if (FAILED(rc)) return rc;
1785
1786 switch(aId)
1787 {
1788 case 0x0:
1789 case 0x1:
1790 case 0x2:
1791 case 0x3:
1792 case 0x4:
1793 case 0x5:
1794 case 0x6:
1795 case 0x7:
1796 case 0x8:
1797 case 0x9:
1798 case 0xA:
1799 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA);
1800 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
1801 setModified(IsModified_MachineData);
1802 mHWData.backup();
1803 /* Invalidate leaf. */
1804 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
1805 break;
1806
1807 case 0x80000000:
1808 case 0x80000001:
1809 case 0x80000002:
1810 case 0x80000003:
1811 case 0x80000004:
1812 case 0x80000005:
1813 case 0x80000006:
1814 case 0x80000007:
1815 case 0x80000008:
1816 case 0x80000009:
1817 case 0x8000000A:
1818 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA);
1819 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
1820 setModified(IsModified_MachineData);
1821 mHWData.backup();
1822 /* Invalidate leaf. */
1823 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
1824 break;
1825
1826 default:
1827 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
1828 }
1829 return S_OK;
1830}
1831
1832STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
1833{
1834 AutoCaller autoCaller(this);
1835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1836
1837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1838
1839 HRESULT rc = checkStateDependency(MutableStateDep);
1840 if (FAILED(rc)) return rc;
1841
1842 setModified(IsModified_MachineData);
1843 mHWData.backup();
1844
1845 /* Invalidate all standard leafs. */
1846 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
1847 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
1848
1849 /* Invalidate all extended leafs. */
1850 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
1851 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
1852
1853 return S_OK;
1854}
1855
1856STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
1857{
1858 if (!aVal)
1859 return E_POINTER;
1860
1861 AutoCaller autoCaller(this);
1862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1863
1864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1865
1866 switch(property)
1867 {
1868 case HWVirtExPropertyType_Enabled:
1869 *aVal = mHWData->mHWVirtExEnabled;
1870 break;
1871
1872 case HWVirtExPropertyType_Exclusive:
1873 *aVal = mHWData->mHWVirtExExclusive;
1874 break;
1875
1876 case HWVirtExPropertyType_VPID:
1877 *aVal = mHWData->mHWVirtExVPIDEnabled;
1878 break;
1879
1880 case HWVirtExPropertyType_NestedPaging:
1881 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
1882 break;
1883
1884 case HWVirtExPropertyType_LargePages:
1885 *aVal = mHWData->mHWVirtExLargePagesEnabled;
1886 break;
1887
1888 default:
1889 return E_INVALIDARG;
1890 }
1891 return S_OK;
1892}
1893
1894STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
1895{
1896 AutoCaller autoCaller(this);
1897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1898
1899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1900
1901 HRESULT rc = checkStateDependency(MutableStateDep);
1902 if (FAILED(rc)) return rc;
1903
1904 switch(property)
1905 {
1906 case HWVirtExPropertyType_Enabled:
1907 setModified(IsModified_MachineData);
1908 mHWData.backup();
1909 mHWData->mHWVirtExEnabled = !!aVal;
1910 break;
1911
1912 case HWVirtExPropertyType_Exclusive:
1913 setModified(IsModified_MachineData);
1914 mHWData.backup();
1915 mHWData->mHWVirtExExclusive = !!aVal;
1916 break;
1917
1918 case HWVirtExPropertyType_VPID:
1919 setModified(IsModified_MachineData);
1920 mHWData.backup();
1921 mHWData->mHWVirtExVPIDEnabled = !!aVal;
1922 break;
1923
1924 case HWVirtExPropertyType_NestedPaging:
1925 setModified(IsModified_MachineData);
1926 mHWData.backup();
1927 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
1928 break;
1929
1930 case HWVirtExPropertyType_LargePages:
1931 setModified(IsModified_MachineData);
1932 mHWData.backup();
1933 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
1934 break;
1935
1936 default:
1937 return E_INVALIDARG;
1938 }
1939
1940 return S_OK;
1941}
1942
1943STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
1944{
1945 CheckComArgOutPointerValid(aSnapshotFolder);
1946
1947 AutoCaller autoCaller(this);
1948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1949
1950 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1951
1952 mUserData->mSnapshotFolderFull.cloneTo(aSnapshotFolder);
1953
1954 return S_OK;
1955}
1956
1957STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
1958{
1959 /* @todo (r=dmik):
1960 * 1. Allow to change the name of the snapshot folder containing snapshots
1961 * 2. Rename the folder on disk instead of just changing the property
1962 * value (to be smart and not to leave garbage). Note that it cannot be
1963 * done here because the change may be rolled back. Thus, the right
1964 * place is #saveSettings().
1965 */
1966
1967 AutoCaller autoCaller(this);
1968 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1969
1970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1971
1972 HRESULT rc = checkStateDependency(MutableStateDep);
1973 if (FAILED(rc)) return rc;
1974
1975 if (!mData->mCurrentSnapshot.isNull())
1976 return setError(E_FAIL,
1977 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
1978
1979 Utf8Str snapshotFolder = aSnapshotFolder;
1980
1981 if (snapshotFolder.isEmpty())
1982 {
1983 if (isInOwnDir())
1984 {
1985 /* the default snapshots folder is 'Snapshots' in the machine dir */
1986 snapshotFolder = "Snapshots";
1987 }
1988 else
1989 {
1990 /* the default snapshots folder is {UUID}, for backwards
1991 * compatibility and to resolve conflicts */
1992 snapshotFolder = Utf8StrFmt("{%RTuuid}", mData->mUuid.raw());
1993 }
1994 }
1995
1996 int vrc = calculateFullPath(snapshotFolder, snapshotFolder);
1997 if (RT_FAILURE(vrc))
1998 return setError(E_FAIL,
1999 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2000 aSnapshotFolder, vrc);
2001
2002 setModified(IsModified_MachineData);
2003 mUserData.backup();
2004 mUserData->mSnapshotFolder = aSnapshotFolder;
2005 mUserData->mSnapshotFolderFull = snapshotFolder;
2006
2007 return S_OK;
2008}
2009
2010STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2011{
2012 if (ComSafeArrayOutIsNull(aAttachments))
2013 return E_POINTER;
2014
2015 AutoCaller autoCaller(this);
2016 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2017
2018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2019
2020 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2021 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2022
2023 return S_OK;
2024}
2025
2026STDMETHODIMP Machine::COMGETTER(VRDPServer)(IVRDPServer **vrdpServer)
2027{
2028#ifdef VBOX_WITH_VRDP
2029 if (!vrdpServer)
2030 return E_POINTER;
2031
2032 AutoCaller autoCaller(this);
2033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2034
2035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2036
2037 Assert(!!mVRDPServer);
2038 mVRDPServer.queryInterfaceTo(vrdpServer);
2039
2040 return S_OK;
2041#else
2042 NOREF(vrdpServer);
2043 ReturnComNotImplemented();
2044#endif
2045}
2046
2047STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2048{
2049 if (!audioAdapter)
2050 return E_POINTER;
2051
2052 AutoCaller autoCaller(this);
2053 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2054
2055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2056
2057 mAudioAdapter.queryInterfaceTo(audioAdapter);
2058 return S_OK;
2059}
2060
2061STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2062{
2063#ifdef VBOX_WITH_VUSB
2064 CheckComArgOutPointerValid(aUSBController);
2065
2066 AutoCaller autoCaller(this);
2067 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2068 MultiResult rc(S_OK);
2069
2070# ifdef VBOX_WITH_USB
2071 rc = mParent->host()->checkUSBProxyService();
2072 if (FAILED(rc)) return rc;
2073# endif
2074
2075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2076
2077 return rc = mUSBController.queryInterfaceTo(aUSBController);
2078#else
2079 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2080 * extended error info to indicate that USB is simply not available
2081 * (w/o treting it as a failure), for example, as in OSE */
2082 NOREF(aUSBController);
2083 ReturnComNotImplemented();
2084#endif /* VBOX_WITH_VUSB */
2085}
2086
2087STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2088{
2089 CheckComArgOutPointerValid(aFilePath);
2090
2091 AutoLimitedCaller autoCaller(this);
2092 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2093
2094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2095
2096 mData->m_strConfigFileFull.cloneTo(aFilePath);
2097 return S_OK;
2098}
2099
2100STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2101{
2102 CheckComArgOutPointerValid(aModified);
2103
2104 AutoCaller autoCaller(this);
2105 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2106
2107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2108
2109 HRESULT rc = checkStateDependency(MutableStateDep);
2110 if (FAILED(rc)) return rc;
2111
2112 if (!mData->pMachineConfigFile->fileExists())
2113 // this is a new machine, and no config file exists yet:
2114 *aModified = TRUE;
2115 else
2116 *aModified = (mData->flModifications != 0);
2117
2118 return S_OK;
2119}
2120
2121STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2122{
2123 CheckComArgOutPointerValid(aSessionState);
2124
2125 AutoCaller autoCaller(this);
2126 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2127
2128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2129
2130 *aSessionState = mData->mSession.mState;
2131
2132 return S_OK;
2133}
2134
2135STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2136{
2137 CheckComArgOutPointerValid(aSessionType);
2138
2139 AutoCaller autoCaller(this);
2140 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2141
2142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2143
2144 mData->mSession.mType.cloneTo(aSessionType);
2145
2146 return S_OK;
2147}
2148
2149STDMETHODIMP Machine::COMGETTER(SessionPid)(ULONG *aSessionPid)
2150{
2151 CheckComArgOutPointerValid(aSessionPid);
2152
2153 AutoCaller autoCaller(this);
2154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2155
2156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 *aSessionPid = mData->mSession.mPid;
2159
2160 return S_OK;
2161}
2162
2163STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2164{
2165 if (!machineState)
2166 return E_POINTER;
2167
2168 AutoCaller autoCaller(this);
2169 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2170
2171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2172
2173 *machineState = mData->mMachineState;
2174
2175 return S_OK;
2176}
2177
2178STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2179{
2180 CheckComArgOutPointerValid(aLastStateChange);
2181
2182 AutoCaller autoCaller(this);
2183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2184
2185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2186
2187 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2188
2189 return S_OK;
2190}
2191
2192STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2193{
2194 CheckComArgOutPointerValid(aStateFilePath);
2195
2196 AutoCaller autoCaller(this);
2197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2198
2199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2200
2201 mSSData->mStateFilePath.cloneTo(aStateFilePath);
2202
2203 return S_OK;
2204}
2205
2206STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2207{
2208 CheckComArgOutPointerValid(aLogFolder);
2209
2210 AutoCaller autoCaller(this);
2211 AssertComRCReturnRC(autoCaller.rc());
2212
2213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2214
2215 Utf8Str logFolder;
2216 getLogFolder(logFolder);
2217
2218 Bstr (logFolder).cloneTo(aLogFolder);
2219
2220 return S_OK;
2221}
2222
2223STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2224{
2225 CheckComArgOutPointerValid(aCurrentSnapshot);
2226
2227 AutoCaller autoCaller(this);
2228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2229
2230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2231
2232 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2233
2234 return S_OK;
2235}
2236
2237STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2238{
2239 CheckComArgOutPointerValid(aSnapshotCount);
2240
2241 AutoCaller autoCaller(this);
2242 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2243
2244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2245
2246 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2247 ? 0
2248 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2249
2250 return S_OK;
2251}
2252
2253STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2254{
2255 CheckComArgOutPointerValid(aCurrentStateModified);
2256
2257 AutoCaller autoCaller(this);
2258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2259
2260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2261
2262 /* Note: for machines with no snapshots, we always return FALSE
2263 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2264 * reasons :) */
2265
2266 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2267 ? FALSE
2268 : mData->mCurrentStateModified;
2269
2270 return S_OK;
2271}
2272
2273STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2274{
2275 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2276
2277 AutoCaller autoCaller(this);
2278 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2279
2280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2281
2282 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2283 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2284
2285 return S_OK;
2286}
2287
2288STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2289{
2290 CheckComArgOutPointerValid(aClipboardMode);
2291
2292 AutoCaller autoCaller(this);
2293 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2294
2295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2296
2297 *aClipboardMode = mHWData->mClipboardMode;
2298
2299 return S_OK;
2300}
2301
2302STDMETHODIMP
2303Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2304{
2305 AutoCaller autoCaller(this);
2306 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2307
2308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2309
2310 HRESULT rc = checkStateDependency(MutableStateDep);
2311 if (FAILED(rc)) return rc;
2312
2313 setModified(IsModified_MachineData);
2314 mHWData.backup();
2315 mHWData->mClipboardMode = aClipboardMode;
2316
2317 return S_OK;
2318}
2319
2320STDMETHODIMP
2321Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2322{
2323 CheckComArgOutPointerValid(aPatterns);
2324
2325 AutoCaller autoCaller(this);
2326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2327
2328 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2329
2330 try
2331 {
2332 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2333 }
2334 catch (...)
2335 {
2336 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
2337 }
2338
2339 return S_OK;
2340}
2341
2342STDMETHODIMP
2343Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2344{
2345 AutoCaller autoCaller(this);
2346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2347
2348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2349
2350 HRESULT rc = checkStateDependency(MutableStateDep);
2351 if (FAILED(rc)) return rc;
2352
2353 setModified(IsModified_MachineData);
2354 mHWData.backup();
2355 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2356 return rc;
2357}
2358
2359STDMETHODIMP
2360Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2361{
2362 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2363
2364 AutoCaller autoCaller(this);
2365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2366
2367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2368
2369 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2370 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2371
2372 return S_OK;
2373}
2374
2375STDMETHODIMP
2376Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2377{
2378 CheckComArgOutPointerValid(aEnabled);
2379
2380 AutoCaller autoCaller(this);
2381 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2382
2383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2384
2385 *aEnabled = mUserData->mTeleporterEnabled;
2386
2387 return S_OK;
2388}
2389
2390STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2391{
2392 AutoCaller autoCaller(this);
2393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2394
2395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2396
2397 /* Only allow it to be set to true when PoweredOff or Aborted.
2398 (Clearing it is always permitted.) */
2399 if ( aEnabled
2400 && mData->mRegistered
2401 && ( getClassID() != clsidSessionMachine
2402 || ( mData->mMachineState != MachineState_PoweredOff
2403 && mData->mMachineState != MachineState_Teleported
2404 && mData->mMachineState != MachineState_Aborted
2405 )
2406 )
2407 )
2408 return setError(VBOX_E_INVALID_VM_STATE,
2409 tr("The machine is not powered off (state is %s)"),
2410 Global::stringifyMachineState(mData->mMachineState));
2411
2412 setModified(IsModified_MachineData);
2413 mUserData.backup();
2414 mUserData->mTeleporterEnabled = aEnabled;
2415
2416 return S_OK;
2417}
2418
2419STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2420{
2421 CheckComArgOutPointerValid(aPort);
2422
2423 AutoCaller autoCaller(this);
2424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2425
2426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2427
2428 *aPort = mUserData->mTeleporterPort;
2429
2430 return S_OK;
2431}
2432
2433STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2434{
2435 if (aPort >= _64K)
2436 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2437
2438 AutoCaller autoCaller(this);
2439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2440
2441 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 HRESULT rc = checkStateDependency(MutableStateDep);
2444 if (FAILED(rc)) return rc;
2445
2446 setModified(IsModified_MachineData);
2447 mUserData.backup();
2448 mUserData->mTeleporterPort = aPort;
2449
2450 return S_OK;
2451}
2452
2453STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2454{
2455 CheckComArgOutPointerValid(aAddress);
2456
2457 AutoCaller autoCaller(this);
2458 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2459
2460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2461
2462 mUserData->mTeleporterAddress.cloneTo(aAddress);
2463
2464 return S_OK;
2465}
2466
2467STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2468{
2469 AutoCaller autoCaller(this);
2470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2471
2472 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2473
2474 HRESULT rc = checkStateDependency(MutableStateDep);
2475 if (FAILED(rc)) return rc;
2476
2477 setModified(IsModified_MachineData);
2478 mUserData.backup();
2479 mUserData->mTeleporterAddress = aAddress;
2480
2481 return S_OK;
2482}
2483
2484STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2485{
2486 CheckComArgOutPointerValid(aPassword);
2487
2488 AutoCaller autoCaller(this);
2489 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2490
2491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2492
2493 mUserData->mTeleporterPassword.cloneTo(aPassword);
2494
2495 return S_OK;
2496}
2497
2498STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2499{
2500 AutoCaller autoCaller(this);
2501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2502
2503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 HRESULT rc = checkStateDependency(MutableStateDep);
2506 if (FAILED(rc)) return rc;
2507
2508 setModified(IsModified_MachineData);
2509 mUserData.backup();
2510 mUserData->mTeleporterPassword = aPassword;
2511
2512 return S_OK;
2513}
2514
2515STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
2516{
2517 CheckComArgOutPointerValid(aEnabled);
2518
2519 AutoCaller autoCaller(this);
2520 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2521
2522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2523
2524 *aEnabled = mUserData->mRTCUseUTC;
2525
2526 return S_OK;
2527}
2528
2529STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
2530{
2531 AutoCaller autoCaller(this);
2532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2533
2534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2535
2536 /* Only allow it to be set to true when PoweredOff or Aborted.
2537 (Clearing it is always permitted.) */
2538 if ( aEnabled
2539 && mData->mRegistered
2540 && ( getClassID() != clsidSessionMachine
2541 || ( mData->mMachineState != MachineState_PoweredOff
2542 && mData->mMachineState != MachineState_Teleported
2543 && mData->mMachineState != MachineState_Aborted
2544 )
2545 )
2546 )
2547 return setError(VBOX_E_INVALID_VM_STATE,
2548 tr("The machine is not powered off (state is %s)"),
2549 Global::stringifyMachineState(mData->mMachineState));
2550
2551 setModified(IsModified_MachineData);
2552 mUserData.backup();
2553 mUserData->mRTCUseUTC = aEnabled;
2554
2555 return S_OK;
2556}
2557
2558STDMETHODIMP Machine::COMGETTER(IoCacheEnabled)(BOOL *aEnabled)
2559{
2560 CheckComArgOutPointerValid(aEnabled);
2561
2562 AutoCaller autoCaller(this);
2563 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2564
2565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2566
2567 *aEnabled = mHWData->mIoCacheEnabled;
2568
2569 return S_OK;
2570}
2571
2572STDMETHODIMP Machine::COMSETTER(IoCacheEnabled)(BOOL aEnabled)
2573{
2574 AutoCaller autoCaller(this);
2575 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2576
2577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2578
2579 HRESULT rc = checkStateDependency(MutableStateDep);
2580 if (FAILED(rc)) return rc;
2581
2582 setModified(IsModified_MachineData);
2583 mHWData.backup();
2584 mHWData->mIoCacheEnabled = aEnabled;
2585
2586 return S_OK;
2587}
2588
2589STDMETHODIMP Machine::COMGETTER(IoCacheSize)(ULONG *aIoCacheSize)
2590{
2591 CheckComArgOutPointerValid(aIoCacheSize);
2592
2593 AutoCaller autoCaller(this);
2594 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2595
2596 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2597
2598 *aIoCacheSize = mHWData->mIoCacheSize;
2599
2600 return S_OK;
2601}
2602
2603STDMETHODIMP Machine::COMSETTER(IoCacheSize)(ULONG aIoCacheSize)
2604{
2605 AutoCaller autoCaller(this);
2606 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2607
2608 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2609
2610 HRESULT rc = checkStateDependency(MutableStateDep);
2611 if (FAILED(rc)) return rc;
2612
2613 setModified(IsModified_MachineData);
2614 mHWData.backup();
2615 mHWData->mIoCacheSize = aIoCacheSize;
2616
2617 return S_OK;
2618}
2619
2620STDMETHODIMP Machine::COMGETTER(IoBandwidthMax)(ULONG *aIoBandwidthMax)
2621{
2622 CheckComArgOutPointerValid(aIoBandwidthMax);
2623
2624 AutoCaller autoCaller(this);
2625 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2626
2627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2628
2629 *aIoBandwidthMax = mHWData->mIoBandwidthMax;
2630
2631 return S_OK;
2632}
2633
2634STDMETHODIMP Machine::COMSETTER(IoBandwidthMax)(ULONG aIoBandwidthMax)
2635{
2636 AutoCaller autoCaller(this);
2637 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2638
2639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2640
2641 HRESULT rc = checkStateDependency(MutableStateDep);
2642 if (FAILED(rc)) return rc;
2643
2644 setModified(IsModified_MachineData);
2645 mHWData.backup();
2646 mHWData->mIoBandwidthMax = aIoBandwidthMax;
2647
2648 return S_OK;
2649}
2650
2651STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
2652{
2653 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
2654 return setError(E_INVALIDARG,
2655 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
2656 aPosition, SchemaDefs::MaxBootPosition);
2657
2658 if (aDevice == DeviceType_USB)
2659 return setError(E_NOTIMPL,
2660 tr("Booting from USB device is currently not supported"));
2661
2662 AutoCaller autoCaller(this);
2663 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2664
2665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2666
2667 HRESULT rc = checkStateDependency(MutableStateDep);
2668 if (FAILED(rc)) return rc;
2669
2670 setModified(IsModified_MachineData);
2671 mHWData.backup();
2672 mHWData->mBootOrder[aPosition - 1] = aDevice;
2673
2674 return S_OK;
2675}
2676
2677STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
2678{
2679 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
2680 return setError(E_INVALIDARG,
2681 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
2682 aPosition, SchemaDefs::MaxBootPosition);
2683
2684 AutoCaller autoCaller(this);
2685 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2686
2687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2688
2689 *aDevice = mHWData->mBootOrder[aPosition - 1];
2690
2691 return S_OK;
2692}
2693
2694STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
2695 LONG aControllerPort,
2696 LONG aDevice,
2697 DeviceType_T aType,
2698 IN_BSTR aId)
2699{
2700 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aId=\"%ls\"\n",
2701 aControllerName, aControllerPort, aDevice, aType, aId));
2702
2703 CheckComArgStrNotEmptyOrNull(aControllerName);
2704
2705 AutoCaller autoCaller(this);
2706 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2707
2708 // if this becomes true then we need to call saveSettings in the end
2709 // @todo r=dj there is no error handling so far...
2710 bool fNeedsSaveSettings = false;
2711
2712 // request the host lock first, since might be calling Host methods for getting host drives;
2713 // next, protect the media tree all the while we're in here, as well as our member variables
2714 AutoMultiWriteLock2 alock(mParent->host()->lockHandle(),
2715 this->lockHandle() COMMA_LOCKVAL_SRC_POS);
2716 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
2717
2718 HRESULT rc = checkStateDependency(MutableStateDep);
2719 if (FAILED(rc)) return rc;
2720
2721 /// @todo NEWMEDIA implicit machine registration
2722 if (!mData->mRegistered)
2723 return setError(VBOX_E_INVALID_OBJECT_STATE,
2724 tr("Cannot attach storage devices to an unregistered machine"));
2725
2726 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
2727
2728 if (Global::IsOnlineOrTransient(mData->mMachineState))
2729 return setError(VBOX_E_INVALID_VM_STATE,
2730 tr("Invalid machine state: %s"),
2731 Global::stringifyMachineState(mData->mMachineState));
2732
2733 /* Check for an existing controller. */
2734 ComObjPtr<StorageController> ctl;
2735 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
2736 if (FAILED(rc)) return rc;
2737
2738 /* check that the port and device are not out of range. */
2739 ULONG portCount;
2740 ULONG devicesPerPort;
2741 rc = ctl->COMGETTER(PortCount)(&portCount);
2742 if (FAILED(rc)) return rc;
2743 rc = ctl->COMGETTER(MaxDevicesPerPortCount)(&devicesPerPort);
2744 if (FAILED(rc)) return rc;
2745
2746 if ( (aControllerPort < 0)
2747 || (aControllerPort >= (LONG)portCount)
2748 || (aDevice < 0)
2749 || (aDevice >= (LONG)devicesPerPort)
2750 )
2751 return setError(E_INVALIDARG,
2752 tr("The port and/or count parameter are out of range [%lu:%lu]"),
2753 portCount,
2754 devicesPerPort);
2755
2756 /* check if the device slot is already busy */
2757 MediumAttachment *pAttachTemp;
2758 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
2759 aControllerName,
2760 aControllerPort,
2761 aDevice)))
2762 {
2763 Medium *pMedium = pAttachTemp->getMedium();
2764 if (pMedium)
2765 {
2766 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
2767 return setError(VBOX_E_OBJECT_IN_USE,
2768 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
2769 pMedium->getLocationFull().raw(),
2770 aControllerPort,
2771 aDevice,
2772 aControllerName);
2773 }
2774 else
2775 return setError(VBOX_E_OBJECT_IN_USE,
2776 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
2777 aControllerPort, aDevice, aControllerName);
2778 }
2779
2780 Guid uuid(aId);
2781
2782 ComObjPtr<Medium> medium;
2783
2784 switch (aType)
2785 {
2786 case DeviceType_HardDisk:
2787 /* find a hard disk by UUID */
2788 rc = mParent->findHardDisk(&uuid, NULL, true /* aSetError */, &medium);
2789 if (FAILED(rc)) return rc;
2790 break;
2791
2792 case DeviceType_DVD: // @todo r=dj eliminate this, replace with findDVDImage
2793 if (!uuid.isEmpty())
2794 {
2795 /* first search for host drive */
2796 SafeIfaceArray<IMedium> drivevec;
2797 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
2798 if (SUCCEEDED(rc))
2799 {
2800 for (size_t i = 0; i < drivevec.size(); ++i)
2801 {
2802 /// @todo eliminate this conversion
2803 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2804 if (med->getId() == uuid)
2805 {
2806 medium = med;
2807 break;
2808 }
2809 }
2810 }
2811
2812 if (medium.isNull())
2813 {
2814 /* find a DVD image by UUID */
2815 rc = mParent->findDVDImage(&uuid, NULL, true /* aSetError */, &medium);
2816 if (FAILED(rc)) return rc;
2817 }
2818 }
2819 else
2820 {
2821 /* null UUID means null medium, which needs no code */
2822 }
2823 break;
2824
2825 case DeviceType_Floppy: // @todo r=dj eliminate this, replace with findFloppyImage
2826 if (!uuid.isEmpty())
2827 {
2828 /* first search for host drive */
2829 SafeIfaceArray<IMedium> drivevec;
2830 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
2831 if (SUCCEEDED(rc))
2832 {
2833 for (size_t i = 0; i < drivevec.size(); ++i)
2834 {
2835 /// @todo eliminate this conversion
2836 ComObjPtr<Medium> med = (Medium *)drivevec[i];
2837 if (med->getId() == uuid)
2838 {
2839 medium = med;
2840 break;
2841 }
2842 }
2843 }
2844
2845 if (medium.isNull())
2846 {
2847 /* find a floppy image by UUID */
2848 rc = mParent->findFloppyImage(&uuid, NULL, true /* aSetError */, &medium);
2849 if (FAILED(rc)) return rc;
2850 }
2851 }
2852 else
2853 {
2854 /* null UUID means null medium, which needs no code */
2855 }
2856 break;
2857
2858 default:
2859 return setError(E_INVALIDARG,
2860 tr("The device type %d is not recognized"),
2861 (int)aType);
2862 }
2863
2864 AutoCaller mediumCaller(medium);
2865 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
2866
2867 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
2868
2869 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
2870 && !medium.isNull()
2871 )
2872 return setError(VBOX_E_OBJECT_IN_USE,
2873 tr("Medium '%s' is already attached to this virtual machine"),
2874 medium->getLocationFull().raw());
2875
2876 bool indirect = false;
2877 if (!medium.isNull())
2878 indirect = medium->isReadOnly();
2879 bool associate = true;
2880
2881 do
2882 {
2883 if (aType == DeviceType_HardDisk && mMediaData.isBackedUp())
2884 {
2885 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
2886
2887 /* check if the medium was attached to the VM before we started
2888 * changing attachments in which case the attachment just needs to
2889 * be restored */
2890 if ((pAttachTemp = findAttachment(oldAtts, medium)))
2891 {
2892 AssertReturn(!indirect, E_FAIL);
2893
2894 /* see if it's the same bus/channel/device */
2895 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
2896 {
2897 /* the simplest case: restore the whole attachment
2898 * and return, nothing else to do */
2899 mMediaData->mAttachments.push_back(pAttachTemp);
2900 return S_OK;
2901 }
2902
2903 /* bus/channel/device differ; we need a new attachment object,
2904 * but don't try to associate it again */
2905 associate = false;
2906 break;
2907 }
2908 }
2909
2910 /* go further only if the attachment is to be indirect */
2911 if (!indirect)
2912 break;
2913
2914 /* perform the so called smart attachment logic for indirect
2915 * attachments. Note that smart attachment is only applicable to base
2916 * hard disks. */
2917
2918 if (medium->getParent().isNull())
2919 {
2920 /* first, investigate the backup copy of the current hard disk
2921 * attachments to make it possible to re-attach existing diffs to
2922 * another device slot w/o losing their contents */
2923 if (mMediaData.isBackedUp())
2924 {
2925 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
2926
2927 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
2928 uint32_t foundLevel = 0;
2929
2930 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
2931 it != oldAtts.end();
2932 ++it)
2933 {
2934 uint32_t level = 0;
2935 MediumAttachment *pAttach = *it;
2936 ComObjPtr<Medium> pMedium = pAttach->getMedium();
2937 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
2938 if (pMedium.isNull())
2939 continue;
2940
2941 if (pMedium->getBase(&level).equalsTo(medium))
2942 {
2943 /* skip the hard disk if its currently attached (we
2944 * cannot attach the same hard disk twice) */
2945 if (findAttachment(mMediaData->mAttachments,
2946 pMedium))
2947 continue;
2948
2949 /* matched device, channel and bus (i.e. attached to the
2950 * same place) will win and immediately stop the search;
2951 * otherwise the attachment that has the youngest
2952 * descendant of medium will be used
2953 */
2954 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
2955 {
2956 /* the simplest case: restore the whole attachment
2957 * and return, nothing else to do */
2958 mMediaData->mAttachments.push_back(*it);
2959 return S_OK;
2960 }
2961 else if ( foundIt == oldAtts.end()
2962 || level > foundLevel /* prefer younger */
2963 )
2964 {
2965 foundIt = it;
2966 foundLevel = level;
2967 }
2968 }
2969 }
2970
2971 if (foundIt != oldAtts.end())
2972 {
2973 /* use the previously attached hard disk */
2974 medium = (*foundIt)->getMedium();
2975 mediumCaller.attach(medium);
2976 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
2977 mediumLock.attach(medium);
2978 /* not implicit, doesn't require association with this VM */
2979 indirect = false;
2980 associate = false;
2981 /* go right to the MediumAttachment creation */
2982 break;
2983 }
2984 }
2985
2986 /* must give up the medium lock and medium tree lock as below we
2987 * go over snapshots, which needs a lock with higher lock order. */
2988 mediumLock.release();
2989 treeLock.release();
2990
2991 /* then, search through snapshots for the best diff in the given
2992 * hard disk's chain to base the new diff on */
2993
2994 ComObjPtr<Medium> base;
2995 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
2996 while (snap)
2997 {
2998 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
2999
3000 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3001
3002 MediaData::AttachmentList::const_iterator foundIt = snapAtts.end();
3003 uint32_t foundLevel = 0;
3004
3005 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3006 it != snapAtts.end();
3007 ++it)
3008 {
3009 MediumAttachment *pAttach = *it;
3010 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3011 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3012 if (pMedium.isNull())
3013 continue;
3014
3015 uint32_t level = 0;
3016 if (pMedium->getBase(&level).equalsTo(medium))
3017 {
3018 /* matched device, channel and bus (i.e. attached to the
3019 * same place) will win and immediately stop the search;
3020 * otherwise the attachment that has the youngest
3021 * descendant of medium will be used
3022 */
3023 if ( (*it)->getDevice() == aDevice
3024 && (*it)->getPort() == aControllerPort
3025 && (*it)->getControllerName() == aControllerName
3026 )
3027 {
3028 foundIt = it;
3029 break;
3030 }
3031 else if ( foundIt == snapAtts.end()
3032 || level > foundLevel /* prefer younger */
3033 )
3034 {
3035 foundIt = it;
3036 foundLevel = level;
3037 }
3038 }
3039 }
3040
3041 if (foundIt != snapAtts.end())
3042 {
3043 base = (*foundIt)->getMedium();
3044 break;
3045 }
3046
3047 snap = snap->getParent();
3048 }
3049
3050 /* re-lock medium tree and the medium, as we need it below */
3051 treeLock.acquire();
3052 mediumLock.acquire();
3053
3054 /* found a suitable diff, use it as a base */
3055 if (!base.isNull())
3056 {
3057 medium = base;
3058 mediumCaller.attach(medium);
3059 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3060 mediumLock.attach(medium);
3061 }
3062 }
3063
3064 ComObjPtr<Medium> diff;
3065 diff.createObject();
3066 rc = diff->init(mParent,
3067 medium->preferredDiffFormat().raw(),
3068 BstrFmt("%ls"RTPATH_SLASH_STR,
3069 mUserData->mSnapshotFolderFull.raw()).raw(),
3070 &fNeedsSaveSettings);
3071 if (FAILED(rc)) return rc;
3072
3073 /* Apply the normal locking logic to the entire chain. */
3074 MediumLockList *pMediumLockList(new MediumLockList());
3075 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
3076 true /* fMediumLockWrite */,
3077 medium,
3078 *pMediumLockList);
3079 if (FAILED(rc)) return rc;
3080 rc = pMediumLockList->Lock();
3081 if (FAILED(rc))
3082 return setError(rc,
3083 tr("Could not lock medium when creating diff '%s'"),
3084 diff->getLocationFull().c_str());
3085
3086 /* will leave the lock before the potentially lengthy operation, so
3087 * protect with the special state */
3088 MachineState_T oldState = mData->mMachineState;
3089 setMachineState(MachineState_SettingUp);
3090
3091 mediumLock.leave();
3092 treeLock.leave();
3093 alock.leave();
3094
3095 rc = medium->createDiffStorage(diff, MediumVariant_Standard,
3096 pMediumLockList, NULL /* aProgress */,
3097 true /* aWait */, &fNeedsSaveSettings);
3098
3099 alock.enter();
3100 treeLock.enter();
3101 mediumLock.enter();
3102
3103 setMachineState(oldState);
3104
3105 /* Unlock the media and free the associated memory. */
3106 delete pMediumLockList;
3107
3108 if (FAILED(rc)) return rc;
3109
3110 /* use the created diff for the actual attachment */
3111 medium = diff;
3112 mediumCaller.attach(medium);
3113 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3114 mediumLock.attach(medium);
3115 }
3116 while (0);
3117
3118 ComObjPtr<MediumAttachment> attachment;
3119 attachment.createObject();
3120 rc = attachment->init(this, medium, aControllerName, aControllerPort, aDevice, aType, indirect);
3121 if (FAILED(rc)) return rc;
3122
3123 if (associate && !medium.isNull())
3124 {
3125 /* as the last step, associate the medium to the VM */
3126 rc = medium->attachTo(mData->mUuid);
3127 /* here we can fail because of Deleting, or being in process of
3128 * creating a Diff */
3129 if (FAILED(rc)) return rc;
3130 }
3131
3132 /* success: finally remember the attachment */
3133 setModified(IsModified_Storage);
3134 mMediaData.backup();
3135 mMediaData->mAttachments.push_back(attachment);
3136
3137 if (fNeedsSaveSettings)
3138 {
3139 mediumLock.release();
3140 treeLock.leave();
3141 alock.release();
3142
3143 AutoWriteLock vboxLock(mParent COMMA_LOCKVAL_SRC_POS);
3144 mParent->saveSettings();
3145 }
3146
3147 return rc;
3148}
3149
3150STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
3151 LONG aDevice)
3152{
3153 CheckComArgStrNotEmptyOrNull(aControllerName);
3154
3155 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
3156 aControllerName, aControllerPort, aDevice));
3157
3158 AutoCaller autoCaller(this);
3159 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3160
3161 bool fNeedsSaveSettings = false;
3162
3163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3164
3165 HRESULT rc = checkStateDependency(MutableStateDep);
3166 if (FAILED(rc)) return rc;
3167
3168 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3169
3170 if (Global::IsOnlineOrTransient(mData->mMachineState))
3171 return setError(VBOX_E_INVALID_VM_STATE,
3172 tr("Invalid machine state: %s"),
3173 Global::stringifyMachineState(mData->mMachineState));
3174
3175 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
3176 aControllerName,
3177 aControllerPort,
3178 aDevice);
3179 if (!pAttach)
3180 return setError(VBOX_E_OBJECT_NOT_FOUND,
3181 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3182 aDevice, aControllerPort, aControllerName);
3183
3184 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
3185 DeviceType_T mediumType = pAttach->getType();
3186
3187 if (pAttach->isImplicit())
3188 {
3189 /* attempt to implicitly delete the implicitly created diff */
3190
3191 /// @todo move the implicit flag from MediumAttachment to Medium
3192 /// and forbid any hard disk operation when it is implicit. Or maybe
3193 /// a special media state for it to make it even more simple.
3194
3195 Assert(mMediaData.isBackedUp());
3196
3197 /* will leave the lock before the potentially lengthy operation, so
3198 * protect with the special state */
3199 MachineState_T oldState = mData->mMachineState;
3200 setMachineState(MachineState_SettingUp);
3201
3202 alock.leave();
3203
3204 rc = oldmedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/,
3205 &fNeedsSaveSettings);
3206
3207 alock.enter();
3208
3209 setMachineState(oldState);
3210
3211 if (FAILED(rc)) return rc;
3212 }
3213
3214 setModified(IsModified_Storage);
3215 mMediaData.backup();
3216
3217 /* we cannot use erase (it) below because backup() above will create
3218 * a copy of the list and make this copy active, but the iterator
3219 * still refers to the original and is not valid for the copy */
3220 mMediaData->mAttachments.remove(pAttach);
3221
3222 /* For non-hard disk media, detach straight away. */
3223 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
3224 oldmedium->detachFrom(mData->mUuid);
3225
3226 if (fNeedsSaveSettings)
3227 {
3228 bool fNeedsGlobalSaveSettings = false;
3229 saveSettings(&fNeedsGlobalSaveSettings);
3230
3231 if (fNeedsGlobalSaveSettings)
3232 {
3233 alock.release();
3234 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS);
3235 mParent->saveSettings();
3236 }
3237 }
3238
3239 return S_OK;
3240}
3241
3242STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
3243 LONG aDevice, BOOL aPassthrough)
3244{
3245 CheckComArgStrNotEmptyOrNull(aControllerName);
3246
3247 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n",
3248 aControllerName, aControllerPort, aDevice, aPassthrough));
3249
3250 AutoCaller autoCaller(this);
3251 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3252
3253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3254
3255 HRESULT rc = checkStateDependency(MutableStateDep);
3256 if (FAILED(rc)) return rc;
3257
3258 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3259
3260 if (Global::IsOnlineOrTransient(mData->mMachineState))
3261 return setError(VBOX_E_INVALID_VM_STATE,
3262 tr("Invalid machine state: %s"),
3263 Global::stringifyMachineState(mData->mMachineState));
3264
3265 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
3266 aControllerName,
3267 aControllerPort,
3268 aDevice);
3269 if (!pAttach)
3270 return setError(VBOX_E_OBJECT_NOT_FOUND,
3271 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3272 aDevice, aControllerPort, aControllerName);
3273
3274
3275 setModified(IsModified_Storage);
3276 mMediaData.backup();
3277
3278 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3279
3280 if (pAttach->getType() != DeviceType_DVD)
3281 return setError(E_INVALIDARG,
3282 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
3283 aDevice, aControllerPort, aControllerName);
3284 pAttach->updatePassthrough(!!aPassthrough);
3285
3286 return S_OK;
3287}
3288
3289STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
3290 LONG aControllerPort,
3291 LONG aDevice,
3292 IN_BSTR aId,
3293 BOOL aForce)
3294{
3295 int rc = S_OK;
3296 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aForce=%d\n",
3297 aControllerName, aControllerPort, aDevice, aForce));
3298
3299 CheckComArgStrNotEmptyOrNull(aControllerName);
3300
3301 AutoCaller autoCaller(this);
3302 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3303
3304 // we're calling host methods for getting DVD and floppy drives so lock host first
3305 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3306
3307 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
3308 aControllerName,
3309 aControllerPort,
3310 aDevice);
3311 if (pAttach.isNull())
3312 return setError(VBOX_E_OBJECT_NOT_FOUND,
3313 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
3314 aDevice, aControllerPort, aControllerName);
3315
3316 /* Remember previously mounted medium. The medium before taking the
3317 * backup is not necessarily the same thing. */
3318 ComObjPtr<Medium> oldmedium;
3319 oldmedium = pAttach->getMedium();
3320
3321 Guid uuid(aId);
3322 ComObjPtr<Medium> medium;
3323 DeviceType_T mediumType = pAttach->getType();
3324 switch (mediumType)
3325 {
3326 case DeviceType_DVD:
3327 if (!uuid.isEmpty())
3328 {
3329 /* find a DVD by host device UUID */
3330 MediaList llHostDVDDrives;
3331 rc = mParent->host()->getDVDDrives(llHostDVDDrives);
3332 if (SUCCEEDED(rc))
3333 {
3334 for (MediaList::iterator it = llHostDVDDrives.begin();
3335 it != llHostDVDDrives.end();
3336 ++it)
3337 {
3338 ComObjPtr<Medium> &p = *it;
3339 if (uuid == p->getId())
3340 {
3341 medium = p;
3342 break;
3343 }
3344 }
3345 }
3346 /* find a DVD by UUID */
3347 if (medium.isNull())
3348 rc = mParent->findDVDImage(&uuid, NULL, true /* aDoSetError */, &medium);
3349 }
3350 if (FAILED(rc)) return rc;
3351 break;
3352 case DeviceType_Floppy:
3353 if (!uuid.isEmpty())
3354 {
3355 /* find a Floppy by host device UUID */
3356 MediaList llHostFloppyDrives;
3357 rc = mParent->host()->getFloppyDrives(llHostFloppyDrives);
3358 if (SUCCEEDED(rc))
3359 {
3360 for (MediaList::iterator it = llHostFloppyDrives.begin();
3361 it != llHostFloppyDrives.end();
3362 ++it)
3363 {
3364 ComObjPtr<Medium> &p = *it;
3365 if (uuid == p->getId())
3366 {
3367 medium = p;
3368 break;
3369 }
3370 }
3371 }
3372 /* find a Floppy by UUID */
3373 if (medium.isNull())
3374 rc = mParent->findFloppyImage(&uuid, NULL, true /* aDoSetError */, &medium);
3375 }
3376 if (FAILED(rc)) return rc;
3377 break;
3378 default:
3379 return setError(VBOX_E_INVALID_OBJECT_STATE,
3380 tr("Cannot change medium attached to device slot %d on port %d of controller '%ls'"),
3381 aDevice, aControllerPort, aControllerName);
3382 }
3383
3384 if (SUCCEEDED(rc))
3385 {
3386 setModified(IsModified_Storage);
3387 mMediaData.backup();
3388
3389 /* The backup operation makes the pAttach reference point to the
3390 * old settings. Re-get the correct reference. */
3391 pAttach = findAttachment(mMediaData->mAttachments,
3392 aControllerName,
3393 aControllerPort,
3394 aDevice);
3395 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3396 /* For non-hard disk media, detach straight away. */
3397 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
3398 oldmedium->detachFrom(mData->mUuid);
3399 if (!medium.isNull())
3400 medium->attachTo(mData->mUuid);
3401 pAttach->updateMedium(medium, false /* aImplicit */);
3402 setModified(IsModified_Storage);
3403 }
3404
3405 alock.leave();
3406 rc = onMediumChange(pAttach, aForce);
3407 alock.enter();
3408
3409 /* On error roll back this change only. */
3410 if (FAILED(rc))
3411 {
3412 if (!medium.isNull())
3413 medium->detachFrom(mData->mUuid);
3414 pAttach = findAttachment(mMediaData->mAttachments,
3415 aControllerName,
3416 aControllerPort,
3417 aDevice);
3418 /* If the attachment is gone in the mean time, bail out. */
3419 if (pAttach.isNull())
3420 return rc;
3421 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
3422 /* For non-hard disk media, re-attach straight away. */
3423 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull())
3424 oldmedium->attachTo(mData->mUuid);
3425 pAttach->updateMedium(oldmedium, false /* aImplicit */);
3426 }
3427
3428 return rc;
3429}
3430
3431STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
3432 LONG aControllerPort,
3433 LONG aDevice,
3434 IMedium **aMedium)
3435{
3436 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
3437 aControllerName, aControllerPort, aDevice));
3438
3439 CheckComArgStrNotEmptyOrNull(aControllerName);
3440 CheckComArgOutPointerValid(aMedium);
3441
3442 AutoCaller autoCaller(this);
3443 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3444
3445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3446
3447 *aMedium = NULL;
3448
3449 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
3450 aControllerName,
3451 aControllerPort,
3452 aDevice);
3453 if (pAttach.isNull())
3454 return setError(VBOX_E_OBJECT_NOT_FOUND,
3455 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
3456 aDevice, aControllerPort, aControllerName);
3457
3458 pAttach->getMedium().queryInterfaceTo(aMedium);
3459
3460 return S_OK;
3461}
3462
3463STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
3464{
3465 CheckComArgOutPointerValid(port);
3466 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
3467
3468 AutoCaller autoCaller(this);
3469 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3470
3471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3472
3473 mSerialPorts[slot].queryInterfaceTo(port);
3474
3475 return S_OK;
3476}
3477
3478STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
3479{
3480 CheckComArgOutPointerValid(port);
3481 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
3482
3483 AutoCaller autoCaller(this);
3484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3485
3486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3487
3488 mParallelPorts[slot].queryInterfaceTo(port);
3489
3490 return S_OK;
3491}
3492
3493STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
3494{
3495 CheckComArgOutPointerValid(adapter);
3496 CheckComArgExpr(slot, slot < RT_ELEMENTS(mNetworkAdapters));
3497
3498 AutoCaller autoCaller(this);
3499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3500
3501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3502
3503 mNetworkAdapters[slot].queryInterfaceTo(adapter);
3504
3505 return S_OK;
3506}
3507
3508STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
3509{
3510 if (ComSafeArrayOutIsNull(aKeys))
3511 return E_POINTER;
3512
3513 AutoCaller autoCaller(this);
3514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3515
3516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3517
3518 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
3519 int i = 0;
3520 for (settings::ExtraDataItemsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
3521 it != mData->pMachineConfigFile->mapExtraDataItems.end();
3522 ++it, ++i)
3523 {
3524 const Utf8Str &strKey = it->first;
3525 strKey.cloneTo(&saKeys[i]);
3526 }
3527 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
3528
3529 return S_OK;
3530 }
3531
3532 /**
3533 * @note Locks this object for reading.
3534 */
3535STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
3536 BSTR *aValue)
3537{
3538 CheckComArgStrNotEmptyOrNull(aKey);
3539 CheckComArgOutPointerValid(aValue);
3540
3541 AutoCaller autoCaller(this);
3542 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3543
3544 /* start with nothing found */
3545 Bstr bstrResult("");
3546
3547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3548
3549 settings::ExtraDataItemsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
3550 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
3551 // found:
3552 bstrResult = it->second; // source is a Utf8Str
3553
3554 /* return the result to caller (may be empty) */
3555 bstrResult.cloneTo(aValue);
3556
3557 return S_OK;
3558}
3559
3560 /**
3561 * @note Locks mParent for writing + this object for writing.
3562 */
3563STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
3564{
3565 CheckComArgStrNotEmptyOrNull(aKey);
3566
3567 AutoCaller autoCaller(this);
3568 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3569
3570 Utf8Str strKey(aKey);
3571 Utf8Str strValue(aValue);
3572 Utf8Str strOldValue; // empty
3573
3574 // locking note: we only hold the read lock briefly to look up the old value,
3575 // then release it and call the onExtraCanChange callbacks. There is a small
3576 // chance of a race insofar as the callback might be called twice if two callers
3577 // change the same key at the same time, but that's a much better solution
3578 // than the deadlock we had here before. The actual changing of the extradata
3579 // is then performed under the write lock and race-free.
3580
3581 // look up the old value first; if nothing's changed then we need not do anything
3582 {
3583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
3584 settings::ExtraDataItemsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
3585 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
3586 strOldValue = it->second;
3587 }
3588
3589 bool fChanged;
3590 if ((fChanged = (strOldValue != strValue)))
3591 {
3592 // ask for permission from all listeners outside the locks;
3593 // onExtraDataCanChange() only briefly requests the VirtualBox
3594 // lock to copy the list of callbacks to invoke
3595 Bstr error;
3596 Bstr bstrValue(aValue);
3597
3598 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue, error))
3599 {
3600 const char *sep = error.isEmpty() ? "" : ": ";
3601 CBSTR err = error.raw();
3602 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
3603 sep, err));
3604 return setError(E_ACCESSDENIED,
3605 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
3606 aKey,
3607 bstrValue.raw(),
3608 sep,
3609 err);
3610 }
3611
3612 // data is changing and change not vetoed: then write it out under the lock
3613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3614
3615 if (getClassID() == clsidSnapshotMachine)
3616 {
3617 HRESULT rc = checkStateDependency(MutableStateDep);
3618 if (FAILED(rc)) return rc;
3619 }
3620
3621 if (strValue.isEmpty())
3622 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
3623 else
3624 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
3625 // creates a new key if needed
3626
3627 bool fNeedsGlobalSaveSettings = false;
3628 saveSettings(&fNeedsGlobalSaveSettings);
3629
3630 if (fNeedsGlobalSaveSettings)
3631 {
3632 alock.release();
3633 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
3634 mParent->saveSettings();
3635 }
3636 }
3637
3638 // fire notification outside the lock
3639 if (fChanged)
3640 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
3641
3642 return S_OK;
3643}
3644
3645STDMETHODIMP Machine::SaveSettings()
3646{
3647 AutoCaller autoCaller(this);
3648 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3649
3650 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
3651
3652 /* when there was auto-conversion, we want to save the file even if
3653 * the VM is saved */
3654 HRESULT rc = checkStateDependency(MutableStateDep);
3655 if (FAILED(rc)) return rc;
3656
3657 /* the settings file path may never be null */
3658 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
3659
3660 /* save all VM data excluding snapshots */
3661 bool fNeedsGlobalSaveSettings = false;
3662 rc = saveSettings(&fNeedsGlobalSaveSettings);
3663 mlock.release();
3664
3665 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
3666 {
3667 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
3668 rc = mParent->saveSettings();
3669 }
3670
3671 return rc;
3672}
3673
3674STDMETHODIMP Machine::DiscardSettings()
3675{
3676 AutoCaller autoCaller(this);
3677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3678
3679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3680
3681 HRESULT rc = checkStateDependency(MutableStateDep);
3682 if (FAILED(rc)) return rc;
3683
3684 /*
3685 * during this rollback, the session will be notified if data has
3686 * been actually changed
3687 */
3688 rollback(true /* aNotify */);
3689
3690 return S_OK;
3691}
3692
3693STDMETHODIMP Machine::DeleteSettings()
3694{
3695 AutoCaller autoCaller(this);
3696 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3697
3698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3699
3700 HRESULT rc = checkStateDependency(MutableStateDep);
3701 if (FAILED(rc)) return rc;
3702
3703 if (mData->mRegistered)
3704 return setError(VBOX_E_INVALID_VM_STATE,
3705 tr("Cannot delete settings of a registered machine"));
3706
3707 ULONG uLogHistoryCount = 3;
3708 ComPtr<ISystemProperties> systemProperties;
3709 mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3710 if (!systemProperties.isNull())
3711 systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
3712
3713 /* delete the settings only when the file actually exists */
3714 if (mData->pMachineConfigFile->fileExists())
3715 {
3716 int vrc = RTFileDelete(mData->m_strConfigFileFull.c_str());
3717 if (RT_FAILURE(vrc))
3718 return setError(VBOX_E_IPRT_ERROR,
3719 tr("Could not delete the settings file '%s' (%Rrc)"),
3720 mData->m_strConfigFileFull.raw(),
3721 vrc);
3722
3723 /* Delete any backup or uncommitted XML files. Ignore failures.
3724 See the fSafe parameter of xml::XmlFileWriter::write for details. */
3725 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
3726 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
3727 RTFileDelete(otherXml.c_str());
3728 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
3729 RTFileDelete(otherXml.c_str());
3730
3731 /* delete the Logs folder, nothing important should be left
3732 * there (we don't check for errors because the user might have
3733 * some private files there that we don't want to delete) */
3734 Utf8Str logFolder;
3735 getLogFolder(logFolder);
3736 Assert(logFolder.length());
3737 if (RTDirExists(logFolder.c_str()))
3738 {
3739 /* Delete all VBox.log[.N] files from the Logs folder
3740 * (this must be in sync with the rotation logic in
3741 * Console::powerUpThread()). Also, delete the VBox.png[.N]
3742 * files that may have been created by the GUI. */
3743 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
3744 logFolder.raw(), RTPATH_DELIMITER);
3745 RTFileDelete(log.c_str());
3746 log = Utf8StrFmt("%s%cVBox.png",
3747 logFolder.raw(), RTPATH_DELIMITER);
3748 RTFileDelete(log.c_str());
3749 for (int i = uLogHistoryCount; i > 0; i--)
3750 {
3751 log = Utf8StrFmt("%s%cVBox.log.%d",
3752 logFolder.raw(), RTPATH_DELIMITER, i);
3753 RTFileDelete(log.c_str());
3754 log = Utf8StrFmt("%s%cVBox.png.%d",
3755 logFolder.raw(), RTPATH_DELIMITER, i);
3756 RTFileDelete(log.c_str());
3757 }
3758
3759 RTDirRemove(logFolder.c_str());
3760 }
3761
3762 /* delete the Snapshots folder, nothing important should be left
3763 * there (we don't check for errors because the user might have
3764 * some private files there that we don't want to delete) */
3765 Utf8Str snapshotFolder(mUserData->mSnapshotFolderFull);
3766 Assert(snapshotFolder.length());
3767 if (RTDirExists(snapshotFolder.c_str()))
3768 RTDirRemove(snapshotFolder.c_str());
3769
3770 /* delete the directory that contains the settings file, but only
3771 * if it matches the VM name (i.e. a structure created by default in
3772 * prepareSaveSettings()) */
3773 {
3774 Utf8Str settingsDir;
3775 if (isInOwnDir(&settingsDir))
3776 RTDirRemove(settingsDir.c_str());
3777 }
3778 }
3779
3780 return S_OK;
3781}
3782
3783STDMETHODIMP Machine::GetSnapshot(IN_BSTR aId, ISnapshot **aSnapshot)
3784{
3785 CheckComArgOutPointerValid(aSnapshot);
3786
3787 AutoCaller autoCaller(this);
3788 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3789
3790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3791
3792 Guid uuid(aId);
3793 /* Todo: fix this properly by perhaps introducing an isValid method for the Guid class */
3794 if ( (aId)
3795 && (*aId != '\0') // an empty Bstr means "get root snapshot", so don't fail on that
3796 && (uuid.isEmpty()))
3797 {
3798 RTUUID uuidTemp;
3799 /* Either it's a null UUID or the conversion failed. (null uuid has a special meaning in findSnapshot) */
3800 if (RT_FAILURE(RTUuidFromUtf16(&uuidTemp, aId)))
3801 return setError(E_FAIL,
3802 tr("Could not find a snapshot with UUID {%ls}"),
3803 aId);
3804 }
3805
3806 ComObjPtr<Snapshot> snapshot;
3807
3808 HRESULT rc = findSnapshot(uuid, snapshot, true /* aSetError */);
3809 snapshot.queryInterfaceTo(aSnapshot);
3810
3811 return rc;
3812}
3813
3814STDMETHODIMP Machine::FindSnapshot(IN_BSTR aName, ISnapshot **aSnapshot)
3815{
3816 CheckComArgStrNotEmptyOrNull(aName);
3817 CheckComArgOutPointerValid(aSnapshot);
3818
3819 AutoCaller autoCaller(this);
3820 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3821
3822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3823
3824 ComObjPtr<Snapshot> snapshot;
3825
3826 HRESULT rc = findSnapshot(aName, snapshot, true /* aSetError */);
3827 snapshot.queryInterfaceTo(aSnapshot);
3828
3829 return rc;
3830}
3831
3832STDMETHODIMP Machine::SetCurrentSnapshot(IN_BSTR /* aId */)
3833{
3834 /// @todo (dmik) don't forget to set
3835 // mData->mCurrentStateModified to FALSE
3836
3837 return setError(E_NOTIMPL, "Not implemented");
3838}
3839
3840STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable)
3841{
3842 CheckComArgStrNotEmptyOrNull(aName);
3843 CheckComArgStrNotEmptyOrNull(aHostPath);
3844
3845 AutoCaller autoCaller(this);
3846 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3847
3848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3849
3850 HRESULT rc = checkStateDependency(MutableStateDep);
3851 if (FAILED(rc)) return rc;
3852
3853 ComObjPtr<SharedFolder> sharedFolder;
3854 rc = findSharedFolder(aName, sharedFolder, false /* aSetError */);
3855 if (SUCCEEDED(rc))
3856 return setError(VBOX_E_OBJECT_IN_USE,
3857 tr("Shared folder named '%ls' already exists"),
3858 aName);
3859
3860 sharedFolder.createObject();
3861 rc = sharedFolder->init(getMachine(), aName, aHostPath, aWritable);
3862 if (FAILED(rc)) return rc;
3863
3864 setModified(IsModified_SharedFolders);
3865 mHWData.backup();
3866 mHWData->mSharedFolders.push_back(sharedFolder);
3867
3868 /* inform the direct session if any */
3869 alock.leave();
3870 onSharedFolderChange();
3871
3872 return S_OK;
3873}
3874
3875STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
3876{
3877 CheckComArgStrNotEmptyOrNull(aName);
3878
3879 AutoCaller autoCaller(this);
3880 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3881
3882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3883
3884 HRESULT rc = checkStateDependency(MutableStateDep);
3885 if (FAILED(rc)) return rc;
3886
3887 ComObjPtr<SharedFolder> sharedFolder;
3888 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
3889 if (FAILED(rc)) return rc;
3890
3891 setModified(IsModified_SharedFolders);
3892 mHWData.backup();
3893 mHWData->mSharedFolders.remove(sharedFolder);
3894
3895 /* inform the direct session if any */
3896 alock.leave();
3897 onSharedFolderChange();
3898
3899 return S_OK;
3900}
3901
3902STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
3903{
3904 CheckComArgOutPointerValid(aCanShow);
3905
3906 /* start with No */
3907 *aCanShow = FALSE;
3908
3909 AutoCaller autoCaller(this);
3910 AssertComRCReturnRC(autoCaller.rc());
3911
3912 ComPtr<IInternalSessionControl> directControl;
3913 {
3914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3915
3916 if (mData->mSession.mState != SessionState_Open)
3917 return setError(VBOX_E_INVALID_VM_STATE,
3918 tr("Machine session is not open (session state: %s)"),
3919 Global::stringifySessionState(mData->mSession.mState));
3920
3921 directControl = mData->mSession.mDirectControl;
3922 }
3923
3924 /* ignore calls made after #OnSessionEnd() is called */
3925 if (!directControl)
3926 return S_OK;
3927
3928 ULONG64 dummy;
3929 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
3930}
3931
3932STDMETHODIMP Machine::ShowConsoleWindow(ULONG64 *aWinId)
3933{
3934 CheckComArgOutPointerValid(aWinId);
3935
3936 AutoCaller autoCaller(this);
3937 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
3938
3939 ComPtr<IInternalSessionControl> directControl;
3940 {
3941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3942
3943 if (mData->mSession.mState != SessionState_Open)
3944 return setError(E_FAIL,
3945 tr("Machine session is not open (session state: %s)"),
3946 Global::stringifySessionState(mData->mSession.mState));
3947
3948 directControl = mData->mSession.mDirectControl;
3949 }
3950
3951 /* ignore calls made after #OnSessionEnd() is called */
3952 if (!directControl)
3953 return S_OK;
3954
3955 BOOL dummy;
3956 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
3957}
3958
3959#ifdef VBOX_WITH_GUEST_PROPS
3960/**
3961 * Look up a guest property in VBoxSVC's internal structures.
3962 */
3963HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
3964 BSTR *aValue,
3965 ULONG64 *aTimestamp,
3966 BSTR *aFlags) const
3967{
3968 using namespace guestProp;
3969
3970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3971 Utf8Str strName(aName);
3972 HWData::GuestPropertyList::const_iterator it;
3973
3974 for (it = mHWData->mGuestProperties.begin();
3975 it != mHWData->mGuestProperties.end(); ++it)
3976 {
3977 if (it->strName == strName)
3978 {
3979 char szFlags[MAX_FLAGS_LEN + 1];
3980 it->strValue.cloneTo(aValue);
3981 *aTimestamp = it->mTimestamp;
3982 writeFlags(it->mFlags, szFlags);
3983 Bstr(szFlags).cloneTo(aFlags);
3984 break;
3985 }
3986 }
3987 return S_OK;
3988}
3989
3990/**
3991 * Query the VM that a guest property belongs to for the property.
3992 * @returns E_ACCESSDENIED if the VM process is not available or not
3993 * currently handling queries and the lookup should then be done in
3994 * VBoxSVC.
3995 */
3996HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
3997 BSTR *aValue,
3998 ULONG64 *aTimestamp,
3999 BSTR *aFlags) const
4000{
4001 HRESULT rc;
4002 ComPtr<IInternalSessionControl> directControl;
4003 directControl = mData->mSession.mDirectControl;
4004
4005 /* fail if we were called after #OnSessionEnd() is called. This is a
4006 * silly race condition. */
4007
4008 if (!directControl)
4009 rc = E_ACCESSDENIED;
4010 else
4011 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
4012 false /* isSetter */,
4013 aValue, aTimestamp, aFlags);
4014 return rc;
4015}
4016#endif // VBOX_WITH_GUEST_PROPS
4017
4018STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
4019 BSTR *aValue,
4020 ULONG64 *aTimestamp,
4021 BSTR *aFlags)
4022{
4023#ifndef VBOX_WITH_GUEST_PROPS
4024 ReturnComNotImplemented();
4025#else // VBOX_WITH_GUEST_PROPS
4026 CheckComArgStrNotEmptyOrNull(aName);
4027 CheckComArgOutPointerValid(aValue);
4028 CheckComArgOutPointerValid(aTimestamp);
4029 CheckComArgOutPointerValid(aFlags);
4030
4031 AutoCaller autoCaller(this);
4032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4033
4034 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
4035 if (rc == E_ACCESSDENIED)
4036 /* The VM is not running or the service is not (yet) accessible */
4037 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
4038 return rc;
4039#endif // VBOX_WITH_GUEST_PROPS
4040}
4041
4042STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
4043{
4044 ULONG64 dummyTimestamp;
4045 BSTR dummyFlags;
4046 return GetGuestProperty(aName, aValue, &dummyTimestamp, &dummyFlags);
4047}
4048
4049STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, ULONG64 *aTimestamp)
4050{
4051 BSTR dummyValue;
4052 BSTR dummyFlags;
4053 return GetGuestProperty(aName, &dummyValue, aTimestamp, &dummyFlags);
4054}
4055
4056#ifdef VBOX_WITH_GUEST_PROPS
4057/**
4058 * Set a guest property in VBoxSVC's internal structures.
4059 */
4060HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
4061 IN_BSTR aFlags)
4062{
4063 using namespace guestProp;
4064
4065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4066 HRESULT rc = S_OK;
4067 HWData::GuestProperty property;
4068 property.mFlags = NILFLAG;
4069 bool found = false;
4070
4071 rc = checkStateDependency(MutableStateDep);
4072 if (FAILED(rc)) return rc;
4073
4074 try
4075 {
4076 Utf8Str utf8Name(aName);
4077 Utf8Str utf8Flags(aFlags);
4078 uint32_t fFlags = NILFLAG;
4079 if ( (aFlags != NULL)
4080 && RT_FAILURE(validateFlags(utf8Flags.raw(), &fFlags))
4081 )
4082 return setError(E_INVALIDARG,
4083 tr("Invalid flag values: '%ls'"),
4084 aFlags);
4085
4086 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
4087 * know, this is simple and do an OK job atm.) */
4088 HWData::GuestPropertyList::iterator it;
4089 for (it = mHWData->mGuestProperties.begin();
4090 it != mHWData->mGuestProperties.end(); ++it)
4091 if (it->strName == utf8Name)
4092 {
4093 property = *it;
4094 if (it->mFlags & (RDONLYHOST))
4095 rc = setError(E_ACCESSDENIED,
4096 tr("The property '%ls' cannot be changed by the host"),
4097 aName);
4098 else
4099 {
4100 setModified(IsModified_MachineData);
4101 mHWData.backup(); // @todo r=dj backup in a loop?!?
4102
4103 /* The backup() operation invalidates our iterator, so
4104 * get a new one. */
4105 for (it = mHWData->mGuestProperties.begin();
4106 it->strName != utf8Name;
4107 ++it)
4108 ;
4109 mHWData->mGuestProperties.erase(it);
4110 }
4111 found = true;
4112 break;
4113 }
4114 if (found && SUCCEEDED(rc))
4115 {
4116 if (*aValue)
4117 {
4118 RTTIMESPEC time;
4119 property.strValue = aValue;
4120 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
4121 if (aFlags != NULL)
4122 property.mFlags = fFlags;
4123 mHWData->mGuestProperties.push_back(property);
4124 }
4125 }
4126 else if (SUCCEEDED(rc) && *aValue)
4127 {
4128 RTTIMESPEC time;
4129 setModified(IsModified_MachineData);
4130 mHWData.backup();
4131 property.strName = aName;
4132 property.strValue = aValue;
4133 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
4134 property.mFlags = fFlags;
4135 mHWData->mGuestProperties.push_back(property);
4136 }
4137 if ( SUCCEEDED(rc)
4138 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
4139 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.raw(), RTSTR_MAX,
4140 utf8Name.raw(), RTSTR_MAX, NULL) )
4141 )
4142 {
4143 /** @todo r=bird: Why aren't we leaving the lock here? The
4144 * same code in PushGuestProperty does... */
4145 mParent->onGuestPropertyChange(mData->mUuid, aName, aValue, aFlags);
4146 }
4147 }
4148 catch (std::bad_alloc &)
4149 {
4150 rc = E_OUTOFMEMORY;
4151 }
4152
4153 return rc;
4154}
4155
4156/**
4157 * Set a property on the VM that that property belongs to.
4158 * @returns E_ACCESSDENIED if the VM process is not available or not
4159 * currently handling queries and the setting should then be done in
4160 * VBoxSVC.
4161 */
4162HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
4163 IN_BSTR aFlags)
4164{
4165 HRESULT rc;
4166
4167 try {
4168 ComPtr<IInternalSessionControl> directControl =
4169 mData->mSession.mDirectControl;
4170
4171 BSTR dummy = NULL;
4172 ULONG64 dummy64;
4173 if (!directControl)
4174 rc = E_ACCESSDENIED;
4175 else
4176 rc = directControl->AccessGuestProperty
4177 (aName,
4178 /** @todo Fix when adding DeleteGuestProperty(),
4179 see defect. */
4180 *aValue ? aValue : NULL, aFlags, true /* isSetter */,
4181 &dummy, &dummy64, &dummy);
4182 }
4183 catch (std::bad_alloc &)
4184 {
4185 rc = E_OUTOFMEMORY;
4186 }
4187
4188 return rc;
4189}
4190#endif // VBOX_WITH_GUEST_PROPS
4191
4192STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
4193 IN_BSTR aFlags)
4194{
4195#ifndef VBOX_WITH_GUEST_PROPS
4196 ReturnComNotImplemented();
4197#else // VBOX_WITH_GUEST_PROPS
4198 CheckComArgStrNotEmptyOrNull(aName);
4199 if ((aFlags != NULL) && !VALID_PTR(aFlags))
4200 return E_INVALIDARG;
4201 AutoCaller autoCaller(this);
4202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4203
4204 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
4205 if (rc == E_ACCESSDENIED)
4206 /* The VM is not running or the service is not (yet) accessible */
4207 rc = setGuestPropertyToService(aName, aValue, aFlags);
4208 return rc;
4209#endif // VBOX_WITH_GUEST_PROPS
4210}
4211
4212STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
4213{
4214 return SetGuestProperty(aName, aValue, NULL);
4215}
4216
4217#ifdef VBOX_WITH_GUEST_PROPS
4218/**
4219 * Enumerate the guest properties in VBoxSVC's internal structures.
4220 */
4221HRESULT Machine::enumerateGuestPropertiesInService
4222 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
4223 ComSafeArrayOut(BSTR, aValues),
4224 ComSafeArrayOut(ULONG64, aTimestamps),
4225 ComSafeArrayOut(BSTR, aFlags))
4226{
4227 using namespace guestProp;
4228
4229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4230 Utf8Str strPatterns(aPatterns);
4231
4232 /*
4233 * Look for matching patterns and build up a list.
4234 */
4235 HWData::GuestPropertyList propList;
4236 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
4237 it != mHWData->mGuestProperties.end();
4238 ++it)
4239 if ( strPatterns.isEmpty()
4240 || RTStrSimplePatternMultiMatch(strPatterns.raw(),
4241 RTSTR_MAX,
4242 it->strName.raw(),
4243 RTSTR_MAX, NULL)
4244 )
4245 propList.push_back(*it);
4246
4247 /*
4248 * And build up the arrays for returning the property information.
4249 */
4250 size_t cEntries = propList.size();
4251 SafeArray<BSTR> names(cEntries);
4252 SafeArray<BSTR> values(cEntries);
4253 SafeArray<ULONG64> timestamps(cEntries);
4254 SafeArray<BSTR> flags(cEntries);
4255 size_t iProp = 0;
4256 for (HWData::GuestPropertyList::iterator it = propList.begin();
4257 it != propList.end();
4258 ++it)
4259 {
4260 char szFlags[MAX_FLAGS_LEN + 1];
4261 it->strName.cloneTo(&names[iProp]);
4262 it->strValue.cloneTo(&values[iProp]);
4263 timestamps[iProp] = it->mTimestamp;
4264 writeFlags(it->mFlags, szFlags);
4265 Bstr(szFlags).cloneTo(&flags[iProp]);
4266 ++iProp;
4267 }
4268 names.detachTo(ComSafeArrayOutArg(aNames));
4269 values.detachTo(ComSafeArrayOutArg(aValues));
4270 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
4271 flags.detachTo(ComSafeArrayOutArg(aFlags));
4272 return S_OK;
4273}
4274
4275/**
4276 * Enumerate the properties managed by a VM.
4277 * @returns E_ACCESSDENIED if the VM process is not available or not
4278 * currently handling queries and the setting should then be done in
4279 * VBoxSVC.
4280 */
4281HRESULT Machine::enumerateGuestPropertiesOnVM
4282 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
4283 ComSafeArrayOut(BSTR, aValues),
4284 ComSafeArrayOut(ULONG64, aTimestamps),
4285 ComSafeArrayOut(BSTR, aFlags))
4286{
4287 HRESULT rc;
4288 ComPtr<IInternalSessionControl> directControl;
4289 directControl = mData->mSession.mDirectControl;
4290
4291 if (!directControl)
4292 rc = E_ACCESSDENIED;
4293 else
4294 rc = directControl->EnumerateGuestProperties
4295 (aPatterns, ComSafeArrayOutArg(aNames),
4296 ComSafeArrayOutArg(aValues),
4297 ComSafeArrayOutArg(aTimestamps),
4298 ComSafeArrayOutArg(aFlags));
4299 return rc;
4300}
4301#endif // VBOX_WITH_GUEST_PROPS
4302
4303STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
4304 ComSafeArrayOut(BSTR, aNames),
4305 ComSafeArrayOut(BSTR, aValues),
4306 ComSafeArrayOut(ULONG64, aTimestamps),
4307 ComSafeArrayOut(BSTR, aFlags))
4308{
4309#ifndef VBOX_WITH_GUEST_PROPS
4310 ReturnComNotImplemented();
4311#else // VBOX_WITH_GUEST_PROPS
4312 if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
4313 return E_POINTER;
4314
4315 CheckComArgOutSafeArrayPointerValid(aNames);
4316 CheckComArgOutSafeArrayPointerValid(aValues);
4317 CheckComArgOutSafeArrayPointerValid(aTimestamps);
4318 CheckComArgOutSafeArrayPointerValid(aFlags);
4319
4320 AutoCaller autoCaller(this);
4321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4322
4323 HRESULT rc = enumerateGuestPropertiesOnVM
4324 (aPatterns, ComSafeArrayOutArg(aNames),
4325 ComSafeArrayOutArg(aValues),
4326 ComSafeArrayOutArg(aTimestamps),
4327 ComSafeArrayOutArg(aFlags));
4328 if (rc == E_ACCESSDENIED)
4329 /* The VM is not running or the service is not (yet) accessible */
4330 rc = enumerateGuestPropertiesInService
4331 (aPatterns, ComSafeArrayOutArg(aNames),
4332 ComSafeArrayOutArg(aValues),
4333 ComSafeArrayOutArg(aTimestamps),
4334 ComSafeArrayOutArg(aFlags));
4335 return rc;
4336#endif // VBOX_WITH_GUEST_PROPS
4337}
4338
4339STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
4340 ComSafeArrayOut(IMediumAttachment*, aAttachments))
4341{
4342 MediaData::AttachmentList atts;
4343
4344 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
4345 if (FAILED(rc)) return rc;
4346
4347 SafeIfaceArray<IMediumAttachment> attachments(atts);
4348 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
4349
4350 return S_OK;
4351}
4352
4353STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
4354 LONG aControllerPort,
4355 LONG aDevice,
4356 IMediumAttachment **aAttachment)
4357{
4358 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4359 aControllerName, aControllerPort, aDevice));
4360
4361 CheckComArgStrNotEmptyOrNull(aControllerName);
4362 CheckComArgOutPointerValid(aAttachment);
4363
4364 AutoCaller autoCaller(this);
4365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4366
4367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4368
4369 *aAttachment = NULL;
4370
4371 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4372 aControllerName,
4373 aControllerPort,
4374 aDevice);
4375 if (pAttach.isNull())
4376 return setError(VBOX_E_OBJECT_NOT_FOUND,
4377 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4378 aDevice, aControllerPort, aControllerName);
4379
4380 pAttach.queryInterfaceTo(aAttachment);
4381
4382 return S_OK;
4383}
4384
4385STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
4386 StorageBus_T aConnectionType,
4387 IStorageController **controller)
4388{
4389 CheckComArgStrNotEmptyOrNull(aName);
4390
4391 if ( (aConnectionType <= StorageBus_Null)
4392 || (aConnectionType > StorageBus_SAS))
4393 return setError(E_INVALIDARG,
4394 tr("Invalid connection type: %d"),
4395 aConnectionType);
4396
4397 AutoCaller autoCaller(this);
4398 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4399
4400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4401
4402 HRESULT rc = checkStateDependency(MutableStateDep);
4403 if (FAILED(rc)) return rc;
4404
4405 /* try to find one with the name first. */
4406 ComObjPtr<StorageController> ctrl;
4407
4408 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
4409 if (SUCCEEDED(rc))
4410 return setError(VBOX_E_OBJECT_IN_USE,
4411 tr("Storage controller named '%ls' already exists"),
4412 aName);
4413
4414 ctrl.createObject();
4415
4416 /* get a new instance number for the storage controller */
4417 ULONG ulInstance = 0;
4418 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
4419 it != mStorageControllers->end();
4420 ++it)
4421 {
4422 if ((*it)->getStorageBus() == aConnectionType)
4423 {
4424 ULONG ulCurInst = (*it)->getInstance();
4425
4426 if (ulCurInst >= ulInstance)
4427 ulInstance = ulCurInst + 1;
4428 }
4429 }
4430
4431 rc = ctrl->init(this, aName, aConnectionType, ulInstance);
4432 if (FAILED(rc)) return rc;
4433
4434 setModified(IsModified_Storage);
4435 mStorageControllers.backup();
4436 mStorageControllers->push_back(ctrl);
4437
4438 ctrl.queryInterfaceTo(controller);
4439
4440 /* inform the direct session if any */
4441 alock.leave();
4442 onStorageControllerChange();
4443
4444 return S_OK;
4445}
4446
4447STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
4448 IStorageController **aStorageController)
4449{
4450 CheckComArgStrNotEmptyOrNull(aName);
4451
4452 AutoCaller autoCaller(this);
4453 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4454
4455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4456
4457 ComObjPtr<StorageController> ctrl;
4458
4459 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
4460 if (SUCCEEDED(rc))
4461 ctrl.queryInterfaceTo(aStorageController);
4462
4463 return rc;
4464}
4465
4466STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
4467 IStorageController **aStorageController)
4468{
4469 AutoCaller autoCaller(this);
4470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4471
4472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4473
4474 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
4475 it != mStorageControllers->end();
4476 ++it)
4477 {
4478 if ((*it)->getInstance() == aInstance)
4479 {
4480 (*it).queryInterfaceTo(aStorageController);
4481 return S_OK;
4482 }
4483 }
4484
4485 return setError(VBOX_E_OBJECT_NOT_FOUND,
4486 tr("Could not find a storage controller with instance number '%lu'"),
4487 aInstance);
4488}
4489
4490STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
4491{
4492 CheckComArgStrNotEmptyOrNull(aName);
4493
4494 AutoCaller autoCaller(this);
4495 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4496
4497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4498
4499 HRESULT rc = checkStateDependency(MutableStateDep);
4500 if (FAILED(rc)) return rc;
4501
4502 ComObjPtr<StorageController> ctrl;
4503 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
4504 if (FAILED(rc)) return rc;
4505
4506 /* We can remove the controller only if there is no device attached. */
4507 /* check if the device slot is already busy */
4508 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
4509 it != mMediaData->mAttachments.end();
4510 ++it)
4511 {
4512 if ((*it)->getControllerName() == aName)
4513 return setError(VBOX_E_OBJECT_IN_USE,
4514 tr("Storage controller named '%ls' has still devices attached"),
4515 aName);
4516 }
4517
4518 /* We can remove it now. */
4519 setModified(IsModified_Storage);
4520 mStorageControllers.backup();
4521
4522 ctrl->unshare();
4523
4524 mStorageControllers->remove(ctrl);
4525
4526 /* inform the direct session if any */
4527 alock.leave();
4528 onStorageControllerChange();
4529
4530 return S_OK;
4531}
4532
4533/* @todo where is the right place for this? */
4534#define sSSMDisplayScreenshotVer 0x00010001
4535
4536static int readSavedDisplayScreenshot(Utf8Str *pStateFilePath, uint32_t u32Type, uint8_t **ppu8Data, uint32_t *pcbData, uint32_t *pu32Width, uint32_t *pu32Height)
4537{
4538 LogFlowFunc(("u32Type = %d [%s]\n", u32Type, pStateFilePath->raw()));
4539
4540 /* @todo cache read data */
4541 if (pStateFilePath->isEmpty())
4542 {
4543 /* No saved state data. */
4544 return VERR_NOT_SUPPORTED;
4545 }
4546
4547 uint8_t *pu8Data = NULL;
4548 uint32_t cbData = 0;
4549 uint32_t u32Width = 0;
4550 uint32_t u32Height = 0;
4551
4552 PSSMHANDLE pSSM;
4553 int vrc = SSMR3Open(pStateFilePath->raw(), 0 /*fFlags*/, &pSSM);
4554 if (RT_SUCCESS(vrc))
4555 {
4556 uint32_t uVersion;
4557 vrc = SSMR3Seek(pSSM, "DisplayScreenshot", 1100 /*iInstance*/, &uVersion);
4558 if (RT_SUCCESS(vrc))
4559 {
4560 if (uVersion == sSSMDisplayScreenshotVer)
4561 {
4562 uint32_t cBlocks;
4563 vrc = SSMR3GetU32(pSSM, &cBlocks);
4564 AssertRCReturn(vrc, vrc);
4565
4566 for (uint32_t i = 0; i < cBlocks; i++)
4567 {
4568 uint32_t cbBlock;
4569 vrc = SSMR3GetU32(pSSM, &cbBlock);
4570 AssertRCBreak(vrc);
4571
4572 uint32_t typeOfBlock;
4573 vrc = SSMR3GetU32(pSSM, &typeOfBlock);
4574 AssertRCBreak(vrc);
4575
4576 LogFlowFunc(("[%d] type %d, size %d bytes\n", i, typeOfBlock, cbBlock));
4577
4578 if (typeOfBlock == u32Type)
4579 {
4580 if (cbBlock > 2 * sizeof(uint32_t))
4581 {
4582 cbData = cbBlock - 2 * sizeof(uint32_t);
4583 pu8Data = (uint8_t *)RTMemAlloc(cbData);
4584 if (pu8Data == NULL)
4585 {
4586 vrc = VERR_NO_MEMORY;
4587 break;
4588 }
4589
4590 vrc = SSMR3GetU32(pSSM, &u32Width);
4591 AssertRCBreak(vrc);
4592 vrc = SSMR3GetU32(pSSM, &u32Height);
4593 AssertRCBreak(vrc);
4594 vrc = SSMR3GetMem(pSSM, pu8Data, cbData);
4595 AssertRCBreak(vrc);
4596 }
4597 else
4598 {
4599 /* No saved state data. */
4600 vrc = VERR_NOT_SUPPORTED;
4601 }
4602
4603 break;
4604 }
4605 else
4606 {
4607 /* displaySSMSaveScreenshot did not write any data, if
4608 * cbBlock was == 2 * sizeof (uint32_t).
4609 */
4610 if (cbBlock > 2 * sizeof (uint32_t))
4611 {
4612 vrc = SSMR3Skip(pSSM, cbBlock);
4613 AssertRCBreak(vrc);
4614 }
4615 }
4616 }
4617 }
4618 else
4619 {
4620 vrc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
4621 }
4622 }
4623
4624 SSMR3Close(pSSM);
4625 }
4626
4627 if (RT_SUCCESS(vrc))
4628 {
4629 if (u32Type == 0 && cbData % 4 != 0)
4630 {
4631 /* Bitmap is 32bpp, so data is invalid. */
4632 vrc = VERR_SSM_UNEXPECTED_DATA;
4633 }
4634 }
4635
4636 if (RT_SUCCESS(vrc))
4637 {
4638 *ppu8Data = pu8Data;
4639 *pcbData = cbData;
4640 *pu32Width = u32Width;
4641 *pu32Height = u32Height;
4642 LogFlowFunc(("cbData %d, u32Width %d, u32Height %d\n", cbData, u32Width, u32Height));
4643 }
4644
4645 LogFlowFunc(("vrc %Rrc\n", vrc));
4646 return vrc;
4647}
4648
4649static void freeSavedDisplayScreenshot(uint8_t *pu8Data)
4650{
4651 /* @todo not necessary when caching is implemented. */
4652 RTMemFree(pu8Data);
4653}
4654
4655STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
4656{
4657 LogFlowThisFunc(("\n"));
4658
4659 CheckComArgNotNull(aSize);
4660 CheckComArgNotNull(aWidth);
4661 CheckComArgNotNull(aHeight);
4662
4663 if (aScreenId != 0)
4664 return E_NOTIMPL;
4665
4666 AutoCaller autoCaller(this);
4667 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4668
4669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4670
4671 uint8_t *pu8Data = NULL;
4672 uint32_t cbData = 0;
4673 uint32_t u32Width = 0;
4674 uint32_t u32Height = 0;
4675
4676 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4677
4678 if (RT_FAILURE(vrc))
4679 return setError(VBOX_E_IPRT_ERROR,
4680 tr("Saved screenshot data is not available (%Rrc)"),
4681 vrc);
4682
4683 *aSize = cbData;
4684 *aWidth = u32Width;
4685 *aHeight = u32Height;
4686
4687 freeSavedDisplayScreenshot(pu8Data);
4688
4689 return S_OK;
4690}
4691
4692STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
4693{
4694 LogFlowThisFunc(("\n"));
4695
4696 CheckComArgNotNull(aWidth);
4697 CheckComArgNotNull(aHeight);
4698 CheckComArgOutSafeArrayPointerValid(aData);
4699
4700 if (aScreenId != 0)
4701 return E_NOTIMPL;
4702
4703 AutoCaller autoCaller(this);
4704 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4705
4706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4707
4708 uint8_t *pu8Data = NULL;
4709 uint32_t cbData = 0;
4710 uint32_t u32Width = 0;
4711 uint32_t u32Height = 0;
4712
4713 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4714
4715 if (RT_FAILURE(vrc))
4716 return setError(VBOX_E_IPRT_ERROR,
4717 tr("Saved screenshot data is not available (%Rrc)"),
4718 vrc);
4719
4720 *aWidth = u32Width;
4721 *aHeight = u32Height;
4722
4723 com::SafeArray<BYTE> bitmap(cbData);
4724 /* Convert pixels to format expected by the API caller. */
4725 if (aBGR)
4726 {
4727 /* [0] B, [1] G, [2] R, [3] A. */
4728 for (unsigned i = 0; i < cbData; i += 4)
4729 {
4730 bitmap[i] = pu8Data[i];
4731 bitmap[i + 1] = pu8Data[i + 1];
4732 bitmap[i + 2] = pu8Data[i + 2];
4733 bitmap[i + 3] = 0xff;
4734 }
4735 }
4736 else
4737 {
4738 /* [0] R, [1] G, [2] B, [3] A. */
4739 for (unsigned i = 0; i < cbData; i += 4)
4740 {
4741 bitmap[i] = pu8Data[i + 2];
4742 bitmap[i + 1] = pu8Data[i + 1];
4743 bitmap[i + 2] = pu8Data[i];
4744 bitmap[i + 3] = 0xff;
4745 }
4746 }
4747 bitmap.detachTo(ComSafeArrayOutArg(aData));
4748
4749 freeSavedDisplayScreenshot(pu8Data);
4750
4751 return S_OK;
4752}
4753
4754STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
4755{
4756 LogFlowThisFunc(("\n"));
4757
4758 CheckComArgNotNull(aSize);
4759 CheckComArgNotNull(aWidth);
4760 CheckComArgNotNull(aHeight);
4761
4762 if (aScreenId != 0)
4763 return E_NOTIMPL;
4764
4765 AutoCaller autoCaller(this);
4766 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4767
4768 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4769
4770 uint8_t *pu8Data = NULL;
4771 uint32_t cbData = 0;
4772 uint32_t u32Width = 0;
4773 uint32_t u32Height = 0;
4774
4775 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4776
4777 if (RT_FAILURE(vrc))
4778 return setError(VBOX_E_IPRT_ERROR,
4779 tr("Saved screenshot data is not available (%Rrc)"),
4780 vrc);
4781
4782 *aSize = cbData;
4783 *aWidth = u32Width;
4784 *aHeight = u32Height;
4785
4786 freeSavedDisplayScreenshot(pu8Data);
4787
4788 return S_OK;
4789}
4790
4791STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
4792{
4793 LogFlowThisFunc(("\n"));
4794
4795 CheckComArgNotNull(aWidth);
4796 CheckComArgNotNull(aHeight);
4797 CheckComArgOutSafeArrayPointerValid(aData);
4798
4799 if (aScreenId != 0)
4800 return E_NOTIMPL;
4801
4802 AutoCaller autoCaller(this);
4803 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4804
4805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4806
4807 uint8_t *pu8Data = NULL;
4808 uint32_t cbData = 0;
4809 uint32_t u32Width = 0;
4810 uint32_t u32Height = 0;
4811
4812 int vrc = readSavedDisplayScreenshot(&mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
4813
4814 if (RT_FAILURE(vrc))
4815 return setError(VBOX_E_IPRT_ERROR,
4816 tr("Saved screenshot data is not available (%Rrc)"),
4817 vrc);
4818
4819 *aWidth = u32Width;
4820 *aHeight = u32Height;
4821
4822 com::SafeArray<BYTE> png(cbData);
4823 for (unsigned i = 0; i < cbData; i++)
4824 png[i] = pu8Data[i];
4825 png.detachTo(ComSafeArrayOutArg(aData));
4826
4827 freeSavedDisplayScreenshot(pu8Data);
4828
4829 return S_OK;
4830}
4831
4832STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
4833{
4834 HRESULT rc = S_OK;
4835 LogFlowThisFunc(("\n"));
4836
4837 AutoCaller autoCaller(this);
4838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4839
4840 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4841
4842 if (!mHWData->mCPUHotPlugEnabled)
4843 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
4844
4845 if (aCpu >= mHWData->mCPUCount)
4846 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
4847
4848 if (mHWData->mCPUAttached[aCpu])
4849 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
4850
4851 alock.leave();
4852 rc = onCPUChange(aCpu, false);
4853 alock.enter();
4854 if (FAILED(rc)) return rc;
4855
4856 setModified(IsModified_MachineData);
4857 mHWData.backup();
4858 mHWData->mCPUAttached[aCpu] = true;
4859
4860 /* Save settings if online */
4861 if (Global::IsOnline(mData->mMachineState))
4862 SaveSettings();
4863
4864 return S_OK;
4865}
4866
4867STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
4868{
4869 HRESULT rc = S_OK;
4870 LogFlowThisFunc(("\n"));
4871
4872 AutoCaller autoCaller(this);
4873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4874
4875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4876
4877 if (!mHWData->mCPUHotPlugEnabled)
4878 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
4879
4880 if (aCpu >= SchemaDefs::MaxCPUCount)
4881 return setError(E_INVALIDARG,
4882 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
4883 SchemaDefs::MaxCPUCount);
4884
4885 if (!mHWData->mCPUAttached[aCpu])
4886 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
4887
4888 /* CPU 0 can't be detached */
4889 if (aCpu == 0)
4890 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
4891
4892 alock.leave();
4893 rc = onCPUChange(aCpu, true);
4894 alock.enter();
4895 if (FAILED(rc)) return rc;
4896
4897 setModified(IsModified_MachineData);
4898 mHWData.backup();
4899 mHWData->mCPUAttached[aCpu] = false;
4900
4901 /* Save settings if online */
4902 if (Global::IsOnline(mData->mMachineState))
4903 SaveSettings();
4904
4905 return S_OK;
4906}
4907
4908STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
4909{
4910 LogFlowThisFunc(("\n"));
4911
4912 CheckComArgNotNull(aCpuAttached);
4913
4914 *aCpuAttached = false;
4915
4916 AutoCaller autoCaller(this);
4917 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4918
4919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4920
4921 /* If hotplug is enabled the CPU is always enabled. */
4922 if (!mHWData->mCPUHotPlugEnabled)
4923 {
4924 if (aCpu < mHWData->mCPUCount)
4925 *aCpuAttached = true;
4926 }
4927 else
4928 {
4929 if (aCpu < SchemaDefs::MaxCPUCount)
4930 *aCpuAttached = mHWData->mCPUAttached[aCpu];
4931 }
4932
4933 return S_OK;
4934}
4935
4936STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
4937{
4938 CheckComArgOutPointerValid(aName);
4939
4940 AutoCaller autoCaller(this);
4941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4942
4943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4944
4945 Utf8Str log = queryLogFilename(aIdx);
4946 if (RTFileExists(log.c_str()))
4947 log.cloneTo(aName);
4948
4949 return S_OK;
4950}
4951
4952STDMETHODIMP Machine::ReadLog(ULONG aIdx, ULONG64 aOffset, ULONG64 aSize, ComSafeArrayOut(BYTE, aData))
4953{
4954 LogFlowThisFunc(("\n"));
4955 CheckComArgOutSafeArrayPointerValid(aData);
4956
4957 AutoCaller autoCaller(this);
4958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4959
4960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4961
4962 HRESULT rc = S_OK;
4963 Utf8Str log = queryLogFilename(aIdx);
4964
4965 /* do not unnecessarily hold the lock while doing something which does
4966 * not need the lock and potentially takes a long time. */
4967 alock.release();
4968
4969 size_t cbData = (size_t)RT_MIN(aSize, 2048);
4970 com::SafeArray<BYTE> logData(cbData);
4971
4972 RTFILE LogFile;
4973 int vrc = RTFileOpen(&LogFile, log.raw(),
4974 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
4975 if (RT_SUCCESS(vrc))
4976 {
4977 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
4978 if (RT_SUCCESS(vrc))
4979 logData.resize(cbData);
4980 else
4981 rc = setError(VBOX_E_IPRT_ERROR,
4982 tr("Could not read log file '%s' (%Rrc)"),
4983 log.raw(), vrc);
4984 }
4985 else
4986 rc = setError(VBOX_E_IPRT_ERROR,
4987 tr("Could not open log file '%s' (%Rrc)"),
4988 log.raw(), vrc);
4989
4990 if (FAILED(rc))
4991 logData.resize(0);
4992 logData.detachTo(ComSafeArrayOutArg(aData));
4993
4994 return rc;
4995}
4996
4997
4998// public methods for internal purposes
4999/////////////////////////////////////////////////////////////////////////////
5000
5001/**
5002 * Adds the given IsModified_* flag to the dirty flags of the machine.
5003 * This must be called either during loadSettings or under the machine write lock.
5004 * @param fl
5005 */
5006void Machine::setModified(uint32_t fl)
5007{
5008 mData->flModifications |= fl;
5009}
5010
5011/**
5012 * Saves the registry entry of this machine to the given configuration node.
5013 *
5014 * @param aEntryNode Node to save the registry entry to.
5015 *
5016 * @note locks this object for reading.
5017 */
5018HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
5019{
5020 AutoLimitedCaller autoCaller(this);
5021 AssertComRCReturnRC(autoCaller.rc());
5022
5023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5024
5025 data.uuid = mData->mUuid;
5026 data.strSettingsFile = mData->m_strConfigFile;
5027
5028 return S_OK;
5029}
5030
5031/**
5032 * Calculates the absolute path of the given path taking the directory of the
5033 * machine settings file as the current directory.
5034 *
5035 * @param aPath Path to calculate the absolute path for.
5036 * @param aResult Where to put the result (used only on success, can be the
5037 * same Utf8Str instance as passed in @a aPath).
5038 * @return IPRT result.
5039 *
5040 * @note Locks this object for reading.
5041 */
5042int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
5043{
5044 AutoCaller autoCaller(this);
5045 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5046
5047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5048
5049 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
5050
5051 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
5052
5053 strSettingsDir.stripFilename();
5054 char folder[RTPATH_MAX];
5055 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
5056 if (RT_SUCCESS(vrc))
5057 aResult = folder;
5058
5059 return vrc;
5060}
5061
5062/**
5063 * Tries to calculate the relative path of the given absolute path using the
5064 * directory of the machine settings file as the base directory.
5065 *
5066 * @param aPath Absolute path to calculate the relative path for.
5067 * @param aResult Where to put the result (used only when it's possible to
5068 * make a relative path from the given absolute path; otherwise
5069 * left untouched).
5070 *
5071 * @note Locks this object for reading.
5072 */
5073void Machine::calculateRelativePath(const Utf8Str &strPath, Utf8Str &aResult)
5074{
5075 AutoCaller autoCaller(this);
5076 AssertComRCReturn(autoCaller.rc(), (void)0);
5077
5078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5079
5080 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
5081
5082 Utf8Str settingsDir = mData->m_strConfigFileFull;
5083
5084 settingsDir.stripFilename();
5085 if (RTPathStartsWith(strPath.c_str(), settingsDir.c_str()))
5086 {
5087 /* when assigning, we create a separate Utf8Str instance because both
5088 * aPath and aResult can point to the same memory location when this
5089 * func is called (if we just do aResult = aPath, aResult will be freed
5090 * first, and since its the same as aPath, an attempt to copy garbage
5091 * will be made. */
5092 aResult = Utf8Str(strPath.c_str() + settingsDir.length() + 1);
5093 }
5094}
5095
5096/**
5097 * Returns the full path to the machine's log folder in the
5098 * \a aLogFolder argument.
5099 */
5100void Machine::getLogFolder(Utf8Str &aLogFolder)
5101{
5102 AutoCaller autoCaller(this);
5103 AssertComRCReturnVoid(autoCaller.rc());
5104
5105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5106
5107 Utf8Str settingsDir;
5108 if (isInOwnDir(&settingsDir))
5109 {
5110 /* Log folder is <Machines>/<VM_Name>/Logs */
5111 aLogFolder = Utf8StrFmt("%s%cLogs", settingsDir.raw(), RTPATH_DELIMITER);
5112 }
5113 else
5114 {
5115 /* Log folder is <Machines>/<VM_SnapshotFolder>/Logs */
5116 Assert(!mUserData->mSnapshotFolderFull.isEmpty());
5117 aLogFolder = Utf8StrFmt ("%ls%cLogs", mUserData->mSnapshotFolderFull.raw(),
5118 RTPATH_DELIMITER);
5119 }
5120}
5121
5122/**
5123 * Returns the full path to the machine's log file for an given index.
5124 */
5125Utf8Str Machine::queryLogFilename(ULONG idx)
5126{
5127 Utf8Str logFolder;
5128 getLogFolder(logFolder);
5129 Assert(logFolder.length());
5130 Utf8Str log;
5131 if (idx == 0)
5132 log = Utf8StrFmt("%s%cVBox.log",
5133 logFolder.raw(), RTPATH_DELIMITER);
5134 else
5135 log = Utf8StrFmt("%s%cVBox.log.%d",
5136 logFolder.raw(), RTPATH_DELIMITER, idx);
5137 return log;
5138}
5139
5140/**
5141 * @note Locks this object for writing, calls the client process (outside the
5142 * lock).
5143 */
5144HRESULT Machine::openSession(IInternalSessionControl *aControl)
5145{
5146 LogFlowThisFuncEnter();
5147
5148 AssertReturn(aControl, E_FAIL);
5149
5150 AutoCaller autoCaller(this);
5151 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5152
5153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5154
5155 if (!mData->mRegistered)
5156 return setError(E_UNEXPECTED,
5157 tr("The machine '%ls' is not registered"),
5158 mUserData->mName.raw());
5159
5160 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
5161
5162 /* Hack: in case the session is closing and there is a progress object
5163 * which allows waiting for the session to be closed, take the opportunity
5164 * and do a limited wait (max. 1 second). This helps a lot when the system
5165 * is busy and thus session closing can take a little while. */
5166 if ( mData->mSession.mState == SessionState_Closing
5167 && mData->mSession.mProgress)
5168 {
5169 alock.leave();
5170 mData->mSession.mProgress->WaitForCompletion(1000);
5171 alock.enter();
5172 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
5173 }
5174
5175 if (mData->mSession.mState == SessionState_Open ||
5176 mData->mSession.mState == SessionState_Closing)
5177 return setError(VBOX_E_INVALID_OBJECT_STATE,
5178 tr("A session for the machine '%ls' is currently open (or being closed)"),
5179 mUserData->mName.raw());
5180
5181 /* may not be busy */
5182 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
5183
5184 /* get the session PID */
5185 RTPROCESS pid = NIL_RTPROCESS;
5186 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
5187 aControl->GetPID((ULONG *) &pid);
5188 Assert(pid != NIL_RTPROCESS);
5189
5190 if (mData->mSession.mState == SessionState_Spawning)
5191 {
5192 /* This machine is awaiting for a spawning session to be opened, so
5193 * reject any other open attempts from processes other than one
5194 * started by #openRemoteSession(). */
5195
5196 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n",
5197 mData->mSession.mPid, mData->mSession.mPid));
5198 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
5199
5200 if (mData->mSession.mPid != pid)
5201 return setError(E_ACCESSDENIED,
5202 tr("An unexpected process (PID=0x%08X) has tried to open a direct "
5203 "session with the machine named '%ls', while only a process "
5204 "started by OpenRemoteSession (PID=0x%08X) is allowed"),
5205 pid, mUserData->mName.raw(), mData->mSession.mPid);
5206 }
5207
5208 /* create a SessionMachine object */
5209 ComObjPtr<SessionMachine> sessionMachine;
5210 sessionMachine.createObject();
5211 HRESULT rc = sessionMachine->init(this);
5212 AssertComRC(rc);
5213
5214 /* NOTE: doing return from this function after this point but
5215 * before the end is forbidden since it may call SessionMachine::uninit()
5216 * (through the ComObjPtr's destructor) which requests the VirtualBox write
5217 * lock while still holding the Machine lock in alock so that a deadlock
5218 * is possible due to the wrong lock order. */
5219
5220 if (SUCCEEDED(rc))
5221 {
5222#ifdef VBOX_WITH_RESOURCE_USAGE_API
5223 registerMetrics(mParent->performanceCollector(), this, pid);
5224#endif /* VBOX_WITH_RESOURCE_USAGE_API */
5225
5226 /*
5227 * Set the session state to Spawning to protect against subsequent
5228 * attempts to open a session and to unregister the machine after
5229 * we leave the lock.
5230 */
5231 SessionState_T origState = mData->mSession.mState;
5232 mData->mSession.mState = SessionState_Spawning;
5233
5234 /*
5235 * Leave the lock before calling the client process -- it will call
5236 * Machine/SessionMachine methods. Leaving the lock here is quite safe
5237 * because the state is Spawning, so that openRemotesession() and
5238 * openExistingSession() calls will fail. This method, called before we
5239 * enter the lock again, will fail because of the wrong PID.
5240 *
5241 * Note that mData->mSession.mRemoteControls accessed outside
5242 * the lock may not be modified when state is Spawning, so it's safe.
5243 */
5244 alock.leave();
5245
5246 LogFlowThisFunc(("Calling AssignMachine()...\n"));
5247 rc = aControl->AssignMachine(sessionMachine);
5248 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
5249
5250 /* The failure may occur w/o any error info (from RPC), so provide one */
5251 if (FAILED(rc))
5252 setError(VBOX_E_VM_ERROR,
5253 tr("Failed to assign the machine to the session (%Rrc)"), rc);
5254
5255 if (SUCCEEDED(rc) && origState == SessionState_Spawning)
5256 {
5257 /* complete the remote session initialization */
5258
5259 /* get the console from the direct session */
5260 ComPtr<IConsole> console;
5261 rc = aControl->GetRemoteConsole(console.asOutParam());
5262 ComAssertComRC(rc);
5263
5264 if (SUCCEEDED(rc) && !console)
5265 {
5266 ComAssert(!!console);
5267 rc = E_FAIL;
5268 }
5269
5270 /* assign machine & console to the remote session */
5271 if (SUCCEEDED(rc))
5272 {
5273 /*
5274 * after openRemoteSession(), the first and the only
5275 * entry in remoteControls is that remote session
5276 */
5277 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
5278 rc = mData->mSession.mRemoteControls.front()->
5279 AssignRemoteMachine(sessionMachine, console);
5280 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
5281
5282 /* The failure may occur w/o any error info (from RPC), so provide one */
5283 if (FAILED(rc))
5284 setError(VBOX_E_VM_ERROR,
5285 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
5286 }
5287
5288 if (FAILED(rc))
5289 aControl->Uninitialize();
5290 }
5291
5292 /* enter the lock again */
5293 alock.enter();
5294
5295 /* Restore the session state */
5296 mData->mSession.mState = origState;
5297 }
5298
5299 /* finalize spawning anyway (this is why we don't return on errors above) */
5300 if (mData->mSession.mState == SessionState_Spawning)
5301 {
5302 /* Note that the progress object is finalized later */
5303
5304 /* We don't reset mSession.mPid here because it is necessary for
5305 * SessionMachine::uninit() to reap the child process later. */
5306
5307 if (FAILED(rc))
5308 {
5309 /* Close the remote session, remove the remote control from the list
5310 * and reset session state to Closed (@note keep the code in sync
5311 * with the relevant part in openSession()). */
5312
5313 Assert(mData->mSession.mRemoteControls.size() == 1);
5314 if (mData->mSession.mRemoteControls.size() == 1)
5315 {
5316 ErrorInfoKeeper eik;
5317 mData->mSession.mRemoteControls.front()->Uninitialize();
5318 }
5319
5320 mData->mSession.mRemoteControls.clear();
5321 mData->mSession.mState = SessionState_Closed;
5322 }
5323 }
5324 else
5325 {
5326 /* memorize PID of the directly opened session */
5327 if (SUCCEEDED(rc))
5328 mData->mSession.mPid = pid;
5329 }
5330
5331 if (SUCCEEDED(rc))
5332 {
5333 /* memorize the direct session control and cache IUnknown for it */
5334 mData->mSession.mDirectControl = aControl;
5335 mData->mSession.mState = SessionState_Open;
5336 /* associate the SessionMachine with this Machine */
5337 mData->mSession.mMachine = sessionMachine;
5338
5339 /* request an IUnknown pointer early from the remote party for later
5340 * identity checks (it will be internally cached within mDirectControl
5341 * at least on XPCOM) */
5342 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
5343 NOREF(unk);
5344 }
5345
5346 /* Leave the lock since SessionMachine::uninit() locks VirtualBox which
5347 * would break the lock order */
5348 alock.leave();
5349
5350 /* uninitialize the created session machine on failure */
5351 if (FAILED(rc))
5352 sessionMachine->uninit();
5353
5354 LogFlowThisFunc(("rc=%08X\n", rc));
5355 LogFlowThisFuncLeave();
5356 return rc;
5357}
5358
5359/**
5360 * @note Locks this object for writing, calls the client process
5361 * (inside the lock).
5362 */
5363HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,
5364 IN_BSTR aType,
5365 IN_BSTR aEnvironment,
5366 Progress *aProgress)
5367{
5368 LogFlowThisFuncEnter();
5369
5370 AssertReturn(aControl, E_FAIL);
5371 AssertReturn(aProgress, E_FAIL);
5372
5373 AutoCaller autoCaller(this);
5374 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5375
5376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5377
5378 if (!mData->mRegistered)
5379 return setError(E_UNEXPECTED,
5380 tr("The machine '%ls' is not registered"),
5381 mUserData->mName.raw());
5382
5383 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
5384
5385 if (mData->mSession.mState == SessionState_Open ||
5386 mData->mSession.mState == SessionState_Spawning ||
5387 mData->mSession.mState == SessionState_Closing)
5388 return setError(VBOX_E_INVALID_OBJECT_STATE,
5389 tr("A session for the machine '%ls' is currently open (or being opened or closed)"),
5390 mUserData->mName.raw());
5391
5392 /* may not be busy */
5393 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
5394
5395 /* get the path to the executable */
5396 char szPath[RTPATH_MAX];
5397 RTPathAppPrivateArch(szPath, RTPATH_MAX);
5398 size_t sz = strlen(szPath);
5399 szPath[sz++] = RTPATH_DELIMITER;
5400 szPath[sz] = 0;
5401 char *cmd = szPath + sz;
5402 sz = RTPATH_MAX - sz;
5403
5404 int vrc = VINF_SUCCESS;
5405 RTPROCESS pid = NIL_RTPROCESS;
5406
5407 RTENV env = RTENV_DEFAULT;
5408
5409 if (aEnvironment != NULL && *aEnvironment)
5410 {
5411 char *newEnvStr = NULL;
5412
5413 do
5414 {
5415 /* clone the current environment */
5416 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
5417 AssertRCBreakStmt(vrc2, vrc = vrc2);
5418
5419 newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str());
5420 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
5421
5422 /* put new variables to the environment
5423 * (ignore empty variable names here since RTEnv API
5424 * intentionally doesn't do that) */
5425 char *var = newEnvStr;
5426 for (char *p = newEnvStr; *p; ++p)
5427 {
5428 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
5429 {
5430 *p = '\0';
5431 if (*var)
5432 {
5433 char *val = strchr(var, '=');
5434 if (val)
5435 {
5436 *val++ = '\0';
5437 vrc2 = RTEnvSetEx(env, var, val);
5438 }
5439 else
5440 vrc2 = RTEnvUnsetEx(env, var);
5441 if (RT_FAILURE(vrc2))
5442 break;
5443 }
5444 var = p + 1;
5445 }
5446 }
5447 if (RT_SUCCESS(vrc2) && *var)
5448 vrc2 = RTEnvPutEx(env, var);
5449
5450 AssertRCBreakStmt(vrc2, vrc = vrc2);
5451 }
5452 while (0);
5453
5454 if (newEnvStr != NULL)
5455 RTStrFree(newEnvStr);
5456 }
5457
5458 Utf8Str strType(aType);
5459
5460 /* Qt is default */
5461#ifdef VBOX_WITH_QTGUI
5462 if (strType == "gui" || strType == "GUI/Qt")
5463 {
5464# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
5465 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
5466# else
5467 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
5468# endif
5469 Assert(sz >= sizeof(VirtualBox_exe));
5470 strcpy(cmd, VirtualBox_exe);
5471
5472 Utf8Str idStr = mData->mUuid.toString();
5473 Utf8Str strName = mUserData->mName;
5474 const char * args[] = {szPath, "--comment", strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
5475 vrc = RTProcCreate(szPath, args, env, 0, &pid);
5476 }
5477#else /* !VBOX_WITH_QTGUI */
5478 if (0)
5479 ;
5480#endif /* VBOX_WITH_QTGUI */
5481
5482 else
5483
5484#ifdef VBOX_WITH_VBOXSDL
5485 if (strType == "sdl" || strType == "GUI/SDL")
5486 {
5487 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
5488 Assert(sz >= sizeof(VBoxSDL_exe));
5489 strcpy(cmd, VBoxSDL_exe);
5490
5491 Utf8Str idStr = mData->mUuid.toString();
5492 Utf8Str strName = mUserData->mName;
5493 const char * args[] = {szPath, "--comment", strName.c_str(), "--startvm", idStr.c_str(), 0 };
5494 vrc = RTProcCreate(szPath, args, env, 0, &pid);
5495 }
5496#else /* !VBOX_WITH_VBOXSDL */
5497 if (0)
5498 ;
5499#endif /* !VBOX_WITH_VBOXSDL */
5500
5501 else
5502
5503#ifdef VBOX_WITH_HEADLESS
5504 if ( strType == "headless"
5505 || strType == "capture"
5506#ifdef VBOX_WITH_VRDP
5507 || strType == "vrdp"
5508#endif
5509 )
5510 {
5511 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
5512 Assert(sz >= sizeof(VBoxHeadless_exe));
5513 strcpy(cmd, VBoxHeadless_exe);
5514
5515 Utf8Str idStr = mData->mUuid.toString();
5516 /* Leave space for 2 args, as "headless" needs --vrdp off on non-OSE. */
5517 Utf8Str strName = mUserData->mName;
5518 const char * args[] = {szPath, "--comment", strName.c_str(), "--startvm", idStr.c_str(), 0, 0, 0 };
5519#ifdef VBOX_WITH_VRDP
5520 if (strType == "headless")
5521 {
5522 unsigned pos = RT_ELEMENTS(args) - 3;
5523 args[pos++] = "--vrdp";
5524 args[pos] = "off";
5525 }
5526#endif
5527 if (strType == "capture")
5528 {
5529 unsigned pos = RT_ELEMENTS(args) - 3;
5530 args[pos] = "--capture";
5531 }
5532 vrc = RTProcCreate(szPath, args, env, 0, &pid);
5533 }
5534#else /* !VBOX_WITH_HEADLESS */
5535 if (0)
5536 ;
5537#endif /* !VBOX_WITH_HEADLESS */
5538 else
5539 {
5540 RTEnvDestroy(env);
5541 return setError(E_INVALIDARG,
5542 tr("Invalid session type: '%s'"),
5543 strType.c_str());
5544 }
5545
5546 RTEnvDestroy(env);
5547
5548 if (RT_FAILURE(vrc))
5549 return setError(VBOX_E_IPRT_ERROR,
5550 tr("Could not launch a process for the machine '%ls' (%Rrc)"),
5551 mUserData->mName.raw(), vrc);
5552
5553 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
5554
5555 /*
5556 * Note that we don't leave the lock here before calling the client,
5557 * because it doesn't need to call us back if called with a NULL argument.
5558 * Leaving the lock herer is dangerous because we didn't prepare the
5559 * launch data yet, but the client we've just started may happen to be
5560 * too fast and call openSession() that will fail (because of PID, etc.),
5561 * so that the Machine will never get out of the Spawning session state.
5562 */
5563
5564 /* inform the session that it will be a remote one */
5565 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
5566 HRESULT rc = aControl->AssignMachine(NULL);
5567 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
5568
5569 if (FAILED(rc))
5570 {
5571 /* restore the session state */
5572 mData->mSession.mState = SessionState_Closed;
5573 /* The failure may occur w/o any error info (from RPC), so provide one */
5574 return setError(VBOX_E_VM_ERROR,
5575 tr("Failed to assign the machine to the session (%Rrc)"), rc);
5576 }
5577
5578 /* attach launch data to the machine */
5579 Assert(mData->mSession.mPid == NIL_RTPROCESS);
5580 mData->mSession.mRemoteControls.push_back (aControl);
5581 mData->mSession.mProgress = aProgress;
5582 mData->mSession.mPid = pid;
5583 mData->mSession.mState = SessionState_Spawning;
5584 mData->mSession.mType = strType;
5585
5586 LogFlowThisFuncLeave();
5587 return S_OK;
5588}
5589
5590/**
5591 * @note Locks this object for writing, calls the client process
5592 * (outside the lock).
5593 */
5594HRESULT Machine::openExistingSession(IInternalSessionControl *aControl)
5595{
5596 LogFlowThisFuncEnter();
5597
5598 AssertReturn(aControl, E_FAIL);
5599
5600 AutoCaller autoCaller(this);
5601 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5602
5603 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5604
5605 if (!mData->mRegistered)
5606 return setError(E_UNEXPECTED,
5607 tr("The machine '%ls' is not registered"),
5608 mUserData->mName.raw());
5609
5610 LogFlowThisFunc(("mSession.state=%s\n", Global::stringifySessionState(mData->mSession.mState)));
5611
5612 if (mData->mSession.mState != SessionState_Open)
5613 return setError(VBOX_E_INVALID_SESSION_STATE,
5614 tr("The machine '%ls' does not have an open session"),
5615 mUserData->mName.raw());
5616
5617 ComAssertRet(!mData->mSession.mDirectControl.isNull(), E_FAIL);
5618
5619 // copy member variables before leaving lock
5620 ComPtr<IInternalSessionControl> pDirectControl = mData->mSession.mDirectControl;
5621 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
5622 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
5623
5624 /*
5625 * Leave the lock before calling the client process. It's safe here
5626 * since the only thing to do after we get the lock again is to add
5627 * the remote control to the list (which doesn't directly influence
5628 * anything).
5629 */
5630 alock.leave();
5631
5632 // get the console from the direct session (this is a remote call)
5633 ComPtr<IConsole> pConsole;
5634 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
5635 HRESULT rc = pDirectControl->GetRemoteConsole(pConsole.asOutParam());
5636 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
5637 if (FAILED (rc))
5638 /* The failure may occur w/o any error info (from RPC), so provide one */
5639 return setError(VBOX_E_VM_ERROR,
5640 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
5641
5642 ComAssertRet(!pConsole.isNull(), E_FAIL);
5643
5644 /* attach the remote session to the machine */
5645 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
5646 rc = aControl->AssignRemoteMachine(pSessionMachine, pConsole);
5647 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
5648
5649 /* The failure may occur w/o any error info (from RPC), so provide one */
5650 if (FAILED(rc))
5651 return setError(VBOX_E_VM_ERROR,
5652 tr("Failed to assign the machine to the session (%Rrc)"),
5653 rc);
5654
5655 alock.enter();
5656
5657 /* need to revalidate the state after entering the lock again */
5658 if (mData->mSession.mState != SessionState_Open)
5659 {
5660 aControl->Uninitialize();
5661
5662 return setError(VBOX_E_INVALID_SESSION_STATE,
5663 tr("The machine '%ls' does not have an open session"),
5664 mUserData->mName.raw());
5665 }
5666
5667 /* store the control in the list */
5668 mData->mSession.mRemoteControls.push_back(aControl);
5669
5670 LogFlowThisFuncLeave();
5671 return S_OK;
5672}
5673
5674/**
5675 * Returns @c true if the given machine has an open direct session and returns
5676 * the session machine instance and additional session data (on some platforms)
5677 * if so.
5678 *
5679 * Note that when the method returns @c false, the arguments remain unchanged.
5680 *
5681 * @param aMachine Session machine object.
5682 * @param aControl Direct session control object (optional).
5683 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
5684 *
5685 * @note locks this object for reading.
5686 */
5687#if defined(RT_OS_WINDOWS)
5688bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
5689 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
5690 HANDLE *aIPCSem /*= NULL*/,
5691 bool aAllowClosing /*= false*/)
5692#elif defined(RT_OS_OS2)
5693bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
5694 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
5695 HMTX *aIPCSem /*= NULL*/,
5696 bool aAllowClosing /*= false*/)
5697#else
5698bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
5699 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
5700 bool aAllowClosing /*= false*/)
5701#endif
5702{
5703 AutoLimitedCaller autoCaller(this);
5704 AssertComRCReturn(autoCaller.rc(), false);
5705
5706 /* just return false for inaccessible machines */
5707 if (autoCaller.state() != Ready)
5708 return false;
5709
5710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5711
5712 if (mData->mSession.mState == SessionState_Open ||
5713 (aAllowClosing && mData->mSession.mState == SessionState_Closing))
5714 {
5715 AssertReturn(!mData->mSession.mMachine.isNull(), false);
5716
5717 aMachine = mData->mSession.mMachine;
5718
5719 if (aControl != NULL)
5720 *aControl = mData->mSession.mDirectControl;
5721
5722#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5723 /* Additional session data */
5724 if (aIPCSem != NULL)
5725 *aIPCSem = aMachine->mIPCSem;
5726#endif
5727 return true;
5728 }
5729
5730 return false;
5731}
5732
5733/**
5734 * Returns @c true if the given machine has an spawning direct session and
5735 * returns and additional session data (on some platforms) if so.
5736 *
5737 * Note that when the method returns @c false, the arguments remain unchanged.
5738 *
5739 * @param aPID PID of the spawned direct session process.
5740 *
5741 * @note locks this object for reading.
5742 */
5743#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5744bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
5745#else
5746bool Machine::isSessionSpawning()
5747#endif
5748{
5749 AutoLimitedCaller autoCaller(this);
5750 AssertComRCReturn(autoCaller.rc(), false);
5751
5752 /* just return false for inaccessible machines */
5753 if (autoCaller.state() != Ready)
5754 return false;
5755
5756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5757
5758 if (mData->mSession.mState == SessionState_Spawning)
5759 {
5760#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5761 /* Additional session data */
5762 if (aPID != NULL)
5763 {
5764 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
5765 *aPID = mData->mSession.mPid;
5766 }
5767#endif
5768 return true;
5769 }
5770
5771 return false;
5772}
5773
5774/**
5775 * Called from the client watcher thread to check for unexpected client process
5776 * death during Session_Spawning state (e.g. before it successfully opened a
5777 * direct session).
5778 *
5779 * On Win32 and on OS/2, this method is called only when we've got the
5780 * direct client's process termination notification, so it always returns @c
5781 * true.
5782 *
5783 * On other platforms, this method returns @c true if the client process is
5784 * terminated and @c false if it's still alive.
5785 *
5786 * @note Locks this object for writing.
5787 */
5788bool Machine::checkForSpawnFailure()
5789{
5790 AutoCaller autoCaller(this);
5791 if (!autoCaller.isOk())
5792 {
5793 /* nothing to do */
5794 LogFlowThisFunc(("Already uninitialized!\n"));
5795 return true;
5796 }
5797
5798 /* VirtualBox::addProcessToReap() needs a write lock */
5799 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
5800
5801 if (mData->mSession.mState != SessionState_Spawning)
5802 {
5803 /* nothing to do */
5804 LogFlowThisFunc(("Not spawning any more!\n"));
5805 return true;
5806 }
5807
5808 HRESULT rc = S_OK;
5809
5810#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
5811
5812 /* the process was already unexpectedly terminated, we just need to set an
5813 * error and finalize session spawning */
5814 rc = setError(E_FAIL,
5815 tr("Virtual machine '%ls' has terminated unexpectedly during startup"),
5816 getName().raw());
5817#else
5818
5819 /* PID not yet initialized, skip check. */
5820 if (mData->mSession.mPid == NIL_RTPROCESS)
5821 return false;
5822
5823 RTPROCSTATUS status;
5824 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
5825 &status);
5826
5827 if (vrc != VERR_PROCESS_RUNNING)
5828 {
5829 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
5830 rc = setError(E_FAIL,
5831 tr("Virtual machine '%ls' has terminated unexpectedly during startup with exit code %d"),
5832 getName().raw(), status.iStatus);
5833 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
5834 rc = setError(E_FAIL,
5835 tr("Virtual machine '%ls' has terminated unexpectedly during startup because of signal %d"),
5836 getName().raw(), status.iStatus);
5837 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
5838 rc = setError(E_FAIL,
5839 tr("Virtual machine '%ls' has terminated abnormally"),
5840 getName().raw(), status.iStatus);
5841 else
5842 rc = setError(E_FAIL,
5843 tr("Virtual machine '%ls' has terminated unexpectedly during startup (%Rrc)"),
5844 getName().raw(), rc);
5845 }
5846
5847#endif
5848
5849 if (FAILED(rc))
5850 {
5851 /* Close the remote session, remove the remote control from the list
5852 * and reset session state to Closed (@note keep the code in sync with
5853 * the relevant part in checkForSpawnFailure()). */
5854
5855 Assert(mData->mSession.mRemoteControls.size() == 1);
5856 if (mData->mSession.mRemoteControls.size() == 1)
5857 {
5858 ErrorInfoKeeper eik;
5859 mData->mSession.mRemoteControls.front()->Uninitialize();
5860 }
5861
5862 mData->mSession.mRemoteControls.clear();
5863 mData->mSession.mState = SessionState_Closed;
5864
5865 /* finalize the progress after setting the state */
5866 if (!mData->mSession.mProgress.isNull())
5867 {
5868 mData->mSession.mProgress->notifyComplete(rc);
5869 mData->mSession.mProgress.setNull();
5870 }
5871
5872 mParent->addProcessToReap(mData->mSession.mPid);
5873 mData->mSession.mPid = NIL_RTPROCESS;
5874
5875 mParent->onSessionStateChange(mData->mUuid, SessionState_Closed);
5876 return true;
5877 }
5878
5879 return false;
5880}
5881
5882/**
5883 * Checks that the registered flag of the machine can be set according to
5884 * the argument and sets it. On success, commits and saves all settings.
5885 *
5886 * @note When this machine is inaccessible, the only valid value for \a
5887 * aRegistered is FALSE (i.e. unregister the machine) because unregistered
5888 * inaccessible machines are not currently supported. Note that unregistering
5889 * an inaccessible machine will \b uninitialize this machine object. Therefore,
5890 * the caller must make sure there are no active Machine::addCaller() calls
5891 * on the current thread because this will block Machine::uninit().
5892 *
5893 * @note Must be called from mParent's write lock. Locks this object and
5894 * children for writing.
5895 */
5896HRESULT Machine::trySetRegistered(BOOL argNewRegistered)
5897{
5898 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
5899
5900 AutoLimitedCaller autoCaller(this);
5901 AssertComRCReturnRC(autoCaller.rc());
5902
5903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5904
5905 /* wait for state dependants to drop to zero */
5906 ensureNoStateDependencies();
5907
5908 ComAssertRet(mData->mRegistered != argNewRegistered, E_FAIL);
5909
5910 if (!mData->mAccessible)
5911 {
5912 /* A special case: the machine is not accessible. */
5913
5914 /* inaccessible machines can only be unregistered */
5915 AssertReturn(!argNewRegistered, E_FAIL);
5916
5917 /* Uninitialize ourselves here because currently there may be no
5918 * unregistered that are inaccessible (this state combination is not
5919 * supported). Note releasing the caller and leaving the lock before
5920 * calling uninit() */
5921
5922 alock.leave();
5923 autoCaller.release();
5924
5925 uninit();
5926
5927 return S_OK;
5928 }
5929
5930 AssertReturn(autoCaller.state() == Ready, E_FAIL);
5931
5932 if (argNewRegistered)
5933 {
5934 if (mData->mRegistered)
5935 return setError(VBOX_E_INVALID_OBJECT_STATE,
5936 tr("The machine '%ls' with UUID {%s} is already registered"),
5937 mUserData->mName.raw(),
5938 mData->mUuid.toString().raw());
5939 }
5940 else
5941 {
5942 if (mData->mMachineState == MachineState_Saved)
5943 return setError(VBOX_E_INVALID_VM_STATE,
5944 tr("Cannot unregister the machine '%ls' because it is in the Saved state"),
5945 mUserData->mName.raw());
5946
5947 size_t snapshotCount = 0;
5948 if (mData->mFirstSnapshot)
5949 snapshotCount = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5950 if (snapshotCount)
5951 return setError(VBOX_E_INVALID_OBJECT_STATE,
5952 tr("Cannot unregister the machine '%ls' because it has %d snapshots"),
5953 mUserData->mName.raw(), snapshotCount);
5954
5955 if (mData->mSession.mState != SessionState_Closed)
5956 return setError(VBOX_E_INVALID_OBJECT_STATE,
5957 tr("Cannot unregister the machine '%ls' because it has an open session"),
5958 mUserData->mName.raw());
5959
5960 if (mMediaData->mAttachments.size() != 0)
5961 return setError(VBOX_E_INVALID_OBJECT_STATE,
5962 tr("Cannot unregister the machine '%ls' because it has %d medium attachments"),
5963 mUserData->mName.raw(),
5964 mMediaData->mAttachments.size());
5965
5966 /* Note that we do not prevent unregistration of a DVD or Floppy image
5967 * is attached: as opposed to hard disks detaching such an image
5968 * implicitly in this method (which we will do below) won't have any
5969 * side effects (like detached orphan base and diff hard disks etc).*/
5970 }
5971
5972 HRESULT rc = S_OK;
5973
5974 // Ensure the settings are saved. If we are going to be registered and
5975 // no config file exists yet, create it by calling saveSettings() too.
5976 if ( (mData->flModifications)
5977 || (argNewRegistered && !mData->pMachineConfigFile->fileExists())
5978 )
5979 {
5980 rc = saveSettings(NULL);
5981 // no need to check whether VirtualBox.xml needs saving too since
5982 // we can't have a machine XML file rename pending
5983 if (FAILED(rc)) return rc;
5984 }
5985
5986 /* more config checking goes here */
5987
5988 if (SUCCEEDED(rc))
5989 {
5990 /* we may have had implicit modifications we want to fix on success */
5991 commit();
5992
5993 mData->mRegistered = argNewRegistered;
5994 }
5995 else
5996 {
5997 /* we may have had implicit modifications we want to cancel on failure*/
5998 rollback(false /* aNotify */);
5999 }
6000
6001 return rc;
6002}
6003
6004/**
6005 * Increases the number of objects dependent on the machine state or on the
6006 * registered state. Guarantees that these two states will not change at least
6007 * until #releaseStateDependency() is called.
6008 *
6009 * Depending on the @a aDepType value, additional state checks may be made.
6010 * These checks will set extended error info on failure. See
6011 * #checkStateDependency() for more info.
6012 *
6013 * If this method returns a failure, the dependency is not added and the caller
6014 * is not allowed to rely on any particular machine state or registration state
6015 * value and may return the failed result code to the upper level.
6016 *
6017 * @param aDepType Dependency type to add.
6018 * @param aState Current machine state (NULL if not interested).
6019 * @param aRegistered Current registered state (NULL if not interested).
6020 *
6021 * @note Locks this object for writing.
6022 */
6023HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
6024 MachineState_T *aState /* = NULL */,
6025 BOOL *aRegistered /* = NULL */)
6026{
6027 AutoCaller autoCaller(this);
6028 AssertComRCReturnRC(autoCaller.rc());
6029
6030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6031
6032 HRESULT rc = checkStateDependency(aDepType);
6033 if (FAILED(rc)) return rc;
6034
6035 {
6036 if (mData->mMachineStateChangePending != 0)
6037 {
6038 /* ensureNoStateDependencies() is waiting for state dependencies to
6039 * drop to zero so don't add more. It may make sense to wait a bit
6040 * and retry before reporting an error (since the pending state
6041 * transition should be really quick) but let's just assert for
6042 * now to see if it ever happens on practice. */
6043
6044 AssertFailed();
6045
6046 return setError(E_ACCESSDENIED,
6047 tr("Machine state change is in progress. Please retry the operation later."));
6048 }
6049
6050 ++mData->mMachineStateDeps;
6051 Assert(mData->mMachineStateDeps != 0 /* overflow */);
6052 }
6053
6054 if (aState)
6055 *aState = mData->mMachineState;
6056 if (aRegistered)
6057 *aRegistered = mData->mRegistered;
6058
6059 return S_OK;
6060}
6061
6062/**
6063 * Decreases the number of objects dependent on the machine state.
6064 * Must always complete the #addStateDependency() call after the state
6065 * dependency is no more necessary.
6066 */
6067void Machine::releaseStateDependency()
6068{
6069 AutoCaller autoCaller(this);
6070 AssertComRCReturnVoid(autoCaller.rc());
6071
6072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6073
6074 /* releaseStateDependency() w/o addStateDependency()? */
6075 AssertReturnVoid(mData->mMachineStateDeps != 0);
6076 -- mData->mMachineStateDeps;
6077
6078 if (mData->mMachineStateDeps == 0)
6079 {
6080 /* inform ensureNoStateDependencies() that there are no more deps */
6081 if (mData->mMachineStateChangePending != 0)
6082 {
6083 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
6084 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
6085 }
6086 }
6087}
6088
6089// protected methods
6090/////////////////////////////////////////////////////////////////////////////
6091
6092/**
6093 * Performs machine state checks based on the @a aDepType value. If a check
6094 * fails, this method will set extended error info, otherwise it will return
6095 * S_OK. It is supposed, that on failure, the caller will immedieately return
6096 * the return value of this method to the upper level.
6097 *
6098 * When @a aDepType is AnyStateDep, this method always returns S_OK.
6099 *
6100 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
6101 * current state of this machine object allows to change settings of the
6102 * machine (i.e. the machine is not registered, or registered but not running
6103 * and not saved). It is useful to call this method from Machine setters
6104 * before performing any change.
6105 *
6106 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
6107 * as for MutableStateDep except that if the machine is saved, S_OK is also
6108 * returned. This is useful in setters which allow changing machine
6109 * properties when it is in the saved state.
6110 *
6111 * @param aDepType Dependency type to check.
6112 *
6113 * @note Non Machine based classes should use #addStateDependency() and
6114 * #releaseStateDependency() methods or the smart AutoStateDependency
6115 * template.
6116 *
6117 * @note This method must be called from under this object's read or write
6118 * lock.
6119 */
6120HRESULT Machine::checkStateDependency(StateDependency aDepType)
6121{
6122 switch (aDepType)
6123 {
6124 case AnyStateDep:
6125 {
6126 break;
6127 }
6128 case MutableStateDep:
6129 {
6130 if ( mData->mRegistered
6131 && ( getClassID() != clsidSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
6132 || ( mData->mMachineState != MachineState_Paused
6133 && mData->mMachineState != MachineState_Running
6134 && mData->mMachineState != MachineState_Aborted
6135 && mData->mMachineState != MachineState_Teleported
6136 && mData->mMachineState != MachineState_PoweredOff
6137 )
6138 )
6139 )
6140 return setError(VBOX_E_INVALID_VM_STATE,
6141 tr("The machine is not mutable (state is %s)"),
6142 Global::stringifyMachineState(mData->mMachineState));
6143 break;
6144 }
6145 case MutableOrSavedStateDep:
6146 {
6147 if ( mData->mRegistered
6148 && ( getClassID() != clsidSessionMachine /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */
6149 || ( mData->mMachineState != MachineState_Paused
6150 && mData->mMachineState != MachineState_Running
6151 && mData->mMachineState != MachineState_Aborted
6152 && mData->mMachineState != MachineState_Teleported
6153 && mData->mMachineState != MachineState_Saved
6154 && mData->mMachineState != MachineState_PoweredOff
6155 )
6156 )
6157 )
6158 return setError(VBOX_E_INVALID_VM_STATE,
6159 tr("The machine is not mutable (state is %s)"),
6160 Global::stringifyMachineState(mData->mMachineState));
6161 break;
6162 }
6163 }
6164
6165 return S_OK;
6166}
6167
6168/**
6169 * Helper to initialize all associated child objects and allocate data
6170 * structures.
6171 *
6172 * This method must be called as a part of the object's initialization procedure
6173 * (usually done in the #init() method).
6174 *
6175 * @note Must be called only from #init() or from #registeredInit().
6176 */
6177HRESULT Machine::initDataAndChildObjects()
6178{
6179 AutoCaller autoCaller(this);
6180 AssertComRCReturnRC(autoCaller.rc());
6181 AssertComRCReturn(autoCaller.state() == InInit ||
6182 autoCaller.state() == Limited, E_FAIL);
6183
6184 AssertReturn(!mData->mAccessible, E_FAIL);
6185
6186 /* allocate data structures */
6187 mSSData.allocate();
6188 mUserData.allocate();
6189 mHWData.allocate();
6190 mMediaData.allocate();
6191 mStorageControllers.allocate();
6192
6193 /* initialize mOSTypeId */
6194 mUserData->mOSTypeId = mParent->getUnknownOSType()->id();
6195
6196 /* create associated BIOS settings object */
6197 unconst(mBIOSSettings).createObject();
6198 mBIOSSettings->init(this);
6199
6200#ifdef VBOX_WITH_VRDP
6201 /* create an associated VRDPServer object (default is disabled) */
6202 unconst(mVRDPServer).createObject();
6203 mVRDPServer->init(this);
6204#endif
6205
6206 /* create associated serial port objects */
6207 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
6208 {
6209 unconst(mSerialPorts[slot]).createObject();
6210 mSerialPorts[slot]->init(this, slot);
6211 }
6212
6213 /* create associated parallel port objects */
6214 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
6215 {
6216 unconst(mParallelPorts[slot]).createObject();
6217 mParallelPorts[slot]->init(this, slot);
6218 }
6219
6220 /* create the audio adapter object (always present, default is disabled) */
6221 unconst(mAudioAdapter).createObject();
6222 mAudioAdapter->init(this);
6223
6224 /* create the USB controller object (always present, default is disabled) */
6225 unconst(mUSBController).createObject();
6226 mUSBController->init(this);
6227
6228 /* create associated network adapter objects */
6229 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot ++)
6230 {
6231 unconst(mNetworkAdapters[slot]).createObject();
6232 mNetworkAdapters[slot]->init(this, slot);
6233 }
6234
6235 return S_OK;
6236}
6237
6238/**
6239 * Helper to uninitialize all associated child objects and to free all data
6240 * structures.
6241 *
6242 * This method must be called as a part of the object's uninitialization
6243 * procedure (usually done in the #uninit() method).
6244 *
6245 * @note Must be called only from #uninit() or from #registeredInit().
6246 */
6247void Machine::uninitDataAndChildObjects()
6248{
6249 AutoCaller autoCaller(this);
6250 AssertComRCReturnVoid(autoCaller.rc());
6251 AssertComRCReturnVoid( autoCaller.state() == InUninit
6252 || autoCaller.state() == Limited);
6253
6254 /* uninit all children using addDependentChild()/removeDependentChild()
6255 * in their init()/uninit() methods */
6256 uninitDependentChildren();
6257
6258 /* tell all our other child objects we've been uninitialized */
6259
6260 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
6261 {
6262 if (mNetworkAdapters[slot])
6263 {
6264 mNetworkAdapters[slot]->uninit();
6265 unconst(mNetworkAdapters[slot]).setNull();
6266 }
6267 }
6268
6269 if (mUSBController)
6270 {
6271 mUSBController->uninit();
6272 unconst(mUSBController).setNull();
6273 }
6274
6275 if (mAudioAdapter)
6276 {
6277 mAudioAdapter->uninit();
6278 unconst(mAudioAdapter).setNull();
6279 }
6280
6281 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
6282 {
6283 if (mParallelPorts[slot])
6284 {
6285 mParallelPorts[slot]->uninit();
6286 unconst(mParallelPorts[slot]).setNull();
6287 }
6288 }
6289
6290 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
6291 {
6292 if (mSerialPorts[slot])
6293 {
6294 mSerialPorts[slot]->uninit();
6295 unconst(mSerialPorts[slot]).setNull();
6296 }
6297 }
6298
6299#ifdef VBOX_WITH_VRDP
6300 if (mVRDPServer)
6301 {
6302 mVRDPServer->uninit();
6303 unconst(mVRDPServer).setNull();
6304 }
6305#endif
6306
6307 if (mBIOSSettings)
6308 {
6309 mBIOSSettings->uninit();
6310 unconst(mBIOSSettings).setNull();
6311 }
6312
6313 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
6314 * instance is uninitialized; SessionMachine instances refer to real
6315 * Machine hard disks). This is necessary for a clean re-initialization of
6316 * the VM after successfully re-checking the accessibility state. Note
6317 * that in case of normal Machine or SnapshotMachine uninitialization (as
6318 * a result of unregistering or deleting the snapshot), outdated hard
6319 * disk attachments will already be uninitialized and deleted, so this
6320 * code will not affect them. */
6321 VBoxClsID clsid = getClassID();
6322 if ( !!mMediaData
6323 && (clsid == clsidMachine || clsid == clsidSnapshotMachine)
6324 )
6325 {
6326 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
6327 it != mMediaData->mAttachments.end();
6328 ++it)
6329 {
6330 ComObjPtr<Medium> hd = (*it)->getMedium();
6331 if (hd.isNull())
6332 continue;
6333 HRESULT rc = hd->detachFrom(mData->mUuid, getSnapshotId());
6334 AssertComRC(rc);
6335 }
6336 }
6337
6338 if (getClassID() == clsidMachine)
6339 {
6340 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
6341 if (mData->mFirstSnapshot)
6342 {
6343 // snapshots tree is protected by media write lock; strictly
6344 // this isn't necessary here since we're deleting the entire
6345 // machine, but otherwise we assert in Snapshot::uninit()
6346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6347 mData->mFirstSnapshot->uninit();
6348 mData->mFirstSnapshot.setNull();
6349 }
6350
6351 mData->mCurrentSnapshot.setNull();
6352 }
6353
6354 /* free data structures (the essential mData structure is not freed here
6355 * since it may be still in use) */
6356 mMediaData.free();
6357 mStorageControllers.free();
6358 mHWData.free();
6359 mUserData.free();
6360 mSSData.free();
6361}
6362
6363/**
6364 * Returns a pointer to the Machine object for this machine that acts like a
6365 * parent for complex machine data objects such as shared folders, etc.
6366 *
6367 * For primary Machine objects and for SnapshotMachine objects, returns this
6368 * object's pointer itself. For SessoinMachine objects, returns the peer
6369 * (primary) machine pointer.
6370 */
6371Machine* Machine::getMachine()
6372{
6373 if (getClassID() == clsidSessionMachine)
6374 return (Machine*)mPeer;
6375 return this;
6376}
6377
6378/**
6379 * Makes sure that there are no machine state dependants. If necessary, waits
6380 * for the number of dependants to drop to zero.
6381 *
6382 * Make sure this method is called from under this object's write lock to
6383 * guarantee that no new dependants may be added when this method returns
6384 * control to the caller.
6385 *
6386 * @note Locks this object for writing. The lock will be released while waiting
6387 * (if necessary).
6388 *
6389 * @warning To be used only in methods that change the machine state!
6390 */
6391void Machine::ensureNoStateDependencies()
6392{
6393 AssertReturnVoid(isWriteLockOnCurrentThread());
6394
6395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6396
6397 /* Wait for all state dependants if necessary */
6398 if (mData->mMachineStateDeps != 0)
6399 {
6400 /* lazy semaphore creation */
6401 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
6402 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
6403
6404 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
6405 mData->mMachineStateDeps));
6406
6407 ++mData->mMachineStateChangePending;
6408
6409 /* reset the semaphore before waiting, the last dependant will signal
6410 * it */
6411 RTSemEventMultiReset(mData->mMachineStateDepsSem);
6412
6413 alock.leave();
6414
6415 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
6416
6417 alock.enter();
6418
6419 -- mData->mMachineStateChangePending;
6420 }
6421}
6422
6423/**
6424 * Changes the machine state and informs callbacks.
6425 *
6426 * This method is not intended to fail so it either returns S_OK or asserts (and
6427 * returns a failure).
6428 *
6429 * @note Locks this object for writing.
6430 */
6431HRESULT Machine::setMachineState(MachineState_T aMachineState)
6432{
6433 LogFlowThisFuncEnter();
6434 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
6435
6436 AutoCaller autoCaller(this);
6437 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6438
6439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6440
6441 /* wait for state dependants to drop to zero */
6442 ensureNoStateDependencies();
6443
6444 if (mData->mMachineState != aMachineState)
6445 {
6446 mData->mMachineState = aMachineState;
6447
6448 RTTimeNow(&mData->mLastStateChange);
6449
6450 mParent->onMachineStateChange(mData->mUuid, aMachineState);
6451 }
6452
6453 LogFlowThisFuncLeave();
6454 return S_OK;
6455}
6456
6457/**
6458 * Searches for a shared folder with the given logical name
6459 * in the collection of shared folders.
6460 *
6461 * @param aName logical name of the shared folder
6462 * @param aSharedFolder where to return the found object
6463 * @param aSetError whether to set the error info if the folder is
6464 * not found
6465 * @return
6466 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
6467 *
6468 * @note
6469 * must be called from under the object's lock!
6470 */
6471HRESULT Machine::findSharedFolder(CBSTR aName,
6472 ComObjPtr<SharedFolder> &aSharedFolder,
6473 bool aSetError /* = false */)
6474{
6475 bool found = false;
6476 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
6477 !found && it != mHWData->mSharedFolders.end();
6478 ++it)
6479 {
6480 AutoWriteLock alock(*it COMMA_LOCKVAL_SRC_POS);
6481 found = (*it)->getName() == aName;
6482 if (found)
6483 aSharedFolder = *it;
6484 }
6485
6486 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND;
6487
6488 if (aSetError && !found)
6489 setError(rc, tr("Could not find a shared folder named '%ls'"), aName);
6490
6491 return rc;
6492}
6493
6494/**
6495 * Initializes all machine instance data from the given settings structures
6496 * from XML. The exception is the machine UUID which needs special handling
6497 * depending on the caller's use case, so the caller needs to set that herself.
6498 *
6499 * @param config
6500 * @param fAllowStorage
6501 */
6502HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config)
6503{
6504 /* name (required) */
6505 mUserData->mName = config.strName;
6506
6507 /* nameSync (optional, default is true) */
6508 mUserData->mNameSync = config.fNameSync;
6509
6510 mUserData->mDescription = config.strDescription;
6511
6512 // guest OS type
6513 mUserData->mOSTypeId = config.strOsType;
6514 /* look up the object by Id to check it is valid */
6515 ComPtr<IGuestOSType> guestOSType;
6516 HRESULT rc = mParent->GetGuestOSType(mUserData->mOSTypeId,
6517 guestOSType.asOutParam());
6518 if (FAILED(rc)) return rc;
6519
6520 // stateFile (optional)
6521 if (config.strStateFile.isEmpty())
6522 mSSData->mStateFilePath.setNull();
6523 else
6524 {
6525 Utf8Str stateFilePathFull(config.strStateFile);
6526 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
6527 if (RT_FAILURE(vrc))
6528 return setError(E_FAIL,
6529 tr("Invalid saved state file path '%s' (%Rrc)"),
6530 config.strStateFile.raw(),
6531 vrc);
6532 mSSData->mStateFilePath = stateFilePathFull;
6533 }
6534
6535 /* snapshotFolder (optional) */
6536 rc = COMSETTER(SnapshotFolder)(Bstr(config.strSnapshotFolder));
6537 if (FAILED(rc)) return rc;
6538
6539 /* currentStateModified (optional, default is true) */
6540 mData->mCurrentStateModified = config.fCurrentStateModified;
6541
6542 mData->mLastStateChange = config.timeLastStateChange;
6543
6544 /* teleportation */
6545 mUserData->mTeleporterEnabled = config.fTeleporterEnabled;
6546 mUserData->mTeleporterPort = config.uTeleporterPort;
6547 mUserData->mTeleporterAddress = config.strTeleporterAddress;
6548 mUserData->mTeleporterPassword = config.strTeleporterPassword;
6549
6550 /* RTC */
6551 mUserData->mRTCUseUTC = config.fRTCUseUTC;
6552
6553 /*
6554 * note: all mUserData members must be assigned prior this point because
6555 * we need to commit changes in order to let mUserData be shared by all
6556 * snapshot machine instances.
6557 */
6558 mUserData.commitCopy();
6559
6560 /* Snapshot node (optional) */
6561 size_t cRootSnapshots;
6562 if ((cRootSnapshots = config.llFirstSnapshot.size()))
6563 {
6564 // there must be only one root snapshot
6565 Assert(cRootSnapshots == 1);
6566
6567 const settings::Snapshot &snap = config.llFirstSnapshot.front();
6568
6569 rc = loadSnapshot(snap,
6570 config.uuidCurrentSnapshot,
6571 NULL); // no parent == first snapshot
6572 if (FAILED(rc)) return rc;
6573 }
6574
6575 /* Hardware node (required) */
6576 rc = loadHardware(config.hardwareMachine);
6577 if (FAILED(rc)) return rc;
6578
6579 /* Load storage controllers */
6580 rc = loadStorageControllers(config.storageMachine);
6581 if (FAILED(rc)) return rc;
6582
6583 /*
6584 * NOTE: the assignment below must be the last thing to do,
6585 * otherwise it will be not possible to change the settings
6586 * somewehere in the code above because all setters will be
6587 * blocked by checkStateDependency(MutableStateDep).
6588 */
6589
6590 /* set the machine state to Aborted or Saved when appropriate */
6591 if (config.fAborted)
6592 {
6593 Assert(!mSSData->mStateFilePath.isEmpty());
6594 mSSData->mStateFilePath.setNull();
6595
6596 /* no need to use setMachineState() during init() */
6597 mData->mMachineState = MachineState_Aborted;
6598 }
6599 else if (!mSSData->mStateFilePath.isEmpty())
6600 {
6601 /* no need to use setMachineState() during init() */
6602 mData->mMachineState = MachineState_Saved;
6603 }
6604
6605 // after loading settings, we are no longer different from the XML on disk
6606 mData->flModifications = 0;
6607
6608 return S_OK;
6609}
6610
6611/**
6612 * Recursively loads all snapshots starting from the given.
6613 *
6614 * @param aNode <Snapshot> node.
6615 * @param aCurSnapshotId Current snapshot ID from the settings file.
6616 * @param aParentSnapshot Parent snapshot.
6617 */
6618HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
6619 const Guid &aCurSnapshotId,
6620 Snapshot *aParentSnapshot)
6621{
6622 AssertReturn(getClassID() == clsidMachine, E_FAIL);
6623
6624 HRESULT rc = S_OK;
6625
6626 Utf8Str strStateFile;
6627 if (!data.strStateFile.isEmpty())
6628 {
6629 /* optional */
6630 strStateFile = data.strStateFile;
6631 int vrc = calculateFullPath(strStateFile, strStateFile);
6632 if (RT_FAILURE(vrc))
6633 return setError(E_FAIL,
6634 tr("Invalid saved state file path '%s' (%Rrc)"),
6635 strStateFile.raw(),
6636 vrc);
6637 }
6638
6639 /* create a snapshot machine object */
6640 ComObjPtr<SnapshotMachine> pSnapshotMachine;
6641 pSnapshotMachine.createObject();
6642 rc = pSnapshotMachine->init(this,
6643 data.hardware,
6644 data.storage,
6645 data.uuid,
6646 strStateFile);
6647 if (FAILED(rc)) return rc;
6648
6649 /* create a snapshot object */
6650 ComObjPtr<Snapshot> pSnapshot;
6651 pSnapshot.createObject();
6652 /* initialize the snapshot */
6653 rc = pSnapshot->init(mParent, // VirtualBox object
6654 data.uuid,
6655 data.strName,
6656 data.strDescription,
6657 data.timestamp,
6658 pSnapshotMachine,
6659 aParentSnapshot);
6660 if (FAILED(rc)) return rc;
6661
6662 /* memorize the first snapshot if necessary */
6663 if (!mData->mFirstSnapshot)
6664 mData->mFirstSnapshot = pSnapshot;
6665
6666 /* memorize the current snapshot when appropriate */
6667 if ( !mData->mCurrentSnapshot
6668 && pSnapshot->getId() == aCurSnapshotId
6669 )
6670 mData->mCurrentSnapshot = pSnapshot;
6671
6672 // now create the children
6673 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
6674 it != data.llChildSnapshots.end();
6675 ++it)
6676 {
6677 const settings::Snapshot &childData = *it;
6678 // recurse
6679 rc = loadSnapshot(childData,
6680 aCurSnapshotId,
6681 pSnapshot); // parent = the one we created above
6682 if (FAILED(rc)) return rc;
6683 }
6684
6685 return rc;
6686}
6687
6688/**
6689 * @param aNode <Hardware> node.
6690 */
6691HRESULT Machine::loadHardware(const settings::Hardware &data)
6692{
6693 AssertReturn(getClassID() == clsidMachine || getClassID() == clsidSnapshotMachine, E_FAIL);
6694
6695 HRESULT rc = S_OK;
6696
6697 try
6698 {
6699 /* The hardware version attribute (optional). */
6700 mHWData->mHWVersion = data.strVersion;
6701 mHWData->mHardwareUUID = data.uuid;
6702
6703 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
6704 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
6705 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
6706 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
6707 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
6708 mHWData->mPAEEnabled = data.fPAE;
6709 mHWData->mSyntheticCpu = data.fSyntheticCpu;
6710
6711 mHWData->mCPUCount = data.cCPUs;
6712 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
6713
6714 // cpu
6715 if (mHWData->mCPUHotPlugEnabled)
6716 {
6717 for (settings::CpuList::const_iterator it = data.llCpus.begin();
6718 it != data.llCpus.end();
6719 ++it)
6720 {
6721 const settings::Cpu &cpu = *it;
6722
6723 mHWData->mCPUAttached[cpu.ulId] = true;
6724 }
6725 }
6726
6727 // cpuid leafs
6728 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
6729 it != data.llCpuIdLeafs.end();
6730 ++it)
6731 {
6732 const settings::CpuIdLeaf &leaf = *it;
6733
6734 switch (leaf.ulId)
6735 {
6736 case 0x0:
6737 case 0x1:
6738 case 0x2:
6739 case 0x3:
6740 case 0x4:
6741 case 0x5:
6742 case 0x6:
6743 case 0x7:
6744 case 0x8:
6745 case 0x9:
6746 case 0xA:
6747 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
6748 break;
6749
6750 case 0x80000000:
6751 case 0x80000001:
6752 case 0x80000002:
6753 case 0x80000003:
6754 case 0x80000004:
6755 case 0x80000005:
6756 case 0x80000006:
6757 case 0x80000007:
6758 case 0x80000008:
6759 case 0x80000009:
6760 case 0x8000000A:
6761 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
6762 break;
6763
6764 default:
6765 /* just ignore */
6766 break;
6767 }
6768 }
6769
6770 mHWData->mMemorySize = data.ulMemorySizeMB;
6771
6772 // boot order
6773 for (size_t i = 0;
6774 i < RT_ELEMENTS(mHWData->mBootOrder);
6775 i++)
6776 {
6777 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
6778 if (it == data.mapBootOrder.end())
6779 mHWData->mBootOrder[i] = DeviceType_Null;
6780 else
6781 mHWData->mBootOrder[i] = it->second;
6782 }
6783
6784 mHWData->mVRAMSize = data.ulVRAMSizeMB;
6785 mHWData->mMonitorCount = data.cMonitors;
6786 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
6787 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
6788 mHWData->mFirmwareType = data.firmwareType;
6789 mHWData->mPointingHidType = data.pointingHidType;
6790 mHWData->mKeyboardHidType = data.keyboardHidType;
6791 mHWData->mHpetEnabled = data.fHpetEnabled;
6792
6793#ifdef VBOX_WITH_VRDP
6794 /* RemoteDisplay */
6795 rc = mVRDPServer->loadSettings(data.vrdpSettings);
6796 if (FAILED(rc)) return rc;
6797#endif
6798
6799 /* BIOS */
6800 rc = mBIOSSettings->loadSettings(data.biosSettings);
6801 if (FAILED(rc)) return rc;
6802
6803 /* USB Controller */
6804 rc = mUSBController->loadSettings(data.usbController);
6805 if (FAILED(rc)) return rc;
6806
6807 // network adapters
6808 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
6809 it != data.llNetworkAdapters.end();
6810 ++it)
6811 {
6812 const settings::NetworkAdapter &nic = *it;
6813
6814 /* slot unicity is guaranteed by XML Schema */
6815 AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
6816 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
6817 if (FAILED(rc)) return rc;
6818 }
6819
6820 // serial ports
6821 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
6822 it != data.llSerialPorts.end();
6823 ++it)
6824 {
6825 const settings::SerialPort &s = *it;
6826
6827 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
6828 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
6829 if (FAILED(rc)) return rc;
6830 }
6831
6832 // parallel ports (optional)
6833 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
6834 it != data.llParallelPorts.end();
6835 ++it)
6836 {
6837 const settings::ParallelPort &p = *it;
6838
6839 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
6840 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
6841 if (FAILED(rc)) return rc;
6842 }
6843
6844 /* AudioAdapter */
6845 rc = mAudioAdapter->loadSettings(data.audioAdapter);
6846 if (FAILED(rc)) return rc;
6847
6848 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
6849 it != data.llSharedFolders.end();
6850 ++it)
6851 {
6852 const settings::SharedFolder &sf = *it;
6853 rc = CreateSharedFolder(Bstr(sf.strName), Bstr(sf.strHostPath), sf.fWritable);
6854 if (FAILED(rc)) return rc;
6855 }
6856
6857 // Clipboard
6858 mHWData->mClipboardMode = data.clipboardMode;
6859
6860 // guest settings
6861 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
6862
6863 // IO settings
6864 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
6865 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
6866 mHWData->mIoBandwidthMax = data.ioSettings.ulIoBandwidthMax;
6867
6868#ifdef VBOX_WITH_GUEST_PROPS
6869 /* Guest properties (optional) */
6870 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
6871 it != data.llGuestProperties.end();
6872 ++it)
6873 {
6874 const settings::GuestProperty &prop = *it;
6875 uint32_t fFlags = guestProp::NILFLAG;
6876 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
6877 HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags };
6878 mHWData->mGuestProperties.push_back(property);
6879 }
6880
6881 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
6882#endif /* VBOX_WITH_GUEST_PROPS defined */
6883 }
6884 catch(std::bad_alloc &)
6885 {
6886 return E_OUTOFMEMORY;
6887 }
6888
6889 AssertComRC(rc);
6890 return rc;
6891}
6892
6893 /**
6894 * @param aNode <StorageControllers> node.
6895 */
6896HRESULT Machine::loadStorageControllers(const settings::Storage &data,
6897 const Guid *aSnapshotId /* = NULL */)
6898{
6899 AssertReturn(getClassID() == clsidMachine || getClassID() == clsidSnapshotMachine, E_FAIL);
6900
6901 HRESULT rc = S_OK;
6902
6903 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
6904 it != data.llStorageControllers.end();
6905 ++it)
6906 {
6907 const settings::StorageController &ctlData = *it;
6908
6909 ComObjPtr<StorageController> pCtl;
6910 /* Try to find one with the name first. */
6911 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
6912 if (SUCCEEDED(rc))
6913 return setError(VBOX_E_OBJECT_IN_USE,
6914 tr("Storage controller named '%s' already exists"),
6915 ctlData.strName.raw());
6916
6917 pCtl.createObject();
6918 rc = pCtl->init(this,
6919 ctlData.strName,
6920 ctlData.storageBus,
6921 ctlData.ulInstance);
6922 if (FAILED(rc)) return rc;
6923
6924 mStorageControllers->push_back(pCtl);
6925
6926 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
6927 if (FAILED(rc)) return rc;
6928
6929 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
6930 if (FAILED(rc)) return rc;
6931
6932 rc = pCtl->COMSETTER(IoBackend)(ctlData.ioBackendType);
6933 if (FAILED(rc)) return rc;
6934
6935 /* Set IDE emulation settings (only for AHCI controller). */
6936 if (ctlData.controllerType == StorageControllerType_IntelAhci)
6937 {
6938 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
6939 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
6940 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
6941 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
6942 )
6943 return rc;
6944 }
6945
6946 /* Load the attached devices now. */
6947 rc = loadStorageDevices(pCtl,
6948 ctlData,
6949 aSnapshotId);
6950 if (FAILED(rc)) return rc;
6951 }
6952
6953 return S_OK;
6954}
6955
6956/**
6957 * @param aNode <HardDiskAttachments> node.
6958 * @param fAllowStorage if false, we produce an error if the config requests media attachments
6959 * (used with importing unregistered machines which cannot have media attachments)
6960 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
6961 *
6962 * @note Lock mParent for reading and hard disks for writing before calling.
6963 */
6964HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
6965 const settings::StorageController &data,
6966 const Guid *aSnapshotId /*= NULL*/)
6967{
6968 AssertReturn( (getClassID() == clsidMachine && aSnapshotId == NULL)
6969 || (getClassID() == clsidSnapshotMachine && aSnapshotId != NULL),
6970 E_FAIL);
6971
6972 HRESULT rc = S_OK;
6973
6974 /* paranoia: detect duplicate attachments */
6975 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
6976 it != data.llAttachedDevices.end();
6977 ++it)
6978 {
6979 for (settings::AttachedDevicesList::const_iterator it2 = it;
6980 it2 != data.llAttachedDevices.end();
6981 ++it2)
6982 {
6983 if (it == it2)
6984 continue;
6985
6986 if ( (*it).lPort == (*it2).lPort
6987 && (*it).lDevice == (*it2).lDevice)
6988 {
6989 return setError(E_FAIL,
6990 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%ls'"),
6991 aStorageController->getName().raw(), (*it).lPort, (*it).lDevice, mUserData->mName.raw());
6992 }
6993 }
6994 }
6995
6996 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
6997 it != data.llAttachedDevices.end();
6998 ++it)
6999 {
7000 const settings::AttachedDevice &dev = *it;
7001 ComObjPtr<Medium> medium;
7002
7003 switch (dev.deviceType)
7004 {
7005 case DeviceType_Floppy:
7006 /* find a floppy by UUID */
7007 if (!dev.uuid.isEmpty())
7008 rc = mParent->findFloppyImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
7009 /* find a floppy by host device name */
7010 else if (!dev.strHostDriveSrc.isEmpty())
7011 {
7012 SafeIfaceArray<IMedium> drivevec;
7013 rc = mParent->host()->COMGETTER(FloppyDrives)(ComSafeArrayAsOutParam(drivevec));
7014 if (SUCCEEDED(rc))
7015 {
7016 for (size_t i = 0; i < drivevec.size(); ++i)
7017 {
7018 /// @todo eliminate this conversion
7019 ComObjPtr<Medium> med = (Medium *)drivevec[i];
7020 if ( dev.strHostDriveSrc == med->getName()
7021 || dev.strHostDriveSrc == med->getLocation())
7022 {
7023 medium = med;
7024 break;
7025 }
7026 }
7027 }
7028 }
7029 break;
7030
7031 case DeviceType_DVD:
7032 /* find a DVD by UUID */
7033 if (!dev.uuid.isEmpty())
7034 rc = mParent->findDVDImage(&dev.uuid, NULL, true /* aDoSetError */, &medium);
7035 /* find a DVD by host device name */
7036 else if (!dev.strHostDriveSrc.isEmpty())
7037 {
7038 SafeIfaceArray<IMedium> drivevec;
7039 rc = mParent->host()->COMGETTER(DVDDrives)(ComSafeArrayAsOutParam(drivevec));
7040 if (SUCCEEDED(rc))
7041 {
7042 for (size_t i = 0; i < drivevec.size(); ++i)
7043 {
7044 Bstr hostDriveSrc(dev.strHostDriveSrc);
7045 /// @todo eliminate this conversion
7046 ComObjPtr<Medium> med = (Medium *)drivevec[i];
7047 if ( hostDriveSrc == med->getName()
7048 || hostDriveSrc == med->getLocation())
7049 {
7050 medium = med;
7051 break;
7052 }
7053 }
7054 }
7055 }
7056 break;
7057
7058 case DeviceType_HardDisk:
7059 {
7060 /* find a hard disk by UUID */
7061 rc = mParent->findHardDisk(&dev.uuid, NULL, true /* aDoSetError */, &medium);
7062 if (FAILED(rc))
7063 {
7064 VBoxClsID clsid = getClassID();
7065 if (clsid == clsidSnapshotMachine)
7066 {
7067 // wrap another error message around the "cannot find hard disk" set by findHardDisk
7068 // so the user knows that the bad disk is in a snapshot somewhere
7069 com::ErrorInfo info;
7070 return setError(E_FAIL,
7071 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
7072 aSnapshotId->raw(),
7073 info.getText().raw());
7074 }
7075 else
7076 return rc;
7077 }
7078
7079 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
7080
7081 if (medium->getType() == MediumType_Immutable)
7082 {
7083 if (getClassID() == clsidSnapshotMachine)
7084 return setError(E_FAIL,
7085 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
7086 "of the virtual machine '%ls' ('%s')"),
7087 medium->getLocationFull().raw(),
7088 dev.uuid.raw(),
7089 aSnapshotId->raw(),
7090 mUserData->mName.raw(),
7091 mData->m_strConfigFileFull.raw());
7092
7093 return setError(E_FAIL,
7094 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s')"),
7095 medium->getLocationFull().raw(),
7096 dev.uuid.raw(),
7097 mUserData->mName.raw(),
7098 mData->m_strConfigFileFull.raw());
7099 }
7100
7101 if ( getClassID() != clsidSnapshotMachine
7102 && medium->getChildren().size() != 0
7103 )
7104 return setError(E_FAIL,
7105 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%ls' ('%s') "
7106 "because it has %d differencing child hard disks"),
7107 medium->getLocationFull().raw(),
7108 dev.uuid.raw(),
7109 mUserData->mName.raw(),
7110 mData->m_strConfigFileFull.raw(),
7111 medium->getChildren().size());
7112
7113 if (findAttachment(mMediaData->mAttachments,
7114 medium))
7115 return setError(E_FAIL,
7116 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%ls' ('%s')"),
7117 medium->getLocationFull().raw(),
7118 dev.uuid.raw(),
7119 mUserData->mName.raw(),
7120 mData->m_strConfigFileFull.raw());
7121
7122 break;
7123 }
7124
7125 default:
7126 return setError(E_FAIL,
7127 tr("Device with unknown type is attached to the virtual machine '%s' ('%s')"),
7128 medium->getLocationFull().raw(),
7129 mUserData->mName.raw(),
7130 mData->m_strConfigFileFull.raw());
7131 }
7132
7133 if (FAILED(rc))
7134 break;
7135
7136 const Bstr controllerName = aStorageController->getName();
7137 ComObjPtr<MediumAttachment> pAttachment;
7138 pAttachment.createObject();
7139 rc = pAttachment->init(this,
7140 medium,
7141 controllerName,
7142 dev.lPort,
7143 dev.lDevice,
7144 dev.deviceType,
7145 dev.fPassThrough);
7146 if (FAILED(rc)) break;
7147
7148 /* associate the medium with this machine and snapshot */
7149 if (!medium.isNull())
7150 {
7151 if (getClassID() == clsidSnapshotMachine)
7152 rc = medium->attachTo(mData->mUuid, *aSnapshotId);
7153 else
7154 rc = medium->attachTo(mData->mUuid);
7155 }
7156
7157 if (FAILED(rc))
7158 break;
7159
7160 /* back up mMediaData to let registeredInit() properly rollback on failure
7161 * (= limited accessibility) */
7162 setModified(IsModified_Storage);
7163 mMediaData.backup();
7164 mMediaData->mAttachments.push_back(pAttachment);
7165 }
7166
7167 return rc;
7168}
7169
7170/**
7171 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
7172 *
7173 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
7174 * @param aSnapshot where to return the found snapshot
7175 * @param aSetError true to set extended error info on failure
7176 */
7177HRESULT Machine::findSnapshot(const Guid &aId,
7178 ComObjPtr<Snapshot> &aSnapshot,
7179 bool aSetError /* = false */)
7180{
7181 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
7182
7183 if (!mData->mFirstSnapshot)
7184 {
7185 if (aSetError)
7186 return setError(E_FAIL,
7187 tr("This machine does not have any snapshots"));
7188 return E_FAIL;
7189 }
7190
7191 if (aId.isEmpty())
7192 aSnapshot = mData->mFirstSnapshot;
7193 else
7194 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId);
7195
7196 if (!aSnapshot)
7197 {
7198 if (aSetError)
7199 return setError(E_FAIL,
7200 tr("Could not find a snapshot with UUID {%s}"),
7201 aId.toString().raw());
7202 return E_FAIL;
7203 }
7204
7205 return S_OK;
7206}
7207
7208/**
7209 * Returns the snapshot with the given name or fails of no such snapshot.
7210 *
7211 * @param aName snapshot name to find
7212 * @param aSnapshot where to return the found snapshot
7213 * @param aSetError true to set extended error info on failure
7214 */
7215HRESULT Machine::findSnapshot(IN_BSTR aName,
7216 ComObjPtr<Snapshot> &aSnapshot,
7217 bool aSetError /* = false */)
7218{
7219 AssertReturn(aName, E_INVALIDARG);
7220
7221 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
7222
7223 if (!mData->mFirstSnapshot)
7224 {
7225 if (aSetError)
7226 return setError(VBOX_E_OBJECT_NOT_FOUND,
7227 tr("This machine does not have any snapshots"));
7228 return VBOX_E_OBJECT_NOT_FOUND;
7229 }
7230
7231 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aName);
7232
7233 if (!aSnapshot)
7234 {
7235 if (aSetError)
7236 return setError(VBOX_E_OBJECT_NOT_FOUND,
7237 tr("Could not find a snapshot named '%ls'"), aName);
7238 return VBOX_E_OBJECT_NOT_FOUND;
7239 }
7240
7241 return S_OK;
7242}
7243
7244/**
7245 * Returns a storage controller object with the given name.
7246 *
7247 * @param aName storage controller name to find
7248 * @param aStorageController where to return the found storage controller
7249 * @param aSetError true to set extended error info on failure
7250 */
7251HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
7252 ComObjPtr<StorageController> &aStorageController,
7253 bool aSetError /* = false */)
7254{
7255 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
7256
7257 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
7258 it != mStorageControllers->end();
7259 ++it)
7260 {
7261 if ((*it)->getName() == aName)
7262 {
7263 aStorageController = (*it);
7264 return S_OK;
7265 }
7266 }
7267
7268 if (aSetError)
7269 return setError(VBOX_E_OBJECT_NOT_FOUND,
7270 tr("Could not find a storage controller named '%s'"),
7271 aName.raw());
7272 return VBOX_E_OBJECT_NOT_FOUND;
7273}
7274
7275HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
7276 MediaData::AttachmentList &atts)
7277{
7278 AutoCaller autoCaller(this);
7279 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7280
7281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7282
7283 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
7284 it != mMediaData->mAttachments.end();
7285 ++it)
7286 {
7287 const ComObjPtr<MediumAttachment> &pAtt = *it;
7288
7289 // should never happen, but deal with NULL pointers in the list.
7290 AssertStmt(!pAtt.isNull(), continue);
7291
7292 // getControllerName() needs caller+read lock
7293 AutoCaller autoAttCaller(pAtt);
7294 if (FAILED(autoAttCaller.rc()))
7295 {
7296 atts.clear();
7297 return autoAttCaller.rc();
7298 }
7299 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
7300
7301 if (pAtt->getControllerName() == aName)
7302 atts.push_back(pAtt);
7303 }
7304
7305 return S_OK;
7306}
7307
7308/**
7309 * Helper for #saveSettings. Cares about renaming the settings directory and
7310 * file if the machine name was changed and about creating a new settings file
7311 * if this is a new machine.
7312 *
7313 * @note Must be never called directly but only from #saveSettings().
7314 */
7315HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
7316{
7317 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7318
7319 HRESULT rc = S_OK;
7320
7321 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
7322
7323 /* attempt to rename the settings file if machine name is changed */
7324 if ( mUserData->mNameSync
7325 && mUserData.isBackedUp()
7326 && mUserData.backedUpData()->mName != mUserData->mName
7327 )
7328 {
7329 bool dirRenamed = false;
7330 bool fileRenamed = false;
7331
7332 Utf8Str configFile, newConfigFile;
7333 Utf8Str configDir, newConfigDir;
7334
7335 do
7336 {
7337 int vrc = VINF_SUCCESS;
7338
7339 Utf8Str name = mUserData.backedUpData()->mName;
7340 Utf8Str newName = mUserData->mName;
7341
7342 configFile = mData->m_strConfigFileFull;
7343
7344 /* first, rename the directory if it matches the machine name */
7345 configDir = configFile;
7346 configDir.stripFilename();
7347 newConfigDir = configDir;
7348 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
7349 {
7350 newConfigDir.stripFilename();
7351 newConfigDir = Utf8StrFmt("%s%c%s",
7352 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
7353 /* new dir and old dir cannot be equal here because of 'if'
7354 * above and because name != newName */
7355 Assert(configDir != newConfigDir);
7356 if (!fSettingsFileIsNew)
7357 {
7358 /* perform real rename only if the machine is not new */
7359 vrc = RTPathRename(configDir.raw(), newConfigDir.raw(), 0);
7360 if (RT_FAILURE(vrc))
7361 {
7362 rc = setError(E_FAIL,
7363 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
7364 configDir.raw(),
7365 newConfigDir.raw(),
7366 vrc);
7367 break;
7368 }
7369 dirRenamed = true;
7370 }
7371 }
7372
7373 newConfigFile = Utf8StrFmt("%s%c%s.xml",
7374 newConfigDir.raw(), RTPATH_DELIMITER, newName.raw());
7375
7376 /* then try to rename the settings file itself */
7377 if (newConfigFile != configFile)
7378 {
7379 /* get the path to old settings file in renamed directory */
7380 configFile = Utf8StrFmt("%s%c%s",
7381 newConfigDir.raw(),
7382 RTPATH_DELIMITER,
7383 RTPathFilename(configFile.c_str()));
7384 if (!fSettingsFileIsNew)
7385 {
7386 /* perform real rename only if the machine is not new */
7387 vrc = RTFileRename(configFile.raw(), newConfigFile.raw(), 0);
7388 if (RT_FAILURE(vrc))
7389 {
7390 rc = setError(E_FAIL,
7391 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
7392 configFile.raw(),
7393 newConfigFile.raw(),
7394 vrc);
7395 break;
7396 }
7397 fileRenamed = true;
7398 }
7399 }
7400
7401 /* update m_strConfigFileFull amd mConfigFile */
7402 mData->m_strConfigFileFull = newConfigFile;
7403
7404 // compute the relative path too
7405 Utf8Str path = newConfigFile;
7406 mParent->calculateRelativePath(path, path);
7407 mData->m_strConfigFile = path;
7408
7409 // store the old and new so that VirtualBox::saveSettings() can update
7410 // the media registry
7411 if ( mData->mRegistered
7412 && configDir != newConfigDir)
7413 {
7414 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
7415
7416 if (pfNeedsGlobalSaveSettings)
7417 *pfNeedsGlobalSaveSettings = true;
7418 }
7419
7420 /* update the snapshot folder */
7421 path = mUserData->mSnapshotFolderFull;
7422 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
7423 {
7424 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
7425 path.raw() + configDir.length());
7426 mUserData->mSnapshotFolderFull = path;
7427 calculateRelativePath(path, path);
7428 mUserData->mSnapshotFolder = path;
7429 }
7430
7431 /* update the saved state file path */
7432 path = mSSData->mStateFilePath;
7433 if (RTPathStartsWith(path.c_str(), configDir.c_str()))
7434 {
7435 path = Utf8StrFmt("%s%s", newConfigDir.raw(),
7436 path.raw() + configDir.length());
7437 mSSData->mStateFilePath = path;
7438 }
7439
7440 /* Update saved state file paths of all online snapshots.
7441 * Note that saveSettings() will recognize name change
7442 * and will save all snapshots in this case. */
7443 if (mData->mFirstSnapshot)
7444 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
7445 newConfigDir.c_str());
7446 }
7447 while (0);
7448
7449 if (FAILED(rc))
7450 {
7451 /* silently try to rename everything back */
7452 if (fileRenamed)
7453 RTFileRename(newConfigFile.raw(), configFile.raw(), 0);
7454 if (dirRenamed)
7455 RTPathRename(newConfigDir.raw(), configDir.raw(), 0);
7456 }
7457
7458 if (FAILED(rc)) return rc;
7459 }
7460
7461 if (fSettingsFileIsNew)
7462 {
7463 /* create a virgin config file */
7464 int vrc = VINF_SUCCESS;
7465
7466 /* ensure the settings directory exists */
7467 Utf8Str path(mData->m_strConfigFileFull);
7468 path.stripFilename();
7469 if (!RTDirExists(path.c_str()))
7470 {
7471 vrc = RTDirCreateFullPath(path.c_str(), 0777);
7472 if (RT_FAILURE(vrc))
7473 {
7474 return setError(E_FAIL,
7475 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
7476 path.raw(),
7477 vrc);
7478 }
7479 }
7480
7481 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
7482 path = Utf8Str(mData->m_strConfigFileFull);
7483 RTFILE f = NIL_RTFILE;
7484 vrc = RTFileOpen(&f, path.c_str(),
7485 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
7486 if (RT_FAILURE(vrc))
7487 return setError(E_FAIL,
7488 tr("Could not create the settings file '%s' (%Rrc)"),
7489 path.raw(),
7490 vrc);
7491 RTFileClose(f);
7492 }
7493
7494 return rc;
7495}
7496
7497/**
7498 * Saves and commits machine data, user data and hardware data.
7499 *
7500 * Note that on failure, the data remains uncommitted.
7501 *
7502 * @a aFlags may combine the following flags:
7503 *
7504 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
7505 * Used when saving settings after an operation that makes them 100%
7506 * correspond to the settings from the current snapshot.
7507 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
7508 * #isReallyModified() returns false. This is necessary for cases when we
7509 * change machine data directly, not through the backup()/commit() mechanism.
7510 * - SaveS_Force: settings will be saved without doing a deep compare of the
7511 * settings structures. This is used when this is called because snapshots
7512 * have changed to avoid the overhead of the deep compare.
7513 *
7514 * @note Must be called from under this object's write lock. Locks children for
7515 * writing.
7516 *
7517 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
7518 * initialized to false and that will be set to true by this function if
7519 * the caller must invoke VirtualBox::saveSettings() because the global
7520 * settings have changed. This will happen if a machine rename has been
7521 * saved and the global machine and media registries will therefore need
7522 * updating.
7523 */
7524HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
7525 int aFlags /*= 0*/)
7526{
7527 LogFlowThisFuncEnter();
7528
7529 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7530
7531 /* make sure child objects are unable to modify the settings while we are
7532 * saving them */
7533 ensureNoStateDependencies();
7534
7535 AssertReturn( getClassID() == clsidMachine
7536 || getClassID() == clsidSessionMachine,
7537 E_FAIL);
7538
7539 HRESULT rc = S_OK;
7540 bool fNeedsWrite = false;
7541
7542 /* First, prepare to save settings. It will care about renaming the
7543 * settings directory and file if the machine name was changed and about
7544 * creating a new settings file if this is a new machine. */
7545 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
7546 if (FAILED(rc)) return rc;
7547
7548 // keep a pointer to the current settings structures
7549 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
7550 settings::MachineConfigFile *pNewConfig = NULL;
7551
7552 try
7553 {
7554 // make a fresh one to have everyone write stuff into
7555 pNewConfig = new settings::MachineConfigFile(NULL);
7556 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
7557
7558 // now go and copy all the settings data from COM to the settings structures
7559 // (this calles saveSettings() on all the COM objects in the machine)
7560 copyMachineDataToSettings(*pNewConfig);
7561
7562 if (aFlags & SaveS_ResetCurStateModified)
7563 {
7564 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
7565 mData->mCurrentStateModified = FALSE;
7566 fNeedsWrite = true; // always, no need to compare
7567 }
7568 else if (aFlags & SaveS_Force)
7569 {
7570 fNeedsWrite = true; // always, no need to compare
7571 }
7572 else
7573 {
7574 if (!mData->mCurrentStateModified)
7575 {
7576 // do a deep compare of the settings that we just saved with the settings
7577 // previously stored in the config file; this invokes MachineConfigFile::operator==
7578 // which does a deep compare of all the settings, which is expensive but less expensive
7579 // than writing out XML in vain
7580 bool fAnySettingsChanged = (*pNewConfig == *pOldConfig);
7581
7582 // could still be modified if any settings changed
7583 mData->mCurrentStateModified = fAnySettingsChanged;
7584
7585 fNeedsWrite = fAnySettingsChanged;
7586 }
7587 else
7588 fNeedsWrite = true;
7589 }
7590
7591 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
7592
7593 if (fNeedsWrite)
7594 // now spit it all out!
7595 pNewConfig->write(mData->m_strConfigFileFull);
7596
7597 mData->pMachineConfigFile = pNewConfig;
7598 delete pOldConfig;
7599 commit();
7600
7601 // after saving settings, we are no longer different from the XML on disk
7602 mData->flModifications = 0;
7603 }
7604 catch (HRESULT err)
7605 {
7606 // we assume that error info is set by the thrower
7607 rc = err;
7608
7609 // restore old config
7610 delete pNewConfig;
7611 mData->pMachineConfigFile = pOldConfig;
7612 }
7613 catch (...)
7614 {
7615 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
7616 }
7617
7618 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
7619 {
7620 /* Fire the data change event, even on failure (since we've already
7621 * committed all data). This is done only for SessionMachines because
7622 * mutable Machine instances are always not registered (i.e. private
7623 * to the client process that creates them) and thus don't need to
7624 * inform callbacks. */
7625 if (getClassID() == clsidSessionMachine)
7626 mParent->onMachineDataChange(mData->mUuid);
7627 }
7628
7629 LogFlowThisFunc(("rc=%08X\n", rc));
7630 LogFlowThisFuncLeave();
7631 return rc;
7632}
7633
7634/**
7635 * Implementation for saving the machine settings into the given
7636 * settings::MachineConfigFile instance. This copies machine extradata
7637 * from the previous machine config file in the instance data, if any.
7638 *
7639 * This gets called from two locations:
7640 *
7641 * -- Machine::saveSettings(), during the regular XML writing;
7642 *
7643 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
7644 * exported to OVF and we write the VirtualBox proprietary XML
7645 * into a <vbox:Machine> tag.
7646 *
7647 * This routine fills all the fields in there, including snapshots, *except*
7648 * for the following:
7649 *
7650 * -- fCurrentStateModified. There is some special logic associated with that.
7651 *
7652 * The caller can then call MachineConfigFile::write() or do something else
7653 * with it.
7654 *
7655 * Caller must hold the machine lock!
7656 *
7657 * This throws XML errors and HRESULT, so the caller must have a catch block!
7658 */
7659void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
7660{
7661 // deep copy extradata
7662 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
7663
7664 config.uuid = mData->mUuid;
7665 config.strName = mUserData->mName;
7666 config.fNameSync = !!mUserData->mNameSync;
7667 config.strDescription = mUserData->mDescription;
7668 config.strOsType = mUserData->mOSTypeId;
7669
7670 if ( mData->mMachineState == MachineState_Saved
7671 || mData->mMachineState == MachineState_Restoring
7672 // when deleting a snapshot we may or may not have a saved state in the current state,
7673 // so let's not assert here please
7674 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
7675 || mData->mMachineState == MachineState_DeletingSnapshotOnline
7676 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
7677 && (!mSSData->mStateFilePath.isEmpty())
7678 )
7679 )
7680 {
7681 Assert(!mSSData->mStateFilePath.isEmpty());
7682 /* try to make the file name relative to the settings file dir */
7683 calculateRelativePath(mSSData->mStateFilePath, config.strStateFile);
7684 }
7685 else
7686 {
7687 Assert(mSSData->mStateFilePath.isEmpty());
7688 config.strStateFile.setNull();
7689 }
7690
7691 if (mData->mCurrentSnapshot)
7692 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
7693 else
7694 config.uuidCurrentSnapshot.clear();
7695
7696 config.strSnapshotFolder = mUserData->mSnapshotFolder;
7697 // config.fCurrentStateModified is special, see below
7698 config.timeLastStateChange = mData->mLastStateChange;
7699 config.fAborted = (mData->mMachineState == MachineState_Aborted);
7700 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
7701
7702 config.fTeleporterEnabled = !!mUserData->mTeleporterEnabled;
7703 config.uTeleporterPort = mUserData->mTeleporterPort;
7704 config.strTeleporterAddress = mUserData->mTeleporterAddress;
7705 config.strTeleporterPassword = mUserData->mTeleporterPassword;
7706
7707 config.fRTCUseUTC = !!mUserData->mRTCUseUTC;
7708
7709 HRESULT rc = saveHardware(config.hardwareMachine);
7710 if (FAILED(rc)) throw rc;
7711
7712 rc = saveStorageControllers(config.storageMachine);
7713 if (FAILED(rc)) throw rc;
7714
7715 // save snapshots
7716 rc = saveAllSnapshots(config);
7717 if (FAILED(rc)) throw rc;
7718}
7719
7720/**
7721 * Saves all snapshots of the machine into the given machine config file. Called
7722 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
7723 * @param config
7724 * @return
7725 */
7726HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
7727{
7728 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
7729
7730 HRESULT rc = S_OK;
7731
7732 try
7733 {
7734 config.llFirstSnapshot.clear();
7735
7736 if (mData->mFirstSnapshot)
7737 {
7738 settings::Snapshot snapNew;
7739 config.llFirstSnapshot.push_back(snapNew);
7740
7741 // get reference to the fresh copy of the snapshot on the list and
7742 // work on that copy directly to avoid excessive copying later
7743 settings::Snapshot &snap = config.llFirstSnapshot.front();
7744
7745 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
7746 if (FAILED(rc)) throw rc;
7747 }
7748
7749// if (mType == IsSessionMachine)
7750// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
7751
7752 }
7753 catch (HRESULT err)
7754 {
7755 /* we assume that error info is set by the thrower */
7756 rc = err;
7757 }
7758 catch (...)
7759 {
7760 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
7761 }
7762
7763 return rc;
7764}
7765
7766/**
7767 * Saves the VM hardware configuration. It is assumed that the
7768 * given node is empty.
7769 *
7770 * @param aNode <Hardware> node to save the VM hardware confguration to.
7771 */
7772HRESULT Machine::saveHardware(settings::Hardware &data)
7773{
7774 HRESULT rc = S_OK;
7775
7776 try
7777 {
7778 /* The hardware version attribute (optional).
7779 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
7780 if ( mHWData->mHWVersion == "1"
7781 && mSSData->mStateFilePath.isEmpty()
7782 )
7783 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. */
7784
7785 data.strVersion = mHWData->mHWVersion;
7786 data.uuid = mHWData->mHardwareUUID;
7787
7788 // CPU
7789 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
7790 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
7791 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
7792 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
7793 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
7794 data.fPAE = !!mHWData->mPAEEnabled;
7795 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
7796
7797 /* Standard and Extended CPUID leafs. */
7798 data.llCpuIdLeafs.clear();
7799 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
7800 {
7801 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
7802 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
7803 }
7804 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
7805 {
7806 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
7807 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
7808 }
7809
7810 data.cCPUs = mHWData->mCPUCount;
7811 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
7812
7813 data.llCpus.clear();
7814 if (data.fCpuHotPlug)
7815 {
7816 for (unsigned idx = 0; idx < data.cCPUs; idx++)
7817 {
7818 if (mHWData->mCPUAttached[idx])
7819 {
7820 settings::Cpu cpu;
7821 cpu.ulId = idx;
7822 data.llCpus.push_back(cpu);
7823 }
7824 }
7825 }
7826
7827 // memory
7828 data.ulMemorySizeMB = mHWData->mMemorySize;
7829
7830 // firmware
7831 data.firmwareType = mHWData->mFirmwareType;
7832
7833 // HID
7834 data.pointingHidType = mHWData->mPointingHidType;
7835 data.keyboardHidType = mHWData->mKeyboardHidType;
7836
7837 // HPET
7838 data.fHpetEnabled = !!mHWData->mHpetEnabled;
7839
7840 // boot order
7841 data.mapBootOrder.clear();
7842 for (size_t i = 0;
7843 i < RT_ELEMENTS(mHWData->mBootOrder);
7844 ++i)
7845 data.mapBootOrder[i] = mHWData->mBootOrder[i];
7846
7847 // display
7848 data.ulVRAMSizeMB = mHWData->mVRAMSize;
7849 data.cMonitors = mHWData->mMonitorCount;
7850 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
7851 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
7852
7853#ifdef VBOX_WITH_VRDP
7854 /* VRDP settings (optional) */
7855 rc = mVRDPServer->saveSettings(data.vrdpSettings);
7856 if (FAILED(rc)) throw rc;
7857#endif
7858
7859 /* BIOS (required) */
7860 rc = mBIOSSettings->saveSettings(data.biosSettings);
7861 if (FAILED(rc)) throw rc;
7862
7863 /* USB Controller (required) */
7864 rc = mUSBController->saveSettings(data.usbController);
7865 if (FAILED(rc)) throw rc;
7866
7867 /* Network adapters (required) */
7868 data.llNetworkAdapters.clear();
7869 for (ULONG slot = 0;
7870 slot < RT_ELEMENTS(mNetworkAdapters);
7871 ++slot)
7872 {
7873 settings::NetworkAdapter nic;
7874 nic.ulSlot = slot;
7875 rc = mNetworkAdapters[slot]->saveSettings(nic);
7876 if (FAILED(rc)) throw rc;
7877
7878 data.llNetworkAdapters.push_back(nic);
7879 }
7880
7881 /* Serial ports */
7882 data.llSerialPorts.clear();
7883 for (ULONG slot = 0;
7884 slot < RT_ELEMENTS(mSerialPorts);
7885 ++slot)
7886 {
7887 settings::SerialPort s;
7888 s.ulSlot = slot;
7889 rc = mSerialPorts[slot]->saveSettings(s);
7890 if (FAILED(rc)) return rc;
7891
7892 data.llSerialPorts.push_back(s);
7893 }
7894
7895 /* Parallel ports */
7896 data.llParallelPorts.clear();
7897 for (ULONG slot = 0;
7898 slot < RT_ELEMENTS(mParallelPorts);
7899 ++slot)
7900 {
7901 settings::ParallelPort p;
7902 p.ulSlot = slot;
7903 rc = mParallelPorts[slot]->saveSettings(p);
7904 if (FAILED(rc)) return rc;
7905
7906 data.llParallelPorts.push_back(p);
7907 }
7908
7909 /* Audio adapter */
7910 rc = mAudioAdapter->saveSettings(data.audioAdapter);
7911 if (FAILED(rc)) return rc;
7912
7913 /* Shared folders */
7914 data.llSharedFolders.clear();
7915 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
7916 it != mHWData->mSharedFolders.end();
7917 ++it)
7918 {
7919 ComObjPtr<SharedFolder> pFolder = *it;
7920 settings::SharedFolder sf;
7921 sf.strName = pFolder->getName();
7922 sf.strHostPath = pFolder->getHostPath();
7923 sf.fWritable = !!pFolder->isWritable();
7924
7925 data.llSharedFolders.push_back(sf);
7926 }
7927
7928 // clipboard
7929 data.clipboardMode = mHWData->mClipboardMode;
7930
7931 /* Guest */
7932 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
7933
7934 // IO settings
7935 data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled;
7936 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
7937 data.ioSettings.ulIoBandwidthMax = mHWData->mIoBandwidthMax;
7938
7939 // guest properties
7940 data.llGuestProperties.clear();
7941#ifdef VBOX_WITH_GUEST_PROPS
7942 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
7943 it != mHWData->mGuestProperties.end();
7944 ++it)
7945 {
7946 HWData::GuestProperty property = *it;
7947
7948 /* Remove transient guest properties at shutdown unless we
7949 * are saving state */
7950 if ( ( mData->mMachineState == MachineState_PoweredOff
7951 || mData->mMachineState == MachineState_Aborted
7952 || mData->mMachineState == MachineState_Teleported)
7953 && property.mFlags & guestProp::TRANSIENT)
7954 continue;
7955 settings::GuestProperty prop;
7956 prop.strName = property.strName;
7957 prop.strValue = property.strValue;
7958 prop.timestamp = property.mTimestamp;
7959 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
7960 guestProp::writeFlags(property.mFlags, szFlags);
7961 prop.strFlags = szFlags;
7962
7963 data.llGuestProperties.push_back(prop);
7964 }
7965
7966 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
7967 /* I presume this doesn't require a backup(). */
7968 mData->mGuestPropertiesModified = FALSE;
7969#endif /* VBOX_WITH_GUEST_PROPS defined */
7970 }
7971 catch(std::bad_alloc &)
7972 {
7973 return E_OUTOFMEMORY;
7974 }
7975
7976 AssertComRC(rc);
7977 return rc;
7978}
7979
7980/**
7981 * Saves the storage controller configuration.
7982 *
7983 * @param aNode <StorageControllers> node to save the VM hardware confguration to.
7984 */
7985HRESULT Machine::saveStorageControllers(settings::Storage &data)
7986{
7987 data.llStorageControllers.clear();
7988
7989 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
7990 it != mStorageControllers->end();
7991 ++it)
7992 {
7993 HRESULT rc;
7994 ComObjPtr<StorageController> pCtl = *it;
7995
7996 settings::StorageController ctl;
7997 ctl.strName = pCtl->getName();
7998 ctl.controllerType = pCtl->getControllerType();
7999 ctl.storageBus = pCtl->getStorageBus();
8000 ctl.ulInstance = pCtl->getInstance();
8001
8002 /* Save the port count. */
8003 ULONG portCount;
8004 rc = pCtl->COMGETTER(PortCount)(&portCount);
8005 ComAssertComRCRet(rc, rc);
8006 ctl.ulPortCount = portCount;
8007
8008 /* Save I/O backend */
8009 IoBackendType_T ioBackendType;
8010 rc = pCtl->COMGETTER(IoBackend)(&ioBackendType);
8011 ComAssertComRCRet(rc, rc);
8012 ctl.ioBackendType = ioBackendType;
8013
8014 /* Save IDE emulation settings. */
8015 if (ctl.controllerType == StorageControllerType_IntelAhci)
8016 {
8017 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
8018 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
8019 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
8020 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
8021 )
8022 ComAssertComRCRet(rc, rc);
8023 }
8024
8025 /* save the devices now. */
8026 rc = saveStorageDevices(pCtl, ctl);
8027 ComAssertComRCRet(rc, rc);
8028
8029 data.llStorageControllers.push_back(ctl);
8030 }
8031
8032 return S_OK;
8033}
8034
8035/**
8036 * Saves the hard disk confguration.
8037 */
8038HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
8039 settings::StorageController &data)
8040{
8041 MediaData::AttachmentList atts;
8042
8043 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()), atts);
8044 if (FAILED(rc)) return rc;
8045
8046 data.llAttachedDevices.clear();
8047 for (MediaData::AttachmentList::const_iterator it = atts.begin();
8048 it != atts.end();
8049 ++it)
8050 {
8051 settings::AttachedDevice dev;
8052
8053 MediumAttachment *pAttach = *it;
8054 Medium *pMedium = pAttach->getMedium();
8055
8056 dev.deviceType = pAttach->getType();
8057 dev.lPort = pAttach->getPort();
8058 dev.lDevice = pAttach->getDevice();
8059 if (pMedium)
8060 {
8061 BOOL fHostDrive = FALSE;
8062 rc = pMedium->COMGETTER(HostDrive)(&fHostDrive);
8063 if (FAILED(rc))
8064 return rc;
8065 if (fHostDrive)
8066 dev.strHostDriveSrc = pMedium->getLocation();
8067 else
8068 dev.uuid = pMedium->getId();
8069 dev.fPassThrough = pAttach->getPassthrough();
8070 }
8071
8072 data.llAttachedDevices.push_back(dev);
8073 }
8074
8075 return S_OK;
8076}
8077
8078/**
8079 * Saves machine state settings as defined by aFlags
8080 * (SaveSTS_* values).
8081 *
8082 * @param aFlags Combination of SaveSTS_* flags.
8083 *
8084 * @note Locks objects for writing.
8085 */
8086HRESULT Machine::saveStateSettings(int aFlags)
8087{
8088 if (aFlags == 0)
8089 return S_OK;
8090
8091 AutoCaller autoCaller(this);
8092 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8093
8094 /* This object's write lock is also necessary to serialize file access
8095 * (prevent concurrent reads and writes) */
8096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8097
8098 HRESULT rc = S_OK;
8099
8100 Assert(mData->pMachineConfigFile);
8101
8102 try
8103 {
8104 if (aFlags & SaveSTS_CurStateModified)
8105 mData->pMachineConfigFile->fCurrentStateModified = true;
8106
8107 if (aFlags & SaveSTS_StateFilePath)
8108 {
8109 if (!mSSData->mStateFilePath.isEmpty())
8110 /* try to make the file name relative to the settings file dir */
8111 calculateRelativePath(mSSData->mStateFilePath, mData->pMachineConfigFile->strStateFile);
8112 else
8113 mData->pMachineConfigFile->strStateFile.setNull();
8114 }
8115
8116 if (aFlags & SaveSTS_StateTimeStamp)
8117 {
8118 Assert( mData->mMachineState != MachineState_Aborted
8119 || mSSData->mStateFilePath.isEmpty());
8120
8121 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
8122
8123 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
8124//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
8125 }
8126
8127 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
8128 }
8129 catch (...)
8130 {
8131 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
8132 }
8133
8134 return rc;
8135}
8136
8137/**
8138 * Creates differencing hard disks for all normal hard disks attached to this
8139 * machine and a new set of attachments to refer to created disks.
8140 *
8141 * Used when taking a snapshot or when deleting the current state.
8142 *
8143 * This method assumes that mMediaData contains the original hard disk attachments
8144 * it needs to create diffs for. On success, these attachments will be replaced
8145 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
8146 * called to delete created diffs which will also rollback mMediaData and restore
8147 * whatever was backed up before calling this method.
8148 *
8149 * Attachments with non-normal hard disks are left as is.
8150 *
8151 * If @a aOnline is @c false then the original hard disks that require implicit
8152 * diffs will be locked for reading. Otherwise it is assumed that they are
8153 * already locked for writing (when the VM was started). Note that in the latter
8154 * case it is responsibility of the caller to lock the newly created diffs for
8155 * writing if this method succeeds.
8156 *
8157 * @param aFolder Folder where to create diff hard disks.
8158 * @param aProgress Progress object to run (must contain at least as
8159 * many operations left as the number of hard disks
8160 * attached).
8161 * @param aOnline Whether the VM was online prior to this operation.
8162 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
8163 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
8164 *
8165 * @note The progress object is not marked as completed, neither on success nor
8166 * on failure. This is a responsibility of the caller.
8167 *
8168 * @note Locks this object for writing.
8169 */
8170HRESULT Machine::createImplicitDiffs(const Bstr &aFolder,
8171 IProgress *aProgress,
8172 ULONG aWeight,
8173 bool aOnline,
8174 bool *pfNeedsSaveSettings)
8175{
8176 AssertReturn(!aFolder.isEmpty(), E_FAIL);
8177
8178 LogFlowThisFunc(("aFolder='%ls', aOnline=%d\n", aFolder.raw(), aOnline));
8179
8180 AutoCaller autoCaller(this);
8181 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8182
8183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8184
8185 /* must be in a protective state because we leave the lock below */
8186 AssertReturn( mData->mMachineState == MachineState_Saving
8187 || mData->mMachineState == MachineState_LiveSnapshotting
8188 || mData->mMachineState == MachineState_RestoringSnapshot
8189 || mData->mMachineState == MachineState_DeletingSnapshot
8190 , E_FAIL);
8191
8192 HRESULT rc = S_OK;
8193
8194 MediumLockListMap lockedMediaOffline;
8195 MediumLockListMap *lockedMediaMap;
8196 if (aOnline)
8197 lockedMediaMap = &mData->mSession.mLockedMedia;
8198 else
8199 lockedMediaMap = &lockedMediaOffline;
8200
8201 try
8202 {
8203 if (!aOnline)
8204 {
8205 /* lock all attached hard disks early to detect "in use"
8206 * situations before creating actual diffs */
8207 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8208 it != mMediaData->mAttachments.end();
8209 ++it)
8210 {
8211 MediumAttachment* pAtt = *it;
8212 if (pAtt->getType() == DeviceType_HardDisk)
8213 {
8214 Medium* pMedium = pAtt->getMedium();
8215 Assert(pMedium);
8216
8217 MediumLockList *pMediumLockList(new MediumLockList());
8218 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
8219 false /* fMediumLockWrite */,
8220 NULL,
8221 *pMediumLockList);
8222 if (FAILED(rc))
8223 {
8224 delete pMediumLockList;
8225 throw rc;
8226 }
8227 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
8228 if (FAILED(rc))
8229 {
8230 throw setError(rc,
8231 tr("Collecting locking information for all attached media failed"));
8232 }
8233 }
8234 }
8235
8236 /* Now lock all media. If this fails, nothing is locked. */
8237 rc = lockedMediaMap->Lock();
8238 if (FAILED(rc))
8239 {
8240 throw setError(rc,
8241 tr("Locking of attached media failed"));
8242 }
8243 }
8244
8245 /* remember the current list (note that we don't use backup() since
8246 * mMediaData may be already backed up) */
8247 MediaData::AttachmentList atts = mMediaData->mAttachments;
8248
8249 /* start from scratch */
8250 mMediaData->mAttachments.clear();
8251
8252 /* go through remembered attachments and create diffs for normal hard
8253 * disks and attach them */
8254 for (MediaData::AttachmentList::const_iterator it = atts.begin();
8255 it != atts.end();
8256 ++it)
8257 {
8258 MediumAttachment* pAtt = *it;
8259
8260 DeviceType_T devType = pAtt->getType();
8261 Medium* pMedium = pAtt->getMedium();
8262
8263 if ( devType != DeviceType_HardDisk
8264 || pMedium == NULL
8265 || pMedium->getType() != MediumType_Normal)
8266 {
8267 /* copy the attachment as is */
8268
8269 /** @todo the progress object created in Console::TakeSnaphot
8270 * only expects operations for hard disks. Later other
8271 * device types need to show up in the progress as well. */
8272 if (devType == DeviceType_HardDisk)
8273 {
8274 if (pMedium == NULL)
8275 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")),
8276 aWeight); // weight
8277 else
8278 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
8279 pMedium->getBase()->getName().raw()),
8280 aWeight); // weight
8281 }
8282
8283 mMediaData->mAttachments.push_back(pAtt);
8284 continue;
8285 }
8286
8287 /* need a diff */
8288 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
8289 pMedium->getBase()->getName().raw()),
8290 aWeight); // weight
8291
8292 ComObjPtr<Medium> diff;
8293 diff.createObject();
8294 rc = diff->init(mParent,
8295 pMedium->preferredDiffFormat().raw(),
8296 BstrFmt("%ls"RTPATH_SLASH_STR,
8297 mUserData->mSnapshotFolderFull.raw()).raw(),
8298 pfNeedsSaveSettings);
8299 if (FAILED(rc)) throw rc;
8300
8301 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
8302 * the push_back? Looks like we're going to leave medium with the
8303 * wrong kind of lock (general issue with if we fail anywhere at all)
8304 * and an orphaned VDI in the snapshots folder. */
8305
8306 /* update the appropriate lock list */
8307 MediumLockList *pMediumLockList;
8308 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
8309 AssertComRCThrowRC(rc);
8310 if (aOnline)
8311 {
8312 rc = pMediumLockList->Update(pMedium, false);
8313 AssertComRCThrowRC(rc);
8314 }
8315
8316 /* leave the lock before the potentially lengthy operation */
8317 alock.leave();
8318 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
8319 pMediumLockList,
8320 NULL /* aProgress */,
8321 true /* aWait */,
8322 pfNeedsSaveSettings);
8323 alock.enter();
8324 if (FAILED(rc)) throw rc;
8325
8326 rc = lockedMediaMap->Unlock();
8327 AssertComRCThrowRC(rc);
8328 rc = pMediumLockList->Append(diff, true);
8329 AssertComRCThrowRC(rc);
8330 rc = lockedMediaMap->Lock();
8331 AssertComRCThrowRC(rc);
8332
8333 rc = diff->attachTo(mData->mUuid);
8334 AssertComRCThrowRC(rc);
8335
8336 /* add a new attachment */
8337 ComObjPtr<MediumAttachment> attachment;
8338 attachment.createObject();
8339 rc = attachment->init(this,
8340 diff,
8341 pAtt->getControllerName(),
8342 pAtt->getPort(),
8343 pAtt->getDevice(),
8344 DeviceType_HardDisk,
8345 true /* aImplicit */);
8346 if (FAILED(rc)) throw rc;
8347
8348 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
8349 AssertComRCThrowRC(rc);
8350 mMediaData->mAttachments.push_back(attachment);
8351 }
8352 }
8353 catch (HRESULT aRC) { rc = aRC; }
8354
8355 /* unlock all hard disks we locked */
8356 if (!aOnline)
8357 {
8358 ErrorInfoKeeper eik;
8359
8360 rc = lockedMediaMap->Clear();
8361 AssertComRC(rc);
8362 }
8363
8364 if (FAILED(rc))
8365 {
8366 MultiResultRef mrc(rc);
8367
8368 mrc = deleteImplicitDiffs(pfNeedsSaveSettings);
8369 }
8370
8371 return rc;
8372}
8373
8374/**
8375 * Deletes implicit differencing hard disks created either by
8376 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
8377 *
8378 * Note that to delete hard disks created by #AttachMedium() this method is
8379 * called from #fixupMedia() when the changes are rolled back.
8380 *
8381 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
8382 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
8383 *
8384 * @note Locks this object for writing.
8385 */
8386HRESULT Machine::deleteImplicitDiffs(bool *pfNeedsSaveSettings)
8387{
8388 AutoCaller autoCaller(this);
8389 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8390
8391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8392 LogFlowThisFuncEnter();
8393
8394 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
8395
8396 HRESULT rc = S_OK;
8397
8398 MediaData::AttachmentList implicitAtts;
8399
8400 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
8401
8402 /* enumerate new attachments */
8403 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8404 it != mMediaData->mAttachments.end();
8405 ++it)
8406 {
8407 ComObjPtr<Medium> hd = (*it)->getMedium();
8408 if (hd.isNull())
8409 continue;
8410
8411 if ((*it)->isImplicit())
8412 {
8413 /* deassociate and mark for deletion */
8414 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
8415 rc = hd->detachFrom(mData->mUuid);
8416 AssertComRC(rc);
8417 implicitAtts.push_back(*it);
8418 continue;
8419 }
8420
8421 /* was this hard disk attached before? */
8422 if (!findAttachment(oldAtts, hd))
8423 {
8424 /* no: de-associate */
8425 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
8426 rc = hd->detachFrom(mData->mUuid);
8427 AssertComRC(rc);
8428 continue;
8429 }
8430 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
8431 }
8432
8433 /* rollback hard disk changes */
8434 mMediaData.rollback();
8435
8436 MultiResult mrc(S_OK);
8437
8438 /* delete unused implicit diffs */
8439 if (implicitAtts.size() != 0)
8440 {
8441 /* will leave the lock before the potentially lengthy
8442 * operation, so protect with the special state (unless already
8443 * protected) */
8444 MachineState_T oldState = mData->mMachineState;
8445 if ( oldState != MachineState_Saving
8446 && oldState != MachineState_LiveSnapshotting
8447 && oldState != MachineState_RestoringSnapshot
8448 && oldState != MachineState_DeletingSnapshot
8449 && oldState != MachineState_DeletingSnapshotOnline
8450 && oldState != MachineState_DeletingSnapshotPaused
8451 )
8452 setMachineState(MachineState_SettingUp);
8453
8454 alock.leave();
8455
8456 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
8457 it != implicitAtts.end();
8458 ++it)
8459 {
8460 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
8461 ComObjPtr<Medium> hd = (*it)->getMedium();
8462
8463 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/,
8464 pfNeedsSaveSettings);
8465 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
8466 mrc = rc;
8467 }
8468
8469 alock.enter();
8470
8471 if (mData->mMachineState == MachineState_SettingUp)
8472 {
8473 setMachineState(oldState);
8474 }
8475 }
8476
8477 return mrc;
8478}
8479
8480/**
8481 * Looks through the given list of media attachments for one with the given parameters
8482 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8483 * can be searched as well if needed.
8484 *
8485 * @param list
8486 * @param aControllerName
8487 * @param aControllerPort
8488 * @param aDevice
8489 * @return
8490 */
8491MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8492 IN_BSTR aControllerName,
8493 LONG aControllerPort,
8494 LONG aDevice)
8495{
8496 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8497 it != ll.end();
8498 ++it)
8499 {
8500 MediumAttachment *pAttach = *it;
8501 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
8502 return pAttach;
8503 }
8504
8505 return NULL;
8506}
8507
8508/**
8509 * Looks through the given list of media attachments for one with the given parameters
8510 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8511 * can be searched as well if needed.
8512 *
8513 * @param list
8514 * @param aControllerName
8515 * @param aControllerPort
8516 * @param aDevice
8517 * @return
8518 */
8519MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8520 ComObjPtr<Medium> pMedium)
8521{
8522 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8523 it != ll.end();
8524 ++it)
8525 {
8526 MediumAttachment *pAttach = *it;
8527 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
8528 if (pMediumThis.equalsTo(pMedium))
8529 return pAttach;
8530 }
8531
8532 return NULL;
8533}
8534
8535/**
8536 * Looks through the given list of media attachments for one with the given parameters
8537 * and returns it, or NULL if not found. The list is a parameter so that backup lists
8538 * can be searched as well if needed.
8539 *
8540 * @param list
8541 * @param aControllerName
8542 * @param aControllerPort
8543 * @param aDevice
8544 * @return
8545 */
8546MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
8547 Guid &id)
8548{
8549 for (MediaData::AttachmentList::const_iterator it = ll.begin();
8550 it != ll.end();
8551 ++it)
8552 {
8553 MediumAttachment *pAttach = *it;
8554 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
8555 if (pMediumThis->getId() == id)
8556 return pAttach;
8557 }
8558
8559 return NULL;
8560}
8561
8562/**
8563 * Perform deferred hard disk detachments.
8564 *
8565 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
8566 * backed up).
8567 *
8568 * If @a aOnline is @c true then this method will also unlock the old hard disks
8569 * for which the new implicit diffs were created and will lock these new diffs for
8570 * writing.
8571 *
8572 * @param aOnline Whether the VM was online prior to this operation.
8573 *
8574 * @note Locks this object for writing!
8575 */
8576void Machine::commitMedia(bool aOnline /*= false*/)
8577{
8578 AutoCaller autoCaller(this);
8579 AssertComRCReturnVoid(autoCaller.rc());
8580
8581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8582
8583 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
8584
8585 HRESULT rc = S_OK;
8586
8587 /* no attach/detach operations -- nothing to do */
8588 if (!mMediaData.isBackedUp())
8589 return;
8590
8591 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
8592 bool fMediaNeedsLocking = false;
8593
8594 /* enumerate new attachments */
8595 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8596 it != mMediaData->mAttachments.end();
8597 ++it)
8598 {
8599 MediumAttachment *pAttach = *it;
8600
8601 pAttach->commit();
8602
8603 Medium* pMedium = pAttach->getMedium();
8604 bool fImplicit = pAttach->isImplicit();
8605
8606 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
8607 (pMedium) ? pMedium->getName().raw() : "NULL",
8608 fImplicit));
8609
8610 /** @todo convert all this Machine-based voodoo to MediumAttachment
8611 * based commit logic. */
8612 if (fImplicit)
8613 {
8614 /* convert implicit attachment to normal */
8615 pAttach->setImplicit(false);
8616
8617 if ( aOnline
8618 && pMedium
8619 && pAttach->getType() == DeviceType_HardDisk
8620 )
8621 {
8622 ComObjPtr<Medium> parent = pMedium->getParent();
8623 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
8624
8625 /* update the appropriate lock list */
8626 MediumLockList *pMediumLockList;
8627 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
8628 AssertComRC(rc);
8629 if (pMediumLockList)
8630 {
8631 /* unlock if there's a need to change the locking */
8632 if (!fMediaNeedsLocking)
8633 {
8634 rc = mData->mSession.mLockedMedia.Unlock();
8635 AssertComRC(rc);
8636 fMediaNeedsLocking = true;
8637 }
8638 rc = pMediumLockList->Update(parent, false);
8639 AssertComRC(rc);
8640 rc = pMediumLockList->Append(pMedium, true);
8641 AssertComRC(rc);
8642 }
8643 }
8644
8645 continue;
8646 }
8647
8648 if (pMedium)
8649 {
8650 /* was this medium attached before? */
8651 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
8652 oldIt != oldAtts.end();
8653 ++oldIt)
8654 {
8655 MediumAttachment *pOldAttach = *oldIt;
8656 if (pOldAttach->getMedium().equalsTo(pMedium))
8657 {
8658 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().raw()));
8659
8660 /* yes: remove from old to avoid de-association */
8661 oldAtts.erase(oldIt);
8662 break;
8663 }
8664 }
8665 }
8666 }
8667
8668 /* enumerate remaining old attachments and de-associate from the
8669 * current machine state */
8670 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
8671 it != oldAtts.end();
8672 ++it)
8673 {
8674 MediumAttachment *pAttach = *it;
8675 Medium* pMedium = pAttach->getMedium();
8676
8677 /* Detach only hard disks, since DVD/floppy media is detached
8678 * instantly in MountMedium. */
8679 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
8680 {
8681 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().raw()));
8682
8683 /* now de-associate from the current machine state */
8684 rc = pMedium->detachFrom(mData->mUuid);
8685 AssertComRC(rc);
8686
8687 if (aOnline)
8688 {
8689 /* unlock since medium is not used anymore */
8690 MediumLockList *pMediumLockList;
8691 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
8692 AssertComRC(rc);
8693 if (pMediumLockList)
8694 {
8695 rc = mData->mSession.mLockedMedia.Remove(pAttach);
8696 AssertComRC(rc);
8697 }
8698 }
8699 }
8700 }
8701
8702 /* take media locks again so that the locking state is consistent */
8703 if (fMediaNeedsLocking)
8704 {
8705 Assert(aOnline);
8706 rc = mData->mSession.mLockedMedia.Lock();
8707 AssertComRC(rc);
8708 }
8709
8710 /* commit the hard disk changes */
8711 mMediaData.commit();
8712
8713 if (getClassID() == clsidSessionMachine)
8714 {
8715 /* attach new data to the primary machine and reshare it */
8716 mPeer->mMediaData.attach(mMediaData);
8717 }
8718
8719 return;
8720}
8721
8722/**
8723 * Perform deferred deletion of implicitly created diffs.
8724 *
8725 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
8726 * backed up).
8727 *
8728 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
8729 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
8730 *
8731 * @note Locks this object for writing!
8732 */
8733void Machine::rollbackMedia()
8734{
8735 AutoCaller autoCaller(this);
8736 AssertComRCReturnVoid (autoCaller.rc());
8737
8738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8739
8740 LogFlowThisFunc(("Entering\n"));
8741
8742 HRESULT rc = S_OK;
8743
8744 /* no attach/detach operations -- nothing to do */
8745 if (!mMediaData.isBackedUp())
8746 return;
8747
8748 /* enumerate new attachments */
8749 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8750 it != mMediaData->mAttachments.end();
8751 ++it)
8752 {
8753 MediumAttachment *pAttach = *it;
8754 /* Fix up the backrefs for DVD/floppy media. */
8755 if (pAttach->getType() != DeviceType_HardDisk)
8756 {
8757 Medium* pMedium = pAttach->getMedium();
8758 if (pMedium)
8759 {
8760 rc = pMedium->detachFrom(mData->mUuid);
8761 AssertComRC(rc);
8762 }
8763 }
8764
8765 (*it)->rollback();
8766
8767 pAttach = *it;
8768 /* Fix up the backrefs for DVD/floppy media. */
8769 if (pAttach->getType() != DeviceType_HardDisk)
8770 {
8771 Medium* pMedium = pAttach->getMedium();
8772 if (pMedium)
8773 {
8774 rc = pMedium->attachTo(mData->mUuid);
8775 AssertComRC(rc);
8776 }
8777 }
8778 }
8779
8780 /** @todo convert all this Machine-based voodoo to MediumAttachment
8781 * based rollback logic. */
8782 // @todo r=dj the below totally fails if this gets called from Machine::rollback(),
8783 // which gets called if Machine::registeredInit() fails...
8784 deleteImplicitDiffs(NULL /*pfNeedsSaveSettings*/);
8785
8786 return;
8787}
8788
8789/**
8790 * Returns true if the settings file is located in the directory named exactly
8791 * as the machine. This will be true if the machine settings structure was
8792 * created by default in #openConfigLoader().
8793 *
8794 * @param aSettingsDir if not NULL, the full machine settings file directory
8795 * name will be assigned there.
8796 *
8797 * @note Doesn't lock anything.
8798 * @note Not thread safe (must be called from this object's lock).
8799 */
8800bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
8801{
8802 Utf8Str settingsDir = mData->m_strConfigFileFull;
8803 settingsDir.stripFilename();
8804 char *dirName = RTPathFilename(settingsDir.c_str());
8805
8806 AssertReturn(dirName, false);
8807
8808 /* if we don't rename anything on name change, return false shorlty */
8809 if (!mUserData->mNameSync)
8810 return false;
8811
8812 if (aSettingsDir)
8813 *aSettingsDir = settingsDir;
8814
8815 return Bstr(dirName) == mUserData->mName;
8816}
8817
8818/**
8819 * Discards all changes to machine settings.
8820 *
8821 * @param aNotify Whether to notify the direct session about changes or not.
8822 *
8823 * @note Locks objects for writing!
8824 */
8825void Machine::rollback(bool aNotify)
8826{
8827 AutoCaller autoCaller(this);
8828 AssertComRCReturn(autoCaller.rc(), (void)0);
8829
8830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8831
8832 if (!mStorageControllers.isNull())
8833 {
8834 if (mStorageControllers.isBackedUp())
8835 {
8836 /* unitialize all new devices (absent in the backed up list). */
8837 StorageControllerList::const_iterator it = mStorageControllers->begin();
8838 StorageControllerList *backedList = mStorageControllers.backedUpData();
8839 while (it != mStorageControllers->end())
8840 {
8841 if ( std::find(backedList->begin(), backedList->end(), *it)
8842 == backedList->end()
8843 )
8844 {
8845 (*it)->uninit();
8846 }
8847 ++it;
8848 }
8849
8850 /* restore the list */
8851 mStorageControllers.rollback();
8852 }
8853
8854 /* rollback any changes to devices after restoring the list */
8855 if (mData->flModifications & IsModified_Storage)
8856 {
8857 StorageControllerList::const_iterator it = mStorageControllers->begin();
8858 while (it != mStorageControllers->end())
8859 {
8860 (*it)->rollback();
8861 ++it;
8862 }
8863 }
8864 }
8865
8866 mUserData.rollback();
8867
8868 mHWData.rollback();
8869
8870 if (mData->flModifications & IsModified_Storage)
8871 rollbackMedia();
8872
8873 if (mBIOSSettings)
8874 mBIOSSettings->rollback();
8875
8876#ifdef VBOX_WITH_VRDP
8877 if (mVRDPServer && (mData->flModifications & IsModified_VRDPServer))
8878 mVRDPServer->rollback();
8879#endif
8880
8881 if (mAudioAdapter)
8882 mAudioAdapter->rollback();
8883
8884 if (mUSBController && (mData->flModifications & IsModified_USB))
8885 mUSBController->rollback();
8886
8887 ComPtr<INetworkAdapter> networkAdapters[RT_ELEMENTS(mNetworkAdapters)];
8888 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
8889 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
8890
8891 if (mData->flModifications & IsModified_NetworkAdapters)
8892 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
8893 if ( mNetworkAdapters[slot]
8894 && mNetworkAdapters[slot]->isModified())
8895 {
8896 mNetworkAdapters[slot]->rollback();
8897 networkAdapters[slot] = mNetworkAdapters[slot];
8898 }
8899
8900 if (mData->flModifications & IsModified_SerialPorts)
8901 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8902 if ( mSerialPorts[slot]
8903 && mSerialPorts[slot]->isModified())
8904 {
8905 mSerialPorts[slot]->rollback();
8906 serialPorts[slot] = mSerialPorts[slot];
8907 }
8908
8909 if (mData->flModifications & IsModified_ParallelPorts)
8910 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8911 if ( mParallelPorts[slot]
8912 && mParallelPorts[slot]->isModified())
8913 {
8914 mParallelPorts[slot]->rollback();
8915 parallelPorts[slot] = mParallelPorts[slot];
8916 }
8917
8918 if (aNotify)
8919 {
8920 /* inform the direct session about changes */
8921
8922 ComObjPtr<Machine> that = this;
8923 uint32_t flModifications = mData->flModifications;
8924 alock.leave();
8925
8926 if (flModifications & IsModified_SharedFolders)
8927 that->onSharedFolderChange();
8928
8929 if (flModifications & IsModified_VRDPServer)
8930 that->onVRDPServerChange(/* aRestart */ TRUE);
8931 if (flModifications & IsModified_USB)
8932 that->onUSBControllerChange();
8933
8934 for (ULONG slot = 0; slot < RT_ELEMENTS(networkAdapters); slot ++)
8935 if (networkAdapters[slot])
8936 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
8937 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot ++)
8938 if (serialPorts[slot])
8939 that->onSerialPortChange(serialPorts[slot]);
8940 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot ++)
8941 if (parallelPorts[slot])
8942 that->onParallelPortChange(parallelPorts[slot]);
8943
8944 if (flModifications & IsModified_Storage)
8945 that->onStorageControllerChange();
8946 }
8947}
8948
8949/**
8950 * Commits all the changes to machine settings.
8951 *
8952 * Note that this operation is supposed to never fail.
8953 *
8954 * @note Locks this object and children for writing.
8955 */
8956void Machine::commit()
8957{
8958 AutoCaller autoCaller(this);
8959 AssertComRCReturnVoid(autoCaller.rc());
8960
8961 AutoCaller peerCaller(mPeer);
8962 AssertComRCReturnVoid(peerCaller.rc());
8963
8964 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
8965
8966 /*
8967 * use safe commit to ensure Snapshot machines (that share mUserData)
8968 * will still refer to a valid memory location
8969 */
8970 mUserData.commitCopy();
8971
8972 mHWData.commit();
8973
8974 if (mMediaData.isBackedUp())
8975 commitMedia();
8976
8977 mBIOSSettings->commit();
8978#ifdef VBOX_WITH_VRDP
8979 mVRDPServer->commit();
8980#endif
8981 mAudioAdapter->commit();
8982 mUSBController->commit();
8983
8984 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
8985 mNetworkAdapters[slot]->commit();
8986 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8987 mSerialPorts[slot]->commit();
8988 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8989 mParallelPorts[slot]->commit();
8990
8991 bool commitStorageControllers = false;
8992
8993 if (mStorageControllers.isBackedUp())
8994 {
8995 mStorageControllers.commit();
8996
8997 if (mPeer)
8998 {
8999 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
9000
9001 /* Commit all changes to new controllers (this will reshare data with
9002 * peers for thos who have peers) */
9003 StorageControllerList *newList = new StorageControllerList();
9004 StorageControllerList::const_iterator it = mStorageControllers->begin();
9005 while (it != mStorageControllers->end())
9006 {
9007 (*it)->commit();
9008
9009 /* look if this controller has a peer device */
9010 ComObjPtr<StorageController> peer = (*it)->getPeer();
9011 if (!peer)
9012 {
9013 /* no peer means the device is a newly created one;
9014 * create a peer owning data this device share it with */
9015 peer.createObject();
9016 peer->init(mPeer, *it, true /* aReshare */);
9017 }
9018 else
9019 {
9020 /* remove peer from the old list */
9021 mPeer->mStorageControllers->remove(peer);
9022 }
9023 /* and add it to the new list */
9024 newList->push_back(peer);
9025
9026 ++it;
9027 }
9028
9029 /* uninit old peer's controllers that are left */
9030 it = mPeer->mStorageControllers->begin();
9031 while (it != mPeer->mStorageControllers->end())
9032 {
9033 (*it)->uninit();
9034 ++it;
9035 }
9036
9037 /* attach new list of controllers to our peer */
9038 mPeer->mStorageControllers.attach(newList);
9039 }
9040 else
9041 {
9042 /* we have no peer (our parent is the newly created machine);
9043 * just commit changes to devices */
9044 commitStorageControllers = true;
9045 }
9046 }
9047 else
9048 {
9049 /* the list of controllers itself is not changed,
9050 * just commit changes to controllers themselves */
9051 commitStorageControllers = true;
9052 }
9053
9054 if (commitStorageControllers)
9055 {
9056 StorageControllerList::const_iterator it = mStorageControllers->begin();
9057 while (it != mStorageControllers->end())
9058 {
9059 (*it)->commit();
9060 ++it;
9061 }
9062 }
9063
9064 if (getClassID() == clsidSessionMachine)
9065 {
9066 /* attach new data to the primary machine and reshare it */
9067 mPeer->mUserData.attach(mUserData);
9068 mPeer->mHWData.attach(mHWData);
9069 /* mMediaData is reshared by fixupMedia */
9070 // mPeer->mMediaData.attach(mMediaData);
9071 Assert(mPeer->mMediaData.data() == mMediaData.data());
9072 }
9073}
9074
9075/**
9076 * Copies all the hardware data from the given machine.
9077 *
9078 * Currently, only called when the VM is being restored from a snapshot. In
9079 * particular, this implies that the VM is not running during this method's
9080 * call.
9081 *
9082 * @note This method must be called from under this object's lock.
9083 *
9084 * @note This method doesn't call #commit(), so all data remains backed up and
9085 * unsaved.
9086 */
9087void Machine::copyFrom(Machine *aThat)
9088{
9089 AssertReturnVoid(getClassID() == clsidMachine || getClassID() == clsidSessionMachine);
9090 AssertReturnVoid(aThat->getClassID() == clsidSnapshotMachine);
9091
9092 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
9093
9094 mHWData.assignCopy(aThat->mHWData);
9095
9096 // create copies of all shared folders (mHWData after attiching a copy
9097 // contains just references to original objects)
9098 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
9099 it != mHWData->mSharedFolders.end();
9100 ++it)
9101 {
9102 ComObjPtr<SharedFolder> folder;
9103 folder.createObject();
9104 HRESULT rc = folder->initCopy(getMachine(), *it);
9105 AssertComRC(rc);
9106 *it = folder;
9107 }
9108
9109 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
9110#ifdef VBOX_WITH_VRDP
9111 mVRDPServer->copyFrom(aThat->mVRDPServer);
9112#endif
9113 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
9114 mUSBController->copyFrom(aThat->mUSBController);
9115
9116 /* create private copies of all controllers */
9117 mStorageControllers.backup();
9118 mStorageControllers->clear();
9119 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
9120 it != aThat->mStorageControllers->end();
9121 ++it)
9122 {
9123 ComObjPtr<StorageController> ctrl;
9124 ctrl.createObject();
9125 ctrl->initCopy(this, *it);
9126 mStorageControllers->push_back(ctrl);
9127 }
9128
9129 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
9130 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
9131 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
9132 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
9133 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
9134 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
9135}
9136
9137#ifdef VBOX_WITH_RESOURCE_USAGE_API
9138
9139void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
9140{
9141 pm::CollectorHAL *hal = aCollector->getHAL();
9142 /* Create sub metrics */
9143 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
9144 "Percentage of processor time spent in user mode by the VM process.");
9145 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
9146 "Percentage of processor time spent in kernel mode by the VM process.");
9147 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
9148 "Size of resident portion of VM process in memory.");
9149 /* Create and register base metrics */
9150 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
9151 cpuLoadUser, cpuLoadKernel);
9152 aCollector->registerBaseMetric(cpuLoad);
9153 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
9154 ramUsageUsed);
9155 aCollector->registerBaseMetric(ramUsage);
9156
9157 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
9158 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
9159 new pm::AggregateAvg()));
9160 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
9161 new pm::AggregateMin()));
9162 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
9163 new pm::AggregateMax()));
9164 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
9165 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
9166 new pm::AggregateAvg()));
9167 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
9168 new pm::AggregateMin()));
9169 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
9170 new pm::AggregateMax()));
9171
9172 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
9173 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
9174 new pm::AggregateAvg()));
9175 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
9176 new pm::AggregateMin()));
9177 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
9178 new pm::AggregateMax()));
9179
9180
9181 /* Guest metrics */
9182 mGuestHAL = new pm::CollectorGuestHAL(this, hal);
9183
9184 /* Create sub metrics */
9185 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
9186 "Percentage of processor time spent in user mode as seen by the guest.");
9187 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
9188 "Percentage of processor time spent in kernel mode as seen by the guest.");
9189 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
9190 "Percentage of processor time spent idling as seen by the guest.");
9191
9192 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
9193 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
9194 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
9195 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
9196 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
9197
9198 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
9199
9200 /* Create and register base metrics */
9201 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mGuestHAL, aMachine, guestLoadUser, guestLoadKernel, guestLoadIdle);
9202 aCollector->registerBaseMetric(guestCpuLoad);
9203
9204 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mGuestHAL, aMachine, guestMemTotal, guestMemFree, guestMemBalloon,
9205 guestMemCache, guestPagedTotal);
9206 aCollector->registerBaseMetric(guestCpuMem);
9207
9208 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
9209 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
9210 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
9211 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
9212
9213 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
9214 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
9215 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
9216 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
9217
9218 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
9219 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
9220 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
9221 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
9222
9223 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
9224 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
9225 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
9226 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
9227
9228 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
9229 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
9230 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
9231 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
9232
9233 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
9234 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
9235 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
9236 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
9237
9238 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
9239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
9240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
9241 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
9242
9243 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
9244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
9245 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
9246 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
9247}
9248
9249void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
9250{
9251 aCollector->unregisterMetricsFor(aMachine);
9252 aCollector->unregisterBaseMetricsFor(aMachine);
9253
9254 if (mGuestHAL)
9255 delete mGuestHAL;
9256}
9257
9258#endif /* VBOX_WITH_RESOURCE_USAGE_API */
9259
9260
9261////////////////////////////////////////////////////////////////////////////////
9262
9263DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
9264
9265HRESULT SessionMachine::FinalConstruct()
9266{
9267 LogFlowThisFunc(("\n"));
9268
9269#if defined(RT_OS_WINDOWS)
9270 mIPCSem = NULL;
9271#elif defined(RT_OS_OS2)
9272 mIPCSem = NULLHANDLE;
9273#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9274 mIPCSem = -1;
9275#else
9276# error "Port me!"
9277#endif
9278
9279 return S_OK;
9280}
9281
9282void SessionMachine::FinalRelease()
9283{
9284 LogFlowThisFunc(("\n"));
9285
9286 uninit(Uninit::Unexpected);
9287}
9288
9289/**
9290 * @note Must be called only by Machine::openSession() from its own write lock.
9291 */
9292HRESULT SessionMachine::init(Machine *aMachine)
9293{
9294 LogFlowThisFuncEnter();
9295 LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
9296
9297 AssertReturn(aMachine, E_INVALIDARG);
9298
9299 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
9300
9301 /* Enclose the state transition NotReady->InInit->Ready */
9302 AutoInitSpan autoInitSpan(this);
9303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
9304
9305 /* create the interprocess semaphore */
9306#if defined(RT_OS_WINDOWS)
9307 mIPCSemName = aMachine->mData->m_strConfigFileFull;
9308 for (size_t i = 0; i < mIPCSemName.length(); i++)
9309 if (mIPCSemName[i] == '\\')
9310 mIPCSemName[i] = '/';
9311 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName);
9312 ComAssertMsgRet(mIPCSem,
9313 ("Cannot create IPC mutex '%ls', err=%d",
9314 mIPCSemName.raw(), ::GetLastError()),
9315 E_FAIL);
9316#elif defined(RT_OS_OS2)
9317 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
9318 aMachine->mData->mUuid.raw());
9319 mIPCSemName = ipcSem;
9320 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.raw(), &mIPCSem, 0, FALSE);
9321 ComAssertMsgRet(arc == NO_ERROR,
9322 ("Cannot create IPC mutex '%s', arc=%ld",
9323 ipcSem.raw(), arc),
9324 E_FAIL);
9325#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9326# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9327# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
9328 /** @todo Check that this still works correctly. */
9329 AssertCompileSize(key_t, 8);
9330# else
9331 AssertCompileSize(key_t, 4);
9332# endif
9333 key_t key;
9334 mIPCSem = -1;
9335 mIPCKey = "0";
9336 for (uint32_t i = 0; i < 1 << 24; i++)
9337 {
9338 key = ((uint32_t)'V' << 24) | i;
9339 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
9340 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
9341 {
9342 mIPCSem = sem;
9343 if (sem >= 0)
9344 mIPCKey = BstrFmt("%u", key);
9345 break;
9346 }
9347 }
9348# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9349 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
9350 char *pszSemName = NULL;
9351 RTStrUtf8ToCurrentCP(&pszSemName, semName);
9352 key_t key = ::ftok(pszSemName, 'V');
9353 RTStrFree(pszSemName);
9354
9355 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
9356# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9357
9358 int errnoSave = errno;
9359 if (mIPCSem < 0 && errnoSave == ENOSYS)
9360 {
9361 setError(E_FAIL,
9362 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
9363 "support for SysV IPC. Check the host kernel configuration for "
9364 "CONFIG_SYSVIPC=y"));
9365 return E_FAIL;
9366 }
9367 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
9368 * the IPC semaphores */
9369 if (mIPCSem < 0 && errnoSave == ENOSPC)
9370 {
9371#ifdef RT_OS_LINUX
9372 setError(E_FAIL,
9373 tr("Cannot create IPC semaphore because the system limit for the "
9374 "maximum number of semaphore sets (SEMMNI), or the system wide "
9375 "maximum number of sempahores (SEMMNS) would be exceeded. The "
9376 "current set of SysV IPC semaphores can be determined from "
9377 "the file /proc/sysvipc/sem"));
9378#else
9379 setError(E_FAIL,
9380 tr("Cannot create IPC semaphore because the system-imposed limit "
9381 "on the maximum number of allowed semaphores or semaphore "
9382 "identifiers system-wide would be exceeded"));
9383#endif
9384 return E_FAIL;
9385 }
9386 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
9387 E_FAIL);
9388 /* set the initial value to 1 */
9389 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
9390 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
9391 E_FAIL);
9392#else
9393# error "Port me!"
9394#endif
9395
9396 /* memorize the peer Machine */
9397 unconst(mPeer) = aMachine;
9398 /* share the parent pointer */
9399 unconst(mParent) = aMachine->mParent;
9400
9401 /* take the pointers to data to share */
9402 mData.share(aMachine->mData);
9403 mSSData.share(aMachine->mSSData);
9404
9405 mUserData.share(aMachine->mUserData);
9406 mHWData.share(aMachine->mHWData);
9407 mMediaData.share(aMachine->mMediaData);
9408
9409 mStorageControllers.allocate();
9410 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
9411 it != aMachine->mStorageControllers->end();
9412 ++it)
9413 {
9414 ComObjPtr<StorageController> ctl;
9415 ctl.createObject();
9416 ctl->init(this, *it);
9417 mStorageControllers->push_back(ctl);
9418 }
9419
9420 unconst(mBIOSSettings).createObject();
9421 mBIOSSettings->init(this, aMachine->mBIOSSettings);
9422#ifdef VBOX_WITH_VRDP
9423 /* create another VRDPServer object that will be mutable */
9424 unconst(mVRDPServer).createObject();
9425 mVRDPServer->init(this, aMachine->mVRDPServer);
9426#endif
9427 /* create another audio adapter object that will be mutable */
9428 unconst(mAudioAdapter).createObject();
9429 mAudioAdapter->init(this, aMachine->mAudioAdapter);
9430 /* create a list of serial ports that will be mutable */
9431 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
9432 {
9433 unconst(mSerialPorts[slot]).createObject();
9434 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
9435 }
9436 /* create a list of parallel ports that will be mutable */
9437 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
9438 {
9439 unconst(mParallelPorts[slot]).createObject();
9440 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
9441 }
9442 /* create another USB controller object that will be mutable */
9443 unconst(mUSBController).createObject();
9444 mUSBController->init(this, aMachine->mUSBController);
9445
9446 /* create a list of network adapters that will be mutable */
9447 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)
9448 {
9449 unconst(mNetworkAdapters[slot]).createObject();
9450 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
9451 }
9452
9453 /* default is to delete saved state on Saved -> PoweredOff transition */
9454 mRemoveSavedState = true;
9455
9456 /* Confirm a successful initialization when it's the case */
9457 autoInitSpan.setSucceeded();
9458
9459 LogFlowThisFuncLeave();
9460 return S_OK;
9461}
9462
9463/**
9464 * Uninitializes this session object. If the reason is other than
9465 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
9466 *
9467 * @param aReason uninitialization reason
9468 *
9469 * @note Locks mParent + this object for writing.
9470 */
9471void SessionMachine::uninit(Uninit::Reason aReason)
9472{
9473 LogFlowThisFuncEnter();
9474 LogFlowThisFunc(("reason=%d\n", aReason));
9475
9476 /*
9477 * Strongly reference ourselves to prevent this object deletion after
9478 * mData->mSession.mMachine.setNull() below (which can release the last
9479 * reference and call the destructor). Important: this must be done before
9480 * accessing any members (and before AutoUninitSpan that does it as well).
9481 * This self reference will be released as the very last step on return.
9482 */
9483 ComObjPtr<SessionMachine> selfRef = this;
9484
9485 /* Enclose the state transition Ready->InUninit->NotReady */
9486 AutoUninitSpan autoUninitSpan(this);
9487 if (autoUninitSpan.uninitDone())
9488 {
9489 LogFlowThisFunc(("Already uninitialized\n"));
9490 LogFlowThisFuncLeave();
9491 return;
9492 }
9493
9494 if (autoUninitSpan.initFailed())
9495 {
9496 /* We've been called by init() because it's failed. It's not really
9497 * necessary (nor it's safe) to perform the regular uninit sequense
9498 * below, the following is enough.
9499 */
9500 LogFlowThisFunc(("Initialization failed.\n"));
9501#if defined(RT_OS_WINDOWS)
9502 if (mIPCSem)
9503 ::CloseHandle(mIPCSem);
9504 mIPCSem = NULL;
9505#elif defined(RT_OS_OS2)
9506 if (mIPCSem != NULLHANDLE)
9507 ::DosCloseMutexSem(mIPCSem);
9508 mIPCSem = NULLHANDLE;
9509#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9510 if (mIPCSem >= 0)
9511 ::semctl(mIPCSem, 0, IPC_RMID);
9512 mIPCSem = -1;
9513# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9514 mIPCKey = "0";
9515# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
9516#else
9517# error "Port me!"
9518#endif
9519 uninitDataAndChildObjects();
9520 mData.free();
9521 unconst(mParent) = NULL;
9522 unconst(mPeer) = NULL;
9523 LogFlowThisFuncLeave();
9524 return;
9525 }
9526
9527 MachineState_T lastState;
9528 {
9529 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
9530 lastState = mData->mMachineState;
9531 }
9532 NOREF(lastState);
9533
9534#ifdef VBOX_WITH_USB
9535 // release all captured USB devices, but do this before requesting the locks below
9536 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
9537 {
9538 /* Console::captureUSBDevices() is called in the VM process only after
9539 * setting the machine state to Starting or Restoring.
9540 * Console::detachAllUSBDevices() will be called upon successful
9541 * termination. So, we need to release USB devices only if there was
9542 * an abnormal termination of a running VM.
9543 *
9544 * This is identical to SessionMachine::DetachAllUSBDevices except
9545 * for the aAbnormal argument. */
9546 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
9547 AssertComRC(rc);
9548 NOREF(rc);
9549
9550 USBProxyService *service = mParent->host()->usbProxyService();
9551 if (service)
9552 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
9553 }
9554#endif /* VBOX_WITH_USB */
9555
9556 // we need to lock this object in uninit() because the lock is shared
9557 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
9558 // and others need mParent lock, and USB needs host lock.
9559 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
9560
9561#ifdef VBOX_WITH_RESOURCE_USAGE_API
9562 unregisterMetrics(mParent->performanceCollector(), mPeer);
9563#endif /* VBOX_WITH_RESOURCE_USAGE_API */
9564
9565 if (aReason == Uninit::Abnormal)
9566 {
9567 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
9568 Global::IsOnlineOrTransient(lastState)));
9569
9570 /* reset the state to Aborted */
9571 if (mData->mMachineState != MachineState_Aborted)
9572 setMachineState(MachineState_Aborted);
9573 }
9574
9575 // any machine settings modified?
9576 if (mData->flModifications)
9577 {
9578 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
9579 rollback(false /* aNotify */);
9580 }
9581
9582 Assert(mSnapshotData.mStateFilePath.isEmpty() || !mSnapshotData.mSnapshot);
9583 if (!mSnapshotData.mStateFilePath.isEmpty())
9584 {
9585 LogWarningThisFunc(("canceling failed save state request!\n"));
9586 endSavingState(FALSE /* aSuccess */);
9587 }
9588 else if (!mSnapshotData.mSnapshot.isNull())
9589 {
9590 LogWarningThisFunc(("canceling untaken snapshot!\n"));
9591
9592 /* delete all differencing hard disks created (this will also attach
9593 * their parents back by rolling back mMediaData) */
9594 rollbackMedia();
9595 /* delete the saved state file (it might have been already created) */
9596 if (mSnapshotData.mSnapshot->stateFilePath().length())
9597 RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str());
9598
9599 mSnapshotData.mSnapshot->uninit();
9600 }
9601
9602 if (!mData->mSession.mType.isEmpty())
9603 {
9604 /* mType is not null when this machine's process has been started by
9605 * VirtualBox::OpenRemoteSession(), therefore it is our child. We
9606 * need to queue the PID to reap the process (and avoid zombies on
9607 * Linux). */
9608 Assert(mData->mSession.mPid != NIL_RTPROCESS);
9609 mParent->addProcessToReap(mData->mSession.mPid);
9610 }
9611
9612 mData->mSession.mPid = NIL_RTPROCESS;
9613
9614 if (aReason == Uninit::Unexpected)
9615 {
9616 /* Uninitialization didn't come from #checkForDeath(), so tell the
9617 * client watcher thread to update the set of machines that have open
9618 * sessions. */
9619 mParent->updateClientWatcher();
9620 }
9621
9622 /* uninitialize all remote controls */
9623 if (mData->mSession.mRemoteControls.size())
9624 {
9625 LogFlowThisFunc(("Closing remote sessions (%d):\n",
9626 mData->mSession.mRemoteControls.size()));
9627
9628 Data::Session::RemoteControlList::iterator it =
9629 mData->mSession.mRemoteControls.begin();
9630 while (it != mData->mSession.mRemoteControls.end())
9631 {
9632 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
9633 HRESULT rc = (*it)->Uninitialize();
9634 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
9635 if (FAILED(rc))
9636 LogWarningThisFunc(("Forgot to close the remote session?\n"));
9637 ++it;
9638 }
9639 mData->mSession.mRemoteControls.clear();
9640 }
9641
9642 /*
9643 * An expected uninitialization can come only from #checkForDeath().
9644 * Otherwise it means that something's got really wrong (for examlple,
9645 * the Session implementation has released the VirtualBox reference
9646 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
9647 * etc). However, it's also possible, that the client releases the IPC
9648 * semaphore correctly (i.e. before it releases the VirtualBox reference),
9649 * but the VirtualBox release event comes first to the server process.
9650 * This case is practically possible, so we should not assert on an
9651 * unexpected uninit, just log a warning.
9652 */
9653
9654 if ((aReason == Uninit::Unexpected))
9655 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
9656
9657 if (aReason != Uninit::Normal)
9658 {
9659 mData->mSession.mDirectControl.setNull();
9660 }
9661 else
9662 {
9663 /* this must be null here (see #OnSessionEnd()) */
9664 Assert(mData->mSession.mDirectControl.isNull());
9665 Assert(mData->mSession.mState == SessionState_Closing);
9666 Assert(!mData->mSession.mProgress.isNull());
9667 }
9668 if (mData->mSession.mProgress)
9669 {
9670 if (aReason == Uninit::Normal)
9671 mData->mSession.mProgress->notifyComplete(S_OK);
9672 else
9673 mData->mSession.mProgress->notifyComplete(E_FAIL,
9674 COM_IIDOF(ISession),
9675 getComponentName(),
9676 tr("The VM session was aborted"));
9677 mData->mSession.mProgress.setNull();
9678 }
9679
9680 /* remove the association between the peer machine and this session machine */
9681 Assert(mData->mSession.mMachine == this ||
9682 aReason == Uninit::Unexpected);
9683
9684 /* reset the rest of session data */
9685 mData->mSession.mMachine.setNull();
9686 mData->mSession.mState = SessionState_Closed;
9687 mData->mSession.mType.setNull();
9688
9689 /* close the interprocess semaphore before leaving the exclusive lock */
9690#if defined(RT_OS_WINDOWS)
9691 if (mIPCSem)
9692 ::CloseHandle(mIPCSem);
9693 mIPCSem = NULL;
9694#elif defined(RT_OS_OS2)
9695 if (mIPCSem != NULLHANDLE)
9696 ::DosCloseMutexSem(mIPCSem);
9697 mIPCSem = NULLHANDLE;
9698#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9699 if (mIPCSem >= 0)
9700 ::semctl(mIPCSem, 0, IPC_RMID);
9701 mIPCSem = -1;
9702# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9703 mIPCKey = "0";
9704# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
9705#else
9706# error "Port me!"
9707#endif
9708
9709 /* fire an event */
9710 mParent->onSessionStateChange(mData->mUuid, SessionState_Closed);
9711
9712 uninitDataAndChildObjects();
9713
9714 /* free the essential data structure last */
9715 mData.free();
9716
9717 /* leave the exclusive lock before setting the below two to NULL */
9718 multilock.leave();
9719
9720 unconst(mParent) = NULL;
9721 unconst(mPeer) = NULL;
9722
9723 LogFlowThisFuncLeave();
9724}
9725
9726// util::Lockable interface
9727////////////////////////////////////////////////////////////////////////////////
9728
9729/**
9730 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
9731 * with the primary Machine instance (mPeer).
9732 */
9733RWLockHandle *SessionMachine::lockHandle() const
9734{
9735 AssertReturn(mPeer != NULL, NULL);
9736 return mPeer->lockHandle();
9737}
9738
9739// IInternalMachineControl methods
9740////////////////////////////////////////////////////////////////////////////////
9741
9742/**
9743 * @note Locks this object for writing.
9744 */
9745STDMETHODIMP SessionMachine::SetRemoveSavedState(BOOL aRemove)
9746{
9747 AutoCaller autoCaller(this);
9748 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9749
9750 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9751
9752 mRemoveSavedState = aRemove;
9753
9754 return S_OK;
9755}
9756
9757/**
9758 * @note Locks the same as #setMachineState() does.
9759 */
9760STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
9761{
9762 return setMachineState(aMachineState);
9763}
9764
9765/**
9766 * @note Locks this object for reading.
9767 */
9768STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
9769{
9770 AutoCaller autoCaller(this);
9771 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9772
9773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9774
9775#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
9776 mIPCSemName.cloneTo(aId);
9777 return S_OK;
9778#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
9779# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
9780 mIPCKey.cloneTo(aId);
9781# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9782 mData->m_strConfigFileFull.cloneTo(aId);
9783# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
9784 return S_OK;
9785#else
9786# error "Port me!"
9787#endif
9788}
9789
9790/**
9791 * @note Locks this object for writing.
9792 */
9793STDMETHODIMP SessionMachine::SetPowerUpInfo(IVirtualBoxErrorInfo *aError)
9794{
9795 AutoCaller autoCaller(this);
9796 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9797
9798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9799
9800 if ( mData->mSession.mState == SessionState_Open
9801 && mData->mSession.mProgress)
9802 {
9803 /* Finalize the progress, since the remote session has completed
9804 * power on (successful or not). */
9805 if (aError)
9806 {
9807 /* Transfer error information immediately, as the
9808 * IVirtualBoxErrorInfo object is most likely transient. */
9809 HRESULT rc;
9810 LONG rRc = S_OK;
9811 rc = aError->COMGETTER(ResultCode)(&rRc);
9812 AssertComRCReturnRC(rc);
9813 Bstr rIID;
9814 rc = aError->COMGETTER(InterfaceID)(rIID.asOutParam());
9815 AssertComRCReturnRC(rc);
9816 Bstr rComponent;
9817 rc = aError->COMGETTER(Component)(rComponent.asOutParam());
9818 AssertComRCReturnRC(rc);
9819 Bstr rText;
9820 rc = aError->COMGETTER(Text)(rText.asOutParam());
9821 AssertComRCReturnRC(rc);
9822 mData->mSession.mProgress->notifyComplete(rRc, Guid(rIID), rComponent, Utf8Str(rText).raw());
9823 }
9824 else
9825 mData->mSession.mProgress->notifyComplete(S_OK);
9826 mData->mSession.mProgress.setNull();
9827
9828 return S_OK;
9829 }
9830 else
9831 return VBOX_E_INVALID_OBJECT_STATE;
9832}
9833
9834/**
9835 * Goes through the USB filters of the given machine to see if the given
9836 * device matches any filter or not.
9837 *
9838 * @note Locks the same as USBController::hasMatchingFilter() does.
9839 */
9840STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
9841 BOOL *aMatched,
9842 ULONG *aMaskedIfs)
9843{
9844 LogFlowThisFunc(("\n"));
9845
9846 CheckComArgNotNull(aUSBDevice);
9847 CheckComArgOutPointerValid(aMatched);
9848
9849 AutoCaller autoCaller(this);
9850 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9851
9852#ifdef VBOX_WITH_USB
9853 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
9854#else
9855 NOREF(aUSBDevice);
9856 NOREF(aMaskedIfs);
9857 *aMatched = FALSE;
9858#endif
9859
9860 return S_OK;
9861}
9862
9863/**
9864 * @note Locks the same as Host::captureUSBDevice() does.
9865 */
9866STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
9867{
9868 LogFlowThisFunc(("\n"));
9869
9870 AutoCaller autoCaller(this);
9871 AssertComRCReturnRC(autoCaller.rc());
9872
9873#ifdef VBOX_WITH_USB
9874 /* if captureDeviceForVM() fails, it must have set extended error info */
9875 MultiResult rc = mParent->host()->checkUSBProxyService();
9876 if (FAILED(rc)) return rc;
9877
9878 USBProxyService *service = mParent->host()->usbProxyService();
9879 AssertReturn(service, E_FAIL);
9880 return service->captureDeviceForVM(this, Guid(aId));
9881#else
9882 NOREF(aId);
9883 return E_NOTIMPL;
9884#endif
9885}
9886
9887/**
9888 * @note Locks the same as Host::detachUSBDevice() does.
9889 */
9890STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
9891{
9892 LogFlowThisFunc(("\n"));
9893
9894 AutoCaller autoCaller(this);
9895 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9896
9897#ifdef VBOX_WITH_USB
9898 USBProxyService *service = mParent->host()->usbProxyService();
9899 AssertReturn(service, E_FAIL);
9900 return service->detachDeviceFromVM(this, Guid(aId), !!aDone);
9901#else
9902 NOREF(aId);
9903 NOREF(aDone);
9904 return E_NOTIMPL;
9905#endif
9906}
9907
9908/**
9909 * Inserts all machine filters to the USB proxy service and then calls
9910 * Host::autoCaptureUSBDevices().
9911 *
9912 * Called by Console from the VM process upon VM startup.
9913 *
9914 * @note Locks what called methods lock.
9915 */
9916STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
9917{
9918 LogFlowThisFunc(("\n"));
9919
9920 AutoCaller autoCaller(this);
9921 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9922
9923#ifdef VBOX_WITH_USB
9924 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
9925 AssertComRC(rc);
9926 NOREF(rc);
9927
9928 USBProxyService *service = mParent->host()->usbProxyService();
9929 AssertReturn(service, E_FAIL);
9930 return service->autoCaptureDevicesForVM(this);
9931#else
9932 return S_OK;
9933#endif
9934}
9935
9936/**
9937 * Removes all machine filters from the USB proxy service and then calls
9938 * Host::detachAllUSBDevices().
9939 *
9940 * Called by Console from the VM process upon normal VM termination or by
9941 * SessionMachine::uninit() upon abnormal VM termination (from under the
9942 * Machine/SessionMachine lock).
9943 *
9944 * @note Locks what called methods lock.
9945 */
9946STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
9947{
9948 LogFlowThisFunc(("\n"));
9949
9950 AutoCaller autoCaller(this);
9951 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9952
9953#ifdef VBOX_WITH_USB
9954 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
9955 AssertComRC(rc);
9956 NOREF(rc);
9957
9958 USBProxyService *service = mParent->host()->usbProxyService();
9959 AssertReturn(service, E_FAIL);
9960 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
9961#else
9962 NOREF(aDone);
9963 return S_OK;
9964#endif
9965}
9966
9967/**
9968 * @note Locks this object for writing.
9969 */
9970STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
9971 IProgress **aProgress)
9972{
9973 LogFlowThisFuncEnter();
9974
9975 AssertReturn(aSession, E_INVALIDARG);
9976 AssertReturn(aProgress, E_INVALIDARG);
9977
9978 AutoCaller autoCaller(this);
9979
9980 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
9981 /*
9982 * We don't assert below because it might happen that a non-direct session
9983 * informs us it is closed right after we've been uninitialized -- it's ok.
9984 */
9985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9986
9987 /* get IInternalSessionControl interface */
9988 ComPtr<IInternalSessionControl> control(aSession);
9989
9990 ComAssertRet(!control.isNull(), E_INVALIDARG);
9991
9992 /* Creating a Progress object requires the VirtualBox lock, and
9993 * thus locking it here is required by the lock order rules. */
9994 AutoMultiWriteLock2 alock(mParent->lockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
9995
9996 if (control.equalsTo(mData->mSession.mDirectControl))
9997 {
9998 ComAssertRet(aProgress, E_POINTER);
9999
10000 /* The direct session is being normally closed by the client process
10001 * ----------------------------------------------------------------- */
10002
10003 /* go to the closing state (essential for all open*Session() calls and
10004 * for #checkForDeath()) */
10005 Assert(mData->mSession.mState == SessionState_Open);
10006 mData->mSession.mState = SessionState_Closing;
10007
10008 /* set direct control to NULL to release the remote instance */
10009 mData->mSession.mDirectControl.setNull();
10010 LogFlowThisFunc(("Direct control is set to NULL\n"));
10011
10012 if (mData->mSession.mProgress)
10013 {
10014 /* finalize the progress, someone might wait if a frontend
10015 * closes the session before powering on the VM. */
10016 mData->mSession.mProgress->notifyComplete(E_FAIL,
10017 COM_IIDOF(ISession),
10018 getComponentName(),
10019 tr("The VM session was closed before any attempt to power it on"));
10020 mData->mSession.mProgress.setNull();
10021 }
10022
10023 /* Create the progress object the client will use to wait until
10024 * #checkForDeath() is called to uninitialize this session object after
10025 * it releases the IPC semaphore. */
10026 Assert(mData->mSession.mProgress.isNull());
10027 ComObjPtr<Progress> progress;
10028 progress.createObject();
10029 ComPtr<IUnknown> pPeer(mPeer);
10030 progress->init(mParent, pPeer,
10031 Bstr(tr("Closing session")), FALSE /* aCancelable */);
10032 progress.queryInterfaceTo(aProgress);
10033 mData->mSession.mProgress = progress;
10034 }
10035 else
10036 {
10037 /* the remote session is being normally closed */
10038 Data::Session::RemoteControlList::iterator it =
10039 mData->mSession.mRemoteControls.begin();
10040 while (it != mData->mSession.mRemoteControls.end())
10041 {
10042 if (control.equalsTo(*it))
10043 break;
10044 ++it;
10045 }
10046 BOOL found = it != mData->mSession.mRemoteControls.end();
10047 ComAssertMsgRet(found, ("The session is not found in the session list!"),
10048 E_INVALIDARG);
10049 mData->mSession.mRemoteControls.remove(*it);
10050 }
10051
10052 LogFlowThisFuncLeave();
10053 return S_OK;
10054}
10055
10056/**
10057 * @note Locks this object for writing.
10058 */
10059STDMETHODIMP SessionMachine::BeginSavingState(IProgress *aProgress, BSTR *aStateFilePath)
10060{
10061 LogFlowThisFuncEnter();
10062
10063 AssertReturn(aProgress, E_INVALIDARG);
10064 AssertReturn(aStateFilePath, E_POINTER);
10065
10066 AutoCaller autoCaller(this);
10067 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10068
10069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10070
10071 AssertReturn( mData->mMachineState == MachineState_Paused
10072 && mSnapshotData.mLastState == MachineState_Null
10073 && mSnapshotData.mProgressId.isEmpty()
10074 && mSnapshotData.mStateFilePath.isEmpty(),
10075 E_FAIL);
10076
10077 /* memorize the progress ID and add it to the global collection */
10078 Bstr progressId;
10079 HRESULT rc = aProgress->COMGETTER(Id)(progressId.asOutParam());
10080 AssertComRCReturn(rc, rc);
10081 rc = mParent->addProgress(aProgress);
10082 AssertComRCReturn(rc, rc);
10083
10084 Bstr stateFilePath;
10085 /* stateFilePath is null when the machine is not running */
10086 if (mData->mMachineState == MachineState_Paused)
10087 {
10088 stateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
10089 mUserData->mSnapshotFolderFull.raw(),
10090 RTPATH_DELIMITER, mData->mUuid.raw());
10091 }
10092
10093 /* fill in the snapshot data */
10094 mSnapshotData.mLastState = mData->mMachineState;
10095 mSnapshotData.mProgressId = Guid(progressId);
10096 mSnapshotData.mStateFilePath = stateFilePath;
10097
10098 /* set the state to Saving (this is expected by Console::SaveState()) */
10099 setMachineState(MachineState_Saving);
10100
10101 stateFilePath.cloneTo(aStateFilePath);
10102
10103 return S_OK;
10104}
10105
10106/**
10107 * @note Locks mParent + this object for writing.
10108 */
10109STDMETHODIMP SessionMachine::EndSavingState(BOOL aSuccess)
10110{
10111 LogFlowThisFunc(("\n"));
10112
10113 AutoCaller autoCaller(this);
10114 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10115
10116 /* endSavingState() need mParent lock */
10117 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
10118
10119 AssertReturn( mData->mMachineState == MachineState_Saving
10120 && mSnapshotData.mLastState != MachineState_Null
10121 && !mSnapshotData.mProgressId.isEmpty()
10122 && !mSnapshotData.mStateFilePath.isEmpty(),
10123 E_FAIL);
10124
10125 /*
10126 * on success, set the state to Saved;
10127 * on failure, set the state to the state we had when BeginSavingState() was
10128 * called (this is expected by Console::SaveState() and
10129 * Console::saveStateThread())
10130 */
10131 if (aSuccess)
10132 setMachineState(MachineState_Saved);
10133 else
10134 setMachineState(mSnapshotData.mLastState);
10135
10136 return endSavingState(aSuccess);
10137}
10138
10139/**
10140 * @note Locks this object for writing.
10141 */
10142STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
10143{
10144 LogFlowThisFunc(("\n"));
10145
10146 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
10147
10148 AutoCaller autoCaller(this);
10149 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10150
10151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10152
10153 AssertReturn( mData->mMachineState == MachineState_PoweredOff
10154 || mData->mMachineState == MachineState_Teleported
10155 || mData->mMachineState == MachineState_Aborted
10156 , E_FAIL); /** @todo setError. */
10157
10158 Utf8Str stateFilePathFull = aSavedStateFile;
10159 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
10160 if (RT_FAILURE(vrc))
10161 return setError(VBOX_E_FILE_ERROR,
10162 tr("Invalid saved state file path '%ls' (%Rrc)"),
10163 aSavedStateFile,
10164 vrc);
10165
10166 mSSData->mStateFilePath = stateFilePathFull;
10167
10168 /* The below setMachineState() will detect the state transition and will
10169 * update the settings file */
10170
10171 return setMachineState(MachineState_Saved);
10172}
10173
10174STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
10175 ComSafeArrayOut(BSTR, aValues),
10176 ComSafeArrayOut(ULONG64, aTimestamps),
10177 ComSafeArrayOut(BSTR, aFlags))
10178{
10179 LogFlowThisFunc(("\n"));
10180
10181#ifdef VBOX_WITH_GUEST_PROPS
10182 using namespace guestProp;
10183
10184 AutoCaller autoCaller(this);
10185 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10186
10187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10188
10189 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);
10190 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);
10191 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);
10192 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);
10193
10194 size_t cEntries = mHWData->mGuestProperties.size();
10195 com::SafeArray<BSTR> names(cEntries);
10196 com::SafeArray<BSTR> values(cEntries);
10197 com::SafeArray<ULONG64> timestamps(cEntries);
10198 com::SafeArray<BSTR> flags(cEntries);
10199 unsigned i = 0;
10200 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
10201 it != mHWData->mGuestProperties.end();
10202 ++it)
10203 {
10204 char szFlags[MAX_FLAGS_LEN + 1];
10205 it->strName.cloneTo(&names[i]);
10206 it->strValue.cloneTo(&values[i]);
10207 timestamps[i] = it->mTimestamp;
10208 /* If it is NULL, keep it NULL. */
10209 if (it->mFlags)
10210 {
10211 writeFlags(it->mFlags, szFlags);
10212 Bstr(szFlags).cloneTo(&flags[i]);
10213 }
10214 else
10215 flags[i] = NULL;
10216 ++i;
10217 }
10218 names.detachTo(ComSafeArrayOutArg(aNames));
10219 values.detachTo(ComSafeArrayOutArg(aValues));
10220 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
10221 flags.detachTo(ComSafeArrayOutArg(aFlags));
10222 return S_OK;
10223#else
10224 ReturnComNotImplemented();
10225#endif
10226}
10227
10228STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
10229 IN_BSTR aValue,
10230 ULONG64 aTimestamp,
10231 IN_BSTR aFlags)
10232{
10233 LogFlowThisFunc(("\n"));
10234
10235#ifdef VBOX_WITH_GUEST_PROPS
10236 using namespace guestProp;
10237
10238 CheckComArgStrNotEmptyOrNull(aName);
10239 if (aValue != NULL && (!VALID_PTR(aValue) || !VALID_PTR(aFlags)))
10240 return E_POINTER; /* aValue can be NULL to indicate deletion */
10241
10242 try
10243 {
10244 /*
10245 * Convert input up front.
10246 */
10247 Utf8Str utf8Name(aName);
10248 uint32_t fFlags = NILFLAG;
10249 if (aFlags)
10250 {
10251 Utf8Str utf8Flags(aFlags);
10252 int vrc = validateFlags(utf8Flags.raw(), &fFlags);
10253 AssertRCReturn(vrc, E_INVALIDARG);
10254 }
10255
10256 /*
10257 * Now grab the object lock, validate the state and do the update.
10258 */
10259 AutoCaller autoCaller(this);
10260 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10261
10262 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10263
10264 switch (mData->mMachineState)
10265 {
10266 case MachineState_Paused:
10267 case MachineState_Running:
10268 case MachineState_Teleporting:
10269 case MachineState_TeleportingPausedVM:
10270 case MachineState_LiveSnapshotting:
10271 case MachineState_DeletingSnapshotOnline:
10272 case MachineState_DeletingSnapshotPaused:
10273 case MachineState_Saving:
10274 break;
10275
10276 default:
10277 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
10278 VBOX_E_INVALID_VM_STATE);
10279 }
10280
10281 setModified(IsModified_MachineData);
10282 mHWData.backup();
10283
10284 /** @todo r=bird: The careful memory handling doesn't work out here because
10285 * the catch block won't undo any damange we've done. So, if push_back throws
10286 * bad_alloc then you've lost the value.
10287 *
10288 * Another thing. Doing a linear search here isn't extremely efficient, esp.
10289 * since values that changes actually bubbles to the end of the list. Using
10290 * something that has an efficient lookup and can tollerate a bit of updates
10291 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
10292 * combination of RTStrCache (for sharing names and getting uniqueness into
10293 * the bargain) and hash/tree is another. */
10294 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
10295 iter != mHWData->mGuestProperties.end();
10296 ++iter)
10297 if (utf8Name == iter->strName)
10298 {
10299 mHWData->mGuestProperties.erase(iter);
10300 mData->mGuestPropertiesModified = TRUE;
10301 break;
10302 }
10303 if (aValue != NULL)
10304 {
10305 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
10306 mHWData->mGuestProperties.push_back(property);
10307 mData->mGuestPropertiesModified = TRUE;
10308 }
10309
10310 /*
10311 * Send a callback notification if appropriate
10312 */
10313 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
10314 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.raw(),
10315 RTSTR_MAX,
10316 utf8Name.raw(),
10317 RTSTR_MAX, NULL)
10318 )
10319 {
10320 alock.leave();
10321
10322 mParent->onGuestPropertyChange(mData->mUuid,
10323 aName,
10324 aValue,
10325 aFlags);
10326 }
10327 }
10328 catch (...)
10329 {
10330 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);
10331 }
10332 return S_OK;
10333#else
10334 ReturnComNotImplemented();
10335#endif
10336}
10337
10338// public methods only for internal purposes
10339/////////////////////////////////////////////////////////////////////////////
10340
10341/**
10342 * Called from the client watcher thread to check for expected or unexpected
10343 * death of the client process that has a direct session to this machine.
10344 *
10345 * On Win32 and on OS/2, this method is called only when we've got the
10346 * mutex (i.e. the client has either died or terminated normally) so it always
10347 * returns @c true (the client is terminated, the session machine is
10348 * uninitialized).
10349 *
10350 * On other platforms, the method returns @c true if the client process has
10351 * terminated normally or abnormally and the session machine was uninitialized,
10352 * and @c false if the client process is still alive.
10353 *
10354 * @note Locks this object for writing.
10355 */
10356bool SessionMachine::checkForDeath()
10357{
10358 Uninit::Reason reason;
10359 bool terminated = false;
10360
10361 /* Enclose autoCaller with a block because calling uninit() from under it
10362 * will deadlock. */
10363 {
10364 AutoCaller autoCaller(this);
10365 if (!autoCaller.isOk())
10366 {
10367 /* return true if not ready, to cause the client watcher to exclude
10368 * the corresponding session from watching */
10369 LogFlowThisFunc(("Already uninitialized!\n"));
10370 return true;
10371 }
10372
10373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10374
10375 /* Determine the reason of death: if the session state is Closing here,
10376 * everything is fine. Otherwise it means that the client did not call
10377 * OnSessionEnd() before it released the IPC semaphore. This may happen
10378 * either because the client process has abnormally terminated, or
10379 * because it simply forgot to call ISession::Close() before exiting. We
10380 * threat the latter also as an abnormal termination (see
10381 * Session::uninit() for details). */
10382 reason = mData->mSession.mState == SessionState_Closing ?
10383 Uninit::Normal :
10384 Uninit::Abnormal;
10385
10386#if defined(RT_OS_WINDOWS)
10387
10388 AssertMsg(mIPCSem, ("semaphore must be created"));
10389
10390 /* release the IPC mutex */
10391 ::ReleaseMutex(mIPCSem);
10392
10393 terminated = true;
10394
10395#elif defined(RT_OS_OS2)
10396
10397 AssertMsg(mIPCSem, ("semaphore must be created"));
10398
10399 /* release the IPC mutex */
10400 ::DosReleaseMutexSem(mIPCSem);
10401
10402 terminated = true;
10403
10404#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
10405
10406 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
10407
10408 int val = ::semctl(mIPCSem, 0, GETVAL);
10409 if (val > 0)
10410 {
10411 /* the semaphore is signaled, meaning the session is terminated */
10412 terminated = true;
10413 }
10414
10415#else
10416# error "Port me!"
10417#endif
10418
10419 } /* AutoCaller block */
10420
10421 if (terminated)
10422 uninit(reason);
10423
10424 return terminated;
10425}
10426
10427/**
10428 * @note Locks this object for reading.
10429 */
10430HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
10431{
10432 LogFlowThisFunc(("\n"));
10433
10434 AutoCaller autoCaller(this);
10435 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10436
10437 ComPtr<IInternalSessionControl> directControl;
10438 {
10439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10440 directControl = mData->mSession.mDirectControl;
10441 }
10442
10443 /* ignore notifications sent after #OnSessionEnd() is called */
10444 if (!directControl)
10445 return S_OK;
10446
10447 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
10448}
10449
10450/**
10451 * @note Locks this object for reading.
10452 */
10453HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
10454{
10455 LogFlowThisFunc(("\n"));
10456
10457 AutoCaller autoCaller(this);
10458 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10459
10460 ComPtr<IInternalSessionControl> directControl;
10461 {
10462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10463 directControl = mData->mSession.mDirectControl;
10464 }
10465
10466 /* ignore notifications sent after #OnSessionEnd() is called */
10467 if (!directControl)
10468 return S_OK;
10469
10470 return directControl->OnSerialPortChange(serialPort);
10471}
10472
10473/**
10474 * @note Locks this object for reading.
10475 */
10476HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
10477{
10478 LogFlowThisFunc(("\n"));
10479
10480 AutoCaller autoCaller(this);
10481 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10482
10483 ComPtr<IInternalSessionControl> directControl;
10484 {
10485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10486 directControl = mData->mSession.mDirectControl;
10487 }
10488
10489 /* ignore notifications sent after #OnSessionEnd() is called */
10490 if (!directControl)
10491 return S_OK;
10492
10493 return directControl->OnParallelPortChange(parallelPort);
10494}
10495
10496/**
10497 * @note Locks this object for reading.
10498 */
10499HRESULT SessionMachine::onStorageControllerChange()
10500{
10501 LogFlowThisFunc(("\n"));
10502
10503 AutoCaller autoCaller(this);
10504 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10505
10506 ComPtr<IInternalSessionControl> directControl;
10507 {
10508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10509 directControl = mData->mSession.mDirectControl;
10510 }
10511
10512 /* ignore notifications sent after #OnSessionEnd() is called */
10513 if (!directControl)
10514 return S_OK;
10515
10516 return directControl->OnStorageControllerChange();
10517}
10518
10519/**
10520 * @note Locks this object for reading.
10521 */
10522HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
10523{
10524 LogFlowThisFunc(("\n"));
10525
10526 AutoCaller autoCaller(this);
10527 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10528
10529 ComPtr<IInternalSessionControl> directControl;
10530 {
10531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10532 directControl = mData->mSession.mDirectControl;
10533 }
10534
10535 /* ignore notifications sent after #OnSessionEnd() is called */
10536 if (!directControl)
10537 return S_OK;
10538
10539 return directControl->OnMediumChange(aAttachment, aForce);
10540}
10541
10542/**
10543 * @note Locks this object for reading.
10544 */
10545HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
10546{
10547 LogFlowThisFunc(("\n"));
10548
10549 AutoCaller autoCaller(this);
10550 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
10551
10552 ComPtr<IInternalSessionControl> directControl;
10553 {
10554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10555 directControl = mData->mSession.mDirectControl;
10556 }
10557
10558 /* ignore notifications sent after #OnSessionEnd() is called */
10559 if (!directControl)
10560 return S_OK;
10561
10562 return directControl->OnCPUChange(aCPU, aRemove);
10563}
10564
10565/**
10566 * @note Locks this object for reading.
10567 */
10568HRESULT SessionMachine::onVRDPServerChange(BOOL aRestart)
10569{
10570 LogFlowThisFunc(("\n"));
10571
10572 AutoCaller autoCaller(this);
10573 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10574
10575 ComPtr<IInternalSessionControl> directControl;
10576 {
10577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10578 directControl = mData->mSession.mDirectControl;
10579 }
10580
10581 /* ignore notifications sent after #OnSessionEnd() is called */
10582 if (!directControl)
10583 return S_OK;
10584
10585 return directControl->OnVRDPServerChange(aRestart);
10586}
10587
10588/**
10589 * @note Locks this object for reading.
10590 */
10591HRESULT SessionMachine::onUSBControllerChange()
10592{
10593 LogFlowThisFunc(("\n"));
10594
10595 AutoCaller autoCaller(this);
10596 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10597
10598 ComPtr<IInternalSessionControl> directControl;
10599 {
10600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10601 directControl = mData->mSession.mDirectControl;
10602 }
10603
10604 /* ignore notifications sent after #OnSessionEnd() is called */
10605 if (!directControl)
10606 return S_OK;
10607
10608 return directControl->OnUSBControllerChange();
10609}
10610
10611/**
10612 * @note Locks this object for reading.
10613 */
10614HRESULT SessionMachine::onSharedFolderChange()
10615{
10616 LogFlowThisFunc(("\n"));
10617
10618 AutoCaller autoCaller(this);
10619 AssertComRCReturnRC(autoCaller.rc());
10620
10621 ComPtr<IInternalSessionControl> directControl;
10622 {
10623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10624 directControl = mData->mSession.mDirectControl;
10625 }
10626
10627 /* ignore notifications sent after #OnSessionEnd() is called */
10628 if (!directControl)
10629 return S_OK;
10630
10631 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
10632}
10633
10634/**
10635 * Returns @c true if this machine's USB controller reports it has a matching
10636 * filter for the given USB device and @c false otherwise.
10637 *
10638 * @note Caller must have requested machine read lock.
10639 */
10640bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
10641{
10642 AutoCaller autoCaller(this);
10643 /* silently return if not ready -- this method may be called after the
10644 * direct machine session has been called */
10645 if (!autoCaller.isOk())
10646 return false;
10647
10648
10649#ifdef VBOX_WITH_USB
10650 switch (mData->mMachineState)
10651 {
10652 case MachineState_Starting:
10653 case MachineState_Restoring:
10654 case MachineState_TeleportingIn:
10655 case MachineState_Paused:
10656 case MachineState_Running:
10657 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
10658 * elsewhere... */
10659 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
10660 default: break;
10661 }
10662#else
10663 NOREF(aDevice);
10664 NOREF(aMaskedIfs);
10665#endif
10666 return false;
10667}
10668
10669/**
10670 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
10671 */
10672HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
10673 IVirtualBoxErrorInfo *aError,
10674 ULONG aMaskedIfs)
10675{
10676 LogFlowThisFunc(("\n"));
10677
10678 AutoCaller autoCaller(this);
10679
10680 /* This notification may happen after the machine object has been
10681 * uninitialized (the session was closed), so don't assert. */
10682 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10683
10684 ComPtr<IInternalSessionControl> directControl;
10685 {
10686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10687 directControl = mData->mSession.mDirectControl;
10688 }
10689
10690 /* fail on notifications sent after #OnSessionEnd() is called, it is
10691 * expected by the caller */
10692 if (!directControl)
10693 return E_FAIL;
10694
10695 /* No locks should be held at this point. */
10696 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
10697 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
10698
10699 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
10700}
10701
10702/**
10703 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
10704 */
10705HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
10706 IVirtualBoxErrorInfo *aError)
10707{
10708 LogFlowThisFunc(("\n"));
10709
10710 AutoCaller autoCaller(this);
10711
10712 /* This notification may happen after the machine object has been
10713 * uninitialized (the session was closed), so don't assert. */
10714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10715
10716 ComPtr<IInternalSessionControl> directControl;
10717 {
10718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10719 directControl = mData->mSession.mDirectControl;
10720 }
10721
10722 /* fail on notifications sent after #OnSessionEnd() is called, it is
10723 * expected by the caller */
10724 if (!directControl)
10725 return E_FAIL;
10726
10727 /* No locks should be held at this point. */
10728 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
10729 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
10730
10731 return directControl->OnUSBDeviceDetach(aId, aError);
10732}
10733
10734// protected methods
10735/////////////////////////////////////////////////////////////////////////////
10736
10737/**
10738 * Helper method to finalize saving the state.
10739 *
10740 * @note Must be called from under this object's lock.
10741 *
10742 * @param aSuccess TRUE if the snapshot has been taken successfully
10743 *
10744 * @note Locks mParent + this objects for writing.
10745 */
10746HRESULT SessionMachine::endSavingState(BOOL aSuccess)
10747{
10748 LogFlowThisFuncEnter();
10749
10750 AutoCaller autoCaller(this);
10751 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10752
10753 /* saveSettings() needs mParent lock */
10754 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
10755
10756 HRESULT rc = S_OK;
10757
10758 if (aSuccess)
10759 {
10760 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;
10761
10762 /* save all VM settings */
10763 rc = saveSettings(NULL);
10764 // no need to check whether VirtualBox.xml needs saving also since
10765 // we can't have a name change pending at this point
10766 }
10767 else
10768 {
10769 /* delete the saved state file (it might have been already created) */
10770 RTFileDelete(mSnapshotData.mStateFilePath.c_str());
10771 }
10772
10773 /* remove the completed progress object */
10774 mParent->removeProgress(mSnapshotData.mProgressId);
10775
10776 /* clear out the temporary saved state data */
10777 mSnapshotData.mLastState = MachineState_Null;
10778 mSnapshotData.mProgressId.clear();
10779 mSnapshotData.mStateFilePath.setNull();
10780
10781 LogFlowThisFuncLeave();
10782 return rc;
10783}
10784
10785/**
10786 * Locks the attached media.
10787 *
10788 * All attached hard disks are locked for writing and DVD/floppy are locked for
10789 * reading. Parents of attached hard disks (if any) are locked for reading.
10790 *
10791 * This method also performs accessibility check of all media it locks: if some
10792 * media is inaccessible, the method will return a failure and a bunch of
10793 * extended error info objects per each inaccessible medium.
10794 *
10795 * Note that this method is atomic: if it returns a success, all media are
10796 * locked as described above; on failure no media is locked at all (all
10797 * succeeded individual locks will be undone).
10798 *
10799 * This method is intended to be called when the machine is in Starting or
10800 * Restoring state and asserts otherwise.
10801 *
10802 * The locks made by this method must be undone by calling #unlockMedia() when
10803 * no more needed.
10804 */
10805HRESULT SessionMachine::lockMedia()
10806{
10807 AutoCaller autoCaller(this);
10808 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10809
10810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10811
10812 AssertReturn( mData->mMachineState == MachineState_Starting
10813 || mData->mMachineState == MachineState_Restoring
10814 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
10815 /* bail out if trying to lock things with already set up locking */
10816 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
10817
10818 MultiResult mrc(S_OK);
10819
10820 /* Collect locking information for all medium objects attached to the VM. */
10821 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10822 it != mMediaData->mAttachments.end();
10823 ++it)
10824 {
10825 MediumAttachment* pAtt = *it;
10826 DeviceType_T devType = pAtt->getType();
10827 Medium *pMedium = pAtt->getMedium();
10828
10829 MediumLockList *pMediumLockList(new MediumLockList());
10830 // There can be attachments without a medium (floppy/dvd), and thus
10831 // it's impossible to create a medium lock list. It still makes sense
10832 // to have the empty medium lock list in the map in case a medium is
10833 // attached later.
10834 if (pMedium != NULL)
10835 {
10836 bool fIsReadOnlyImage = (devType == DeviceType_DVD);
10837 bool fIsVitalImage = (devType == DeviceType_HardDisk);
10838 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
10839 !fIsReadOnlyImage /* fMediumLockWrite */,
10840 NULL,
10841 *pMediumLockList);
10842 if (FAILED(mrc))
10843 {
10844 delete pMediumLockList;
10845 mData->mSession.mLockedMedia.Clear();
10846 break;
10847 }
10848 }
10849
10850 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
10851 if (FAILED(rc))
10852 {
10853 mData->mSession.mLockedMedia.Clear();
10854 mrc = setError(rc,
10855 tr("Collecting locking information for all attached media failed"));
10856 break;
10857 }
10858 }
10859
10860 if (SUCCEEDED(mrc))
10861 {
10862 /* Now lock all media. If this fails, nothing is locked. */
10863 HRESULT rc = mData->mSession.mLockedMedia.Lock();
10864 if (FAILED(rc))
10865 {
10866 mrc = setError(rc,
10867 tr("Locking of attached media failed"));
10868 }
10869 }
10870
10871 return mrc;
10872}
10873
10874/**
10875 * Undoes the locks made by by #lockMedia().
10876 */
10877void SessionMachine::unlockMedia()
10878{
10879 AutoCaller autoCaller(this);
10880 AssertComRCReturnVoid(autoCaller.rc());
10881
10882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10883
10884 /* we may be holding important error info on the current thread;
10885 * preserve it */
10886 ErrorInfoKeeper eik;
10887
10888 HRESULT rc = mData->mSession.mLockedMedia.Clear();
10889 AssertComRC(rc);
10890}
10891
10892/**
10893 * Helper to change the machine state (reimplementation).
10894 *
10895 * @note Locks this object for writing.
10896 */
10897HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
10898{
10899 LogFlowThisFuncEnter();
10900 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
10901
10902 AutoCaller autoCaller(this);
10903 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10904
10905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10906
10907 MachineState_T oldMachineState = mData->mMachineState;
10908
10909 AssertMsgReturn(oldMachineState != aMachineState,
10910 ("oldMachineState=%s, aMachineState=%s\n",
10911 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
10912 E_FAIL);
10913
10914 HRESULT rc = S_OK;
10915
10916 int stsFlags = 0;
10917 bool deleteSavedState = false;
10918
10919 /* detect some state transitions */
10920
10921 if ( ( oldMachineState == MachineState_Saved
10922 && aMachineState == MachineState_Restoring)
10923 || ( ( oldMachineState == MachineState_PoweredOff
10924 || oldMachineState == MachineState_Teleported
10925 || oldMachineState == MachineState_Aborted
10926 )
10927 && ( aMachineState == MachineState_TeleportingIn
10928 || aMachineState == MachineState_Starting
10929 )
10930 )
10931 )
10932 {
10933 /* The EMT thread is about to start */
10934
10935 /* Nothing to do here for now... */
10936
10937 /// @todo NEWMEDIA don't let mDVDDrive and other children
10938 /// change anything when in the Starting/Restoring state
10939 }
10940 else if ( ( oldMachineState == MachineState_Running
10941 || oldMachineState == MachineState_Paused
10942 || oldMachineState == MachineState_Teleporting
10943 || oldMachineState == MachineState_LiveSnapshotting
10944 || oldMachineState == MachineState_Stuck
10945 || oldMachineState == MachineState_Starting
10946 || oldMachineState == MachineState_Stopping
10947 || oldMachineState == MachineState_Saving
10948 || oldMachineState == MachineState_Restoring
10949 || oldMachineState == MachineState_TeleportingPausedVM
10950 || oldMachineState == MachineState_TeleportingIn
10951 )
10952 && ( aMachineState == MachineState_PoweredOff
10953 || aMachineState == MachineState_Saved
10954 || aMachineState == MachineState_Teleported
10955 || aMachineState == MachineState_Aborted
10956 )
10957 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
10958 * snapshot */
10959 && ( mSnapshotData.mSnapshot.isNull()
10960 || mSnapshotData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
10961 )
10962 )
10963 {
10964 /* The EMT thread has just stopped, unlock attached media. Note that as
10965 * opposed to locking that is done from Console, we do unlocking here
10966 * because the VM process may have aborted before having a chance to
10967 * properly unlock all media it locked. */
10968
10969 unlockMedia();
10970 }
10971
10972 if (oldMachineState == MachineState_Restoring)
10973 {
10974 if (aMachineState != MachineState_Saved)
10975 {
10976 /*
10977 * delete the saved state file once the machine has finished
10978 * restoring from it (note that Console sets the state from
10979 * Restoring to Saved if the VM couldn't restore successfully,
10980 * to give the user an ability to fix an error and retry --
10981 * we keep the saved state file in this case)
10982 */
10983 deleteSavedState = true;
10984 }
10985 }
10986 else if ( oldMachineState == MachineState_Saved
10987 && ( aMachineState == MachineState_PoweredOff
10988 || aMachineState == MachineState_Aborted
10989 || aMachineState == MachineState_Teleported
10990 )
10991 )
10992 {
10993 /*
10994 * delete the saved state after Console::ForgetSavedState() is called
10995 * or if the VM process (owning a direct VM session) crashed while the
10996 * VM was Saved
10997 */
10998
10999 /// @todo (dmik)
11000 // Not sure that deleting the saved state file just because of the
11001 // client death before it attempted to restore the VM is a good
11002 // thing. But when it crashes we need to go to the Aborted state
11003 // which cannot have the saved state file associated... The only
11004 // way to fix this is to make the Aborted condition not a VM state
11005 // but a bool flag: i.e., when a crash occurs, set it to true and
11006 // change the state to PoweredOff or Saved depending on the
11007 // saved state presence.
11008
11009 deleteSavedState = true;
11010 mData->mCurrentStateModified = TRUE;
11011 stsFlags |= SaveSTS_CurStateModified;
11012 }
11013
11014 if ( aMachineState == MachineState_Starting
11015 || aMachineState == MachineState_Restoring
11016 || aMachineState == MachineState_TeleportingIn
11017 )
11018 {
11019 /* set the current state modified flag to indicate that the current
11020 * state is no more identical to the state in the
11021 * current snapshot */
11022 if (!mData->mCurrentSnapshot.isNull())
11023 {
11024 mData->mCurrentStateModified = TRUE;
11025 stsFlags |= SaveSTS_CurStateModified;
11026 }
11027 }
11028
11029 if (deleteSavedState)
11030 {
11031 if (mRemoveSavedState)
11032 {
11033 Assert(!mSSData->mStateFilePath.isEmpty());
11034 RTFileDelete(mSSData->mStateFilePath.c_str());
11035 }
11036 mSSData->mStateFilePath.setNull();
11037 stsFlags |= SaveSTS_StateFilePath;
11038 }
11039
11040 /* redirect to the underlying peer machine */
11041 mPeer->setMachineState(aMachineState);
11042
11043 if ( aMachineState == MachineState_PoweredOff
11044 || aMachineState == MachineState_Teleported
11045 || aMachineState == MachineState_Aborted
11046 || aMachineState == MachineState_Saved)
11047 {
11048 /* the machine has stopped execution
11049 * (or the saved state file was adopted) */
11050 stsFlags |= SaveSTS_StateTimeStamp;
11051 }
11052
11053 if ( ( oldMachineState == MachineState_PoweredOff
11054 || oldMachineState == MachineState_Aborted
11055 || oldMachineState == MachineState_Teleported
11056 )
11057 && aMachineState == MachineState_Saved)
11058 {
11059 /* the saved state file was adopted */
11060 Assert(!mSSData->mStateFilePath.isEmpty());
11061 stsFlags |= SaveSTS_StateFilePath;
11062 }
11063
11064 if ( aMachineState == MachineState_PoweredOff
11065 || aMachineState == MachineState_Aborted
11066 || aMachineState == MachineState_Teleported)
11067 {
11068 /* Make sure any transient guest properties get removed from the
11069 * property store on shutdown. */
11070
11071 HWData::GuestPropertyList::iterator it;
11072 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
11073 if (!fNeedsSaving)
11074 for (it = mHWData->mGuestProperties.begin();
11075 it != mHWData->mGuestProperties.end(); ++it)
11076 if (it->mFlags & guestProp::TRANSIENT)
11077 {
11078 fNeedsSaving = true;
11079 break;
11080 }
11081 if (fNeedsSaving)
11082 {
11083 mData->mCurrentStateModified = TRUE;
11084 stsFlags |= SaveSTS_CurStateModified;
11085 SaveSettings();
11086 }
11087 }
11088
11089 rc = saveStateSettings(stsFlags);
11090
11091 if ( ( oldMachineState != MachineState_PoweredOff
11092 && oldMachineState != MachineState_Aborted
11093 && oldMachineState != MachineState_Teleported
11094 )
11095 && ( aMachineState == MachineState_PoweredOff
11096 || aMachineState == MachineState_Aborted
11097 || aMachineState == MachineState_Teleported
11098 )
11099 )
11100 {
11101 /* we've been shut down for any reason */
11102 /* no special action so far */
11103 }
11104
11105 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
11106 LogFlowThisFuncLeave();
11107 return rc;
11108}
11109
11110/**
11111 * Sends the current machine state value to the VM process.
11112 *
11113 * @note Locks this object for reading, then calls a client process.
11114 */
11115HRESULT SessionMachine::updateMachineStateOnClient()
11116{
11117 AutoCaller autoCaller(this);
11118 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11119
11120 ComPtr<IInternalSessionControl> directControl;
11121 {
11122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11123 AssertReturn(!!mData, E_FAIL);
11124 directControl = mData->mSession.mDirectControl;
11125
11126 /* directControl may be already set to NULL here in #OnSessionEnd()
11127 * called too early by the direct session process while there is still
11128 * some operation (like deleting the snapshot) in progress. The client
11129 * process in this case is waiting inside Session::close() for the
11130 * "end session" process object to complete, while #uninit() called by
11131 * #checkForDeath() on the Watcher thread is waiting for the pending
11132 * operation to complete. For now, we accept this inconsitent behavior
11133 * and simply do nothing here. */
11134
11135 if (mData->mSession.mState == SessionState_Closing)
11136 return S_OK;
11137
11138 AssertReturn(!directControl.isNull(), E_FAIL);
11139 }
11140
11141 return directControl->UpdateMachineState(mData->mMachineState);
11142}
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