VirtualBox

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

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

Main: Reapplied r62200 with bug fixes.

  • 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 29937 2010-06-01 08:41:32Z 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.

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