VirtualBox

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

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

Main: partial revert or r67042, bring back the overwrite parameter

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

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