VirtualBox

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

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

Main/Medium: Implement medium type Shareable, and along the way added a method to query the medium variant. Lots of terminology cleanup to make sure the error messages talk about "medium" instead of "hard disk", as the latter sometimes also showed up in errors about floppy/DVD images.

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

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