VirtualBox

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

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

Main: Rewrote ProgressProxyImpl.cpp to proxy at the most one progress object and skip the final operation of the proxy object that wasn't really needed in the openRemoteSession case. Operations are proxied as well so that the openRemoteSession user can wait for the teleporter to become operational by waiting for the 3rd last operation to complete in a loop(!).

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

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