VirtualBox

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

Last change on this file since 31046 was 31046, checked in by vboxsync, 15 years ago

Main: don't emit session changed event if it doesn't changed

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

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