VirtualBox

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

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

MachineImpl.cpp: Drop the hardcoded xml suffixes. (A bit ugly, but it's better than hardcoding things.)

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

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