VirtualBox

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

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

Main: less string copying with media paths

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

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