VirtualBox

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

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

Main: Save guest dimension in save state; add method for queering this info to IMachine.

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