VirtualBox

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

Last change on this file since 31907 was 31825, checked in by vboxsync, 14 years ago

Machine::GetHWVirtExProperty: Missing break.

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