VirtualBox

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

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

Disable an assert for DEBUG_sunlover.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use