VirtualBox

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

Last change on this file since 33517 was 33504, checked in by vboxsync, 14 years ago

Default large page support to on for new VMs or VMs that didn't specify this setting before. (only relevant for nested paging)

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