VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/MachineImpl.cpp@ 49742

Last change on this file since 49742 was 49742, checked in by vboxsync, 11 years ago

6813 stage 2 - Use the server side API wrapper code..

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 506.9 KB
Line 
1/* $Id: MachineImpl.cpp 49742 2013-12-02 17:59:21Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "GuestImpl.h"
42#include "StorageControllerImpl.h"
43#include "DisplayImpl.h"
44#include "DisplayUtils.h"
45#include "MachineImplCloneVM.h"
46#include "AutostartDb.h"
47#include "SystemPropertiesImpl.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70#include <iprt/base64.h>
71
72#include <VBox/com/array.h>
73#include <VBox/com/list.h>
74
75#include <VBox/err.h>
76#include <VBox/param.h>
77#include <VBox/settings.h>
78#include <VBox/vmm/ssm.h>
79
80#ifdef VBOX_WITH_GUEST_PROPS
81# include <VBox/HostServices/GuestPropertySvc.h>
82# include <VBox/com/array.h>
83#endif
84
85#include "VBox/com/MultiResult.h"
86
87#include <algorithm>
88
89#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
90# define HOSTSUFF_EXE ".exe"
91#else /* !RT_OS_WINDOWS */
92# define HOSTSUFF_EXE ""
93#endif /* !RT_OS_WINDOWS */
94
95// defines / prototypes
96/////////////////////////////////////////////////////////////////////////////
97
98/////////////////////////////////////////////////////////////////////////////
99// Machine::Data structure
100/////////////////////////////////////////////////////////////////////////////
101
102Machine::Data::Data()
103{
104 mRegistered = FALSE;
105 pMachineConfigFile = NULL;
106 /* Contains hints on what has changed when the user is using the VM (config
107 * changes, running the VM, ...). This is used to decide if a config needs
108 * to be written to disk. */
109 flModifications = 0;
110 /* VM modification usually also trigger setting the current state to
111 * "Modified". Although this is not always the case. An e.g. is the VM
112 * initialization phase or when snapshot related data is changed. The
113 * actually behavior is controlled by the following flag. */
114 m_fAllowStateModification = false;
115 mAccessible = FALSE;
116 /* mUuid is initialized in Machine::init() */
117
118 mMachineState = MachineState_PoweredOff;
119 RTTimeNow(&mLastStateChange);
120
121 mMachineStateDeps = 0;
122 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
123 mMachineStateChangePending = 0;
124
125 mCurrentStateModified = TRUE;
126 mGuestPropertiesModified = FALSE;
127
128 mSession.mPID = NIL_RTPROCESS;
129 mSession.mState = SessionState_Unlocked;
130}
131
132Machine::Data::~Data()
133{
134 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
135 {
136 RTSemEventMultiDestroy(mMachineStateDepsSem);
137 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
138 }
139 if (pMachineConfigFile)
140 {
141 delete pMachineConfigFile;
142 pMachineConfigFile = NULL;
143 }
144}
145
146/////////////////////////////////////////////////////////////////////////////
147// Machine::HWData structure
148/////////////////////////////////////////////////////////////////////////////
149
150Machine::HWData::HWData()
151{
152 /* default values for a newly created machine */
153 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
154 mMemorySize = 128;
155 mCPUCount = 1;
156 mCPUHotPlugEnabled = false;
157 mMemoryBalloonSize = 0;
158 mPageFusionEnabled = false;
159 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
160 mVRAMSize = 8;
161 mAccelerate3DEnabled = false;
162 mAccelerate2DVideoEnabled = false;
163 mMonitorCount = 1;
164 mVideoCaptureWidth = 1024;
165 mVideoCaptureHeight = 768;
166 mVideoCaptureRate = 512;
167 mVideoCaptureFPS = 25;
168 mVideoCaptureEnabled = false;
169 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
170 maVideoCaptureScreens[i] = true;
171
172 mHWVirtExEnabled = true;
173 mHWVirtExNestedPagingEnabled = true;
174#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
175 mHWVirtExLargePagesEnabled = true;
176#else
177 /* Not supported on 32 bits hosts. */
178 mHWVirtExLargePagesEnabled = false;
179#endif
180 mHWVirtExVPIDEnabled = true;
181 mHWVirtExUXEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
189 mSyntheticCpu = false;
190 mTripleFaultReset = false;
191 mHPETEnabled = false;
192
193 /* default boot order: floppy - DVD - HDD */
194 mBootOrder[0] = DeviceType_Floppy;
195 mBootOrder[1] = DeviceType_DVD;
196 mBootOrder[2] = DeviceType_HardDisk;
197 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
198 mBootOrder[i] = DeviceType_Null;
199
200 mClipboardMode = ClipboardMode_Disabled;
201 mDragAndDropMode = DragAndDropMode_Disabled;
202 mGuestPropertyNotificationPatterns = "";
203
204 mFirmwareType = FirmwareType_BIOS;
205 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
206 mPointingHIDType = PointingHIDType_PS2Mouse;
207 mChipsetType = ChipsetType_PIIX3;
208 mEmulatedUSBCardReaderEnabled = FALSE;
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 execution cap by default. */
217 mCpuExecutionCap = 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#ifdef VBOX_WITH_RESOURCE_USAGE_API
245 mCollectorGuest(NULL),
246#endif
247 mPeer(NULL),
248 mParent(NULL),
249 mSerialPorts(),
250 mParallelPorts(),
251 uRegistryNeedsSaving(0)
252{}
253
254Machine::~Machine()
255{}
256
257HRESULT Machine::FinalConstruct()
258{
259 LogFlowThisFunc(("\n"));
260 return BaseFinalConstruct();
261}
262
263void Machine::FinalRelease()
264{
265 LogFlowThisFunc(("\n"));
266 uninit();
267 BaseFinalRelease();
268}
269
270/**
271 * Initializes a new machine instance; this init() variant creates a new, empty machine.
272 * This gets called from VirtualBox::CreateMachine().
273 *
274 * @param aParent Associated parent object
275 * @param strConfigFile Local file system path to the VM settings file (can
276 * be relative to the VirtualBox config directory).
277 * @param strName name for the machine
278 * @param llGroups list of groups for the machine
279 * @param aOsType OS Type of this machine or NULL.
280 * @param aId UUID for the new machine.
281 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
282 *
283 * @return Success indicator. if not S_OK, the machine object is invalid
284 */
285HRESULT Machine::init(VirtualBox *aParent,
286 const Utf8Str &strConfigFile,
287 const Utf8Str &strName,
288 const StringsList &llGroups,
289 GuestOSType *aOsType,
290 const Guid &aId,
291 bool fForceOverwrite,
292 bool fDirectoryIncludesUUID)
293{
294 LogFlowThisFuncEnter();
295 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
296
297 /* Enclose the state transition NotReady->InInit->Ready */
298 AutoInitSpan autoInitSpan(this);
299 AssertReturn(autoInitSpan.isOk(), E_FAIL);
300
301 HRESULT rc = initImpl(aParent, strConfigFile);
302 if (FAILED(rc)) return rc;
303
304 rc = tryCreateMachineConfigFile(fForceOverwrite);
305 if (FAILED(rc)) return rc;
306
307 if (SUCCEEDED(rc))
308 {
309 // create an empty machine config
310 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
311
312 rc = initDataAndChildObjects();
313 }
314
315 if (SUCCEEDED(rc))
316 {
317 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
318 mData->mAccessible = TRUE;
319
320 unconst(mData->mUuid) = aId;
321
322 mUserData->s.strName = strName;
323
324 mUserData->s.llGroups = llGroups;
325
326 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
327 // the "name sync" flag determines whether the machine directory gets renamed along
328 // with the machine file; say so if the settings file name is the same as the
329 // settings file parent directory (machine directory)
330 mUserData->s.fNameSync = isInOwnDir();
331
332 // initialize the default snapshots folder
333 rc = COMSETTER(SnapshotFolder)(NULL);
334 AssertComRC(rc);
335
336 if (aOsType)
337 {
338 /* Store OS type */
339 mUserData->s.strOsType = aOsType->i_id();
340
341 /* Apply BIOS defaults */
342 mBIOSSettings->applyDefaults(aOsType);
343
344 /* Apply network adapters defaults */
345 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
346 mNetworkAdapters[slot]->applyDefaults(aOsType);
347
348 /* Apply serial port defaults */
349 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
350 mSerialPorts[slot]->i_applyDefaults(aOsType);
351
352 /* Let the OS type select 64-bit ness. */
353 mHWData->mLongMode = aOsType->i_is64Bit()
354 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
355 }
356
357 /* At this point the changing of the current state modification
358 * flag is allowed. */
359 allowStateModification();
360
361 /* commit all changes made during the initialization */
362 commit();
363 }
364
365 /* Confirm a successful initialization when it's the case */
366 if (SUCCEEDED(rc))
367 {
368 if (mData->mAccessible)
369 autoInitSpan.setSucceeded();
370 else
371 autoInitSpan.setLimited();
372 }
373
374 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
375 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
376 mData->mRegistered,
377 mData->mAccessible,
378 rc));
379
380 LogFlowThisFuncLeave();
381
382 return rc;
383}
384
385/**
386 * Initializes a new instance with data from machine XML (formerly Init_Registered).
387 * Gets called in two modes:
388 *
389 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
390 * UUID is specified and we mark the machine as "registered";
391 *
392 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
393 * and the machine remains unregistered until RegisterMachine() is called.
394 *
395 * @param aParent Associated parent object
396 * @param aConfigFile Local file system path to the VM settings file (can
397 * be relative to the VirtualBox config directory).
398 * @param aId UUID of the machine or NULL (see above).
399 *
400 * @return Success indicator. if not S_OK, the machine object is invalid
401 */
402HRESULT Machine::initFromSettings(VirtualBox *aParent,
403 const Utf8Str &strConfigFile,
404 const Guid *aId)
405{
406 LogFlowThisFuncEnter();
407 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
408
409 /* Enclose the state transition NotReady->InInit->Ready */
410 AutoInitSpan autoInitSpan(this);
411 AssertReturn(autoInitSpan.isOk(), E_FAIL);
412
413 HRESULT rc = initImpl(aParent, strConfigFile);
414 if (FAILED(rc)) return rc;
415
416 if (aId)
417 {
418 // loading a registered VM:
419 unconst(mData->mUuid) = *aId;
420 mData->mRegistered = TRUE;
421 // now load the settings from XML:
422 rc = registeredInit();
423 // this calls initDataAndChildObjects() and loadSettings()
424 }
425 else
426 {
427 // opening an unregistered VM (VirtualBox::OpenMachine()):
428 rc = initDataAndChildObjects();
429
430 if (SUCCEEDED(rc))
431 {
432 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
433 mData->mAccessible = TRUE;
434
435 try
436 {
437 // load and parse machine XML; this will throw on XML or logic errors
438 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
439
440 // reject VM UUID duplicates, they can happen if someone
441 // tries to register an already known VM config again
442 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
443 true /* fPermitInaccessible */,
444 false /* aDoSetError */,
445 NULL) != VBOX_E_OBJECT_NOT_FOUND)
446 {
447 throw setError(E_FAIL,
448 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
449 mData->m_strConfigFile.c_str());
450 }
451
452 // use UUID from machine config
453 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
454
455 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
456 NULL /* puuidRegistry */);
457 if (FAILED(rc)) throw rc;
458
459 /* At this point the changing of the current state modification
460 * flag is allowed. */
461 allowStateModification();
462
463 commit();
464 }
465 catch (HRESULT err)
466 {
467 /* we assume that error info is set by the thrower */
468 rc = err;
469 }
470 catch (...)
471 {
472 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
473 }
474 }
475 }
476
477 /* Confirm a successful initialization when it's the case */
478 if (SUCCEEDED(rc))
479 {
480 if (mData->mAccessible)
481 autoInitSpan.setSucceeded();
482 else
483 {
484 autoInitSpan.setLimited();
485
486 // uninit media from this machine's media registry, or else
487 // reloading the settings will fail
488 mParent->unregisterMachineMedia(getId());
489 }
490 }
491
492 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
493 "rc=%08X\n",
494 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
495 mData->mRegistered, mData->mAccessible, rc));
496
497 LogFlowThisFuncLeave();
498
499 return rc;
500}
501
502/**
503 * Initializes a new instance from a machine config that is already in memory
504 * (import OVF case). Since we are importing, the UUID in the machine
505 * config is ignored and we always generate a fresh one.
506 *
507 * @param strName Name for the new machine; this overrides what is specified in config and is used
508 * for the settings file as well.
509 * @param config Machine configuration loaded and parsed from XML.
510 *
511 * @return Success indicator. if not S_OK, the machine object is invalid
512 */
513HRESULT Machine::init(VirtualBox *aParent,
514 const Utf8Str &strName,
515 const settings::MachineConfigFile &config)
516{
517 LogFlowThisFuncEnter();
518
519 /* Enclose the state transition NotReady->InInit->Ready */
520 AutoInitSpan autoInitSpan(this);
521 AssertReturn(autoInitSpan.isOk(), E_FAIL);
522
523 Utf8Str strConfigFile;
524 aParent->getDefaultMachineFolder(strConfigFile);
525 strConfigFile.append(RTPATH_DELIMITER);
526 strConfigFile.append(strName);
527 strConfigFile.append(RTPATH_DELIMITER);
528 strConfigFile.append(strName);
529 strConfigFile.append(".vbox");
530
531 HRESULT rc = initImpl(aParent, strConfigFile);
532 if (FAILED(rc)) return rc;
533
534 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
535 if (FAILED(rc)) return rc;
536
537 rc = initDataAndChildObjects();
538
539 if (SUCCEEDED(rc))
540 {
541 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
542 mData->mAccessible = TRUE;
543
544 // create empty machine config for instance data
545 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
546
547 // generate fresh UUID, ignore machine config
548 unconst(mData->mUuid).create();
549
550 rc = loadMachineDataFromSettings(config,
551 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
552
553 // override VM name as well, it may be different
554 mUserData->s.strName = strName;
555
556 if (SUCCEEDED(rc))
557 {
558 /* At this point the changing of the current state modification
559 * flag is allowed. */
560 allowStateModification();
561
562 /* commit all changes made during the initialization */
563 commit();
564 }
565 }
566
567 /* Confirm a successful initialization when it's the case */
568 if (SUCCEEDED(rc))
569 {
570 if (mData->mAccessible)
571 autoInitSpan.setSucceeded();
572 else
573 {
574 autoInitSpan.setLimited();
575
576 // uninit media from this machine's media registry, or else
577 // reloading the settings will fail
578 mParent->unregisterMachineMedia(getId());
579 }
580 }
581
582 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
583 "rc=%08X\n",
584 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
585 mData->mRegistered, mData->mAccessible, rc));
586
587 LogFlowThisFuncLeave();
588
589 return rc;
590}
591
592/**
593 * Shared code between the various init() implementations.
594 * @param aParent
595 * @return
596 */
597HRESULT Machine::initImpl(VirtualBox *aParent,
598 const Utf8Str &strConfigFile)
599{
600 LogFlowThisFuncEnter();
601
602 AssertReturn(aParent, E_INVALIDARG);
603 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
604
605 HRESULT rc = S_OK;
606
607 /* share the parent weakly */
608 unconst(mParent) = aParent;
609
610 /* allocate the essential machine data structure (the rest will be
611 * allocated later by initDataAndChildObjects() */
612 mData.allocate();
613
614 /* memorize the config file name (as provided) */
615 mData->m_strConfigFile = strConfigFile;
616
617 /* get the full file name */
618 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
619 if (RT_FAILURE(vrc1))
620 return setError(VBOX_E_FILE_ERROR,
621 tr("Invalid machine settings file name '%s' (%Rrc)"),
622 strConfigFile.c_str(),
623 vrc1);
624
625 LogFlowThisFuncLeave();
626
627 return rc;
628}
629
630/**
631 * Tries to create a machine settings file in the path stored in the machine
632 * instance data. Used when a new machine is created to fail gracefully if
633 * the settings file could not be written (e.g. because machine dir is read-only).
634 * @return
635 */
636HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
637{
638 HRESULT rc = S_OK;
639
640 // when we create a new machine, we must be able to create the settings file
641 RTFILE f = NIL_RTFILE;
642 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
643 if ( RT_SUCCESS(vrc)
644 || vrc == VERR_SHARING_VIOLATION
645 )
646 {
647 if (RT_SUCCESS(vrc))
648 RTFileClose(f);
649 if (!fForceOverwrite)
650 rc = setError(VBOX_E_FILE_ERROR,
651 tr("Machine settings file '%s' already exists"),
652 mData->m_strConfigFileFull.c_str());
653 else
654 {
655 /* try to delete the config file, as otherwise the creation
656 * of a new settings file will fail. */
657 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
658 if (RT_FAILURE(vrc2))
659 rc = setError(VBOX_E_FILE_ERROR,
660 tr("Could not delete the existing settings file '%s' (%Rrc)"),
661 mData->m_strConfigFileFull.c_str(), vrc2);
662 }
663 }
664 else if ( vrc != VERR_FILE_NOT_FOUND
665 && vrc != VERR_PATH_NOT_FOUND
666 )
667 rc = setError(VBOX_E_FILE_ERROR,
668 tr("Invalid machine settings file name '%s' (%Rrc)"),
669 mData->m_strConfigFileFull.c_str(),
670 vrc);
671 return rc;
672}
673
674/**
675 * Initializes the registered machine by loading the settings file.
676 * This method is separated from #init() in order to make it possible to
677 * retry the operation after VirtualBox startup instead of refusing to
678 * startup the whole VirtualBox server in case if the settings file of some
679 * registered VM is invalid or inaccessible.
680 *
681 * @note Must be always called from this object's write lock
682 * (unless called from #init() that doesn't need any locking).
683 * @note Locks the mUSBController method for writing.
684 * @note Subclasses must not call this method.
685 */
686HRESULT Machine::registeredInit()
687{
688 AssertReturn(!isSessionMachine(), E_FAIL);
689 AssertReturn(!isSnapshotMachine(), E_FAIL);
690 AssertReturn(mData->mUuid.isValid(), E_FAIL);
691 AssertReturn(!mData->mAccessible, E_FAIL);
692
693 HRESULT rc = initDataAndChildObjects();
694
695 if (SUCCEEDED(rc))
696 {
697 /* Temporarily reset the registered flag in order to let setters
698 * potentially called from loadSettings() succeed (isMutable() used in
699 * all setters will return FALSE for a Machine instance if mRegistered
700 * is TRUE). */
701 mData->mRegistered = FALSE;
702
703 try
704 {
705 // load and parse machine XML; this will throw on XML or logic errors
706 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
707
708 if (mData->mUuid != mData->pMachineConfigFile->uuid)
709 throw setError(E_FAIL,
710 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
711 mData->pMachineConfigFile->uuid.raw(),
712 mData->m_strConfigFileFull.c_str(),
713 mData->mUuid.toString().c_str(),
714 mParent->settingsFilePath().c_str());
715
716 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
717 NULL /* const Guid *puuidRegistry */);
718 if (FAILED(rc)) throw rc;
719 }
720 catch (HRESULT err)
721 {
722 /* we assume that error info is set by the thrower */
723 rc = err;
724 }
725 catch (...)
726 {
727 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
728 }
729
730 /* Restore the registered flag (even on failure) */
731 mData->mRegistered = TRUE;
732 }
733
734 if (SUCCEEDED(rc))
735 {
736 /* Set mAccessible to TRUE only if we successfully locked and loaded
737 * the settings file */
738 mData->mAccessible = TRUE;
739
740 /* commit all changes made during loading the settings file */
741 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
742 /// @todo r=klaus for some reason the settings loading logic backs up
743 // the settings, and therefore a commit is needed. Should probably be changed.
744 }
745 else
746 {
747 /* If the machine is registered, then, instead of returning a
748 * failure, we mark it as inaccessible and set the result to
749 * success to give it a try later */
750
751 /* fetch the current error info */
752 mData->mAccessError = com::ErrorInfo();
753 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
754 mData->mUuid.raw(),
755 mData->mAccessError.getText().raw()));
756
757 /* rollback all changes */
758 rollback(false /* aNotify */);
759
760 // uninit media from this machine's media registry, or else
761 // reloading the settings will fail
762 mParent->unregisterMachineMedia(getId());
763
764 /* uninitialize the common part to make sure all data is reset to
765 * default (null) values */
766 uninitDataAndChildObjects();
767
768 rc = S_OK;
769 }
770
771 return rc;
772}
773
774/**
775 * Uninitializes the instance.
776 * Called either from FinalRelease() or by the parent when it gets destroyed.
777 *
778 * @note The caller of this method must make sure that this object
779 * a) doesn't have active callers on the current thread and b) is not locked
780 * by the current thread; otherwise uninit() will hang either a) due to
781 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
782 * a dead-lock caused by this thread waiting for all callers on the other
783 * threads are done but preventing them from doing so by holding a lock.
784 */
785void Machine::uninit()
786{
787 LogFlowThisFuncEnter();
788
789 Assert(!isWriteLockOnCurrentThread());
790
791 Assert(!uRegistryNeedsSaving);
792 if (uRegistryNeedsSaving)
793 {
794 AutoCaller autoCaller(this);
795 if (SUCCEEDED(autoCaller.rc()))
796 {
797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
798 saveSettings(NULL, Machine::SaveS_Force);
799 }
800 }
801
802 /* Enclose the state transition Ready->InUninit->NotReady */
803 AutoUninitSpan autoUninitSpan(this);
804 if (autoUninitSpan.uninitDone())
805 return;
806
807 Assert(!isSnapshotMachine());
808 Assert(!isSessionMachine());
809 Assert(!!mData);
810
811 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
812 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
813
814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
815
816 if (!mData->mSession.mMachine.isNull())
817 {
818 /* Theoretically, this can only happen if the VirtualBox server has been
819 * terminated while there were clients running that owned open direct
820 * sessions. Since in this case we are definitely called by
821 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
822 * won't happen on the client watcher thread (because it does
823 * VirtualBox::addCaller() for the duration of the
824 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
825 * cannot happen until the VirtualBox caller is released). This is
826 * important, because SessionMachine::uninit() cannot correctly operate
827 * after we return from this method (it expects the Machine instance is
828 * still valid). We'll call it ourselves below.
829 */
830 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
831 (SessionMachine*)mData->mSession.mMachine));
832
833 if (Global::IsOnlineOrTransient(mData->mMachineState))
834 {
835 LogWarningThisFunc(("Setting state to Aborted!\n"));
836 /* set machine state using SessionMachine reimplementation */
837 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
838 }
839
840 /*
841 * Uninitialize SessionMachine using public uninit() to indicate
842 * an unexpected uninitialization.
843 */
844 mData->mSession.mMachine->uninit();
845 /* SessionMachine::uninit() must set mSession.mMachine to null */
846 Assert(mData->mSession.mMachine.isNull());
847 }
848
849 // uninit media from this machine's media registry, if they're still there
850 Guid uuidMachine(getId());
851
852 /* the lock is no more necessary (SessionMachine is uninitialized) */
853 alock.release();
854
855 /* XXX This will fail with
856 * "cannot be closed because it is still attached to 1 virtual machines"
857 * because at this point we did not call uninitDataAndChildObjects() yet
858 * and therefore also removeBackReference() for all these mediums was not called! */
859
860 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
861 mParent->unregisterMachineMedia(uuidMachine);
862
863 // has machine been modified?
864 if (mData->flModifications)
865 {
866 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
867 rollback(false /* aNotify */);
868 }
869
870 if (mData->mAccessible)
871 uninitDataAndChildObjects();
872
873 /* free the essential data structure last */
874 mData.free();
875
876 LogFlowThisFuncLeave();
877}
878
879// IMachine properties
880/////////////////////////////////////////////////////////////////////////////
881
882STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
883{
884 CheckComArgOutPointerValid(aParent);
885
886 AutoLimitedCaller autoCaller(this);
887 if (FAILED(autoCaller.rc())) return autoCaller.rc();
888
889 /* mParent is constant during life time, no need to lock */
890 ComObjPtr<VirtualBox> pVirtualBox(mParent);
891 pVirtualBox.queryInterfaceTo(aParent);
892
893 return S_OK;
894}
895
896STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
897{
898 CheckComArgOutPointerValid(aAccessible);
899
900 AutoLimitedCaller autoCaller(this);
901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
902
903 LogFlowThisFunc(("ENTER\n"));
904
905 /* In some cases (medium registry related), it is necessary to be able to
906 * go through the list of all machines. Happens when an inaccessible VM
907 * has a sensible medium registry. */
908 AutoReadLock mllock(mParent->getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
955{
956 CheckComArgOutPointerValid(aAccessError);
957
958 AutoLimitedCaller autoCaller(this);
959 if (FAILED(autoCaller.rc())) return autoCaller.rc();
960
961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
962
963 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
964 {
965 /* return shortly */
966 aAccessError = NULL;
967 return S_OK;
968 }
969
970 HRESULT rc = S_OK;
971
972 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
973 rc = errorInfo.createObject();
974 if (SUCCEEDED(rc))
975 {
976 errorInfo->init(mData->mAccessError.getResultCode(),
977 mData->mAccessError.getInterfaceID().ref(),
978 Utf8Str(mData->mAccessError.getComponent()).c_str(),
979 Utf8Str(mData->mAccessError.getText()));
980 rc = errorInfo.queryInterfaceTo(aAccessError);
981 }
982
983 return rc;
984}
985
986STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
987{
988 CheckComArgOutPointerValid(aName);
989
990 AutoCaller autoCaller(this);
991 if (FAILED(autoCaller.rc())) return autoCaller.rc();
992
993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 mUserData->s.strName.cloneTo(aName);
996
997 return S_OK;
998}
999
1000STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1001{
1002 CheckComArgStrNotEmptyOrNull(aName);
1003
1004 AutoCaller autoCaller(this);
1005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1006
1007 // prohibit setting a UUID only as the machine name, or else it can
1008 // never be found by findMachine()
1009 Guid test(aName);
1010
1011 if (test.isValid())
1012 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1013
1014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1015
1016 HRESULT rc = checkStateDependency(MutableStateDep);
1017 if (FAILED(rc)) return rc;
1018
1019 setModified(IsModified_MachineData);
1020 mUserData.backup();
1021 mUserData->s.strName = aName;
1022
1023 return S_OK;
1024}
1025
1026STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1027{
1028 CheckComArgOutPointerValid(aDescription);
1029
1030 AutoCaller autoCaller(this);
1031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1032
1033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 mUserData->s.strDescription.cloneTo(aDescription);
1036
1037 return S_OK;
1038}
1039
1040STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1041{
1042 AutoCaller autoCaller(this);
1043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1044
1045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1046
1047 // this can be done in principle in any state as it doesn't affect the VM
1048 // significantly, but play safe by not messing around while complex
1049 // activities are going on
1050 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1051 if (FAILED(rc)) return rc;
1052
1053 setModified(IsModified_MachineData);
1054 mUserData.backup();
1055 mUserData->s.strDescription = aDescription;
1056
1057 return S_OK;
1058}
1059
1060STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1061{
1062 CheckComArgOutPointerValid(aId);
1063
1064 AutoLimitedCaller autoCaller(this);
1065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1066
1067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1068
1069 mData->mUuid.toUtf16().cloneTo(aId);
1070
1071 return S_OK;
1072}
1073
1074STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1075{
1076 CheckComArgOutSafeArrayPointerValid(aGroups);
1077
1078 AutoCaller autoCaller(this);
1079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1080
1081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1082 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1083 size_t i = 0;
1084 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1085 it != mUserData->s.llGroups.end();
1086 ++it, i++)
1087 {
1088 Bstr tmp = *it;
1089 tmp.cloneTo(&groups[i]);
1090 }
1091 groups.detachTo(ComSafeArrayOutArg(aGroups));
1092
1093 return S_OK;
1094}
1095
1096STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1097{
1098 AutoCaller autoCaller(this);
1099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1100
1101 StringsList llGroups;
1102 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1103 if (FAILED(rc))
1104 return rc;
1105
1106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1107
1108 // changing machine groups is possible while the VM is offline
1109 rc = checkStateDependency(OfflineStateDep);
1110 if (FAILED(rc)) return rc;
1111
1112 setModified(IsModified_MachineData);
1113 mUserData.backup();
1114 mUserData->s.llGroups = llGroups;
1115
1116 return S_OK;
1117}
1118
1119STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1120{
1121 CheckComArgOutPointerValid(aOSTypeId);
1122
1123 AutoCaller autoCaller(this);
1124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1125
1126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1127
1128 mUserData->s.strOsType.cloneTo(aOSTypeId);
1129
1130 return S_OK;
1131}
1132
1133STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1134{
1135 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1136
1137 AutoCaller autoCaller(this);
1138 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1139
1140 /* look up the object by Id to check it is valid */
1141 ComPtr<IGuestOSType> guestOSType;
1142 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1143 if (FAILED(rc)) return rc;
1144
1145 /* when setting, always use the "etalon" value for consistency -- lookup
1146 * by ID is case-insensitive and the input value may have different case */
1147 Bstr osTypeId;
1148 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1149 if (FAILED(rc)) return rc;
1150
1151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1152
1153 rc = checkStateDependency(MutableStateDep);
1154 if (FAILED(rc)) return rc;
1155
1156 setModified(IsModified_MachineData);
1157 mUserData.backup();
1158 mUserData->s.strOsType = osTypeId;
1159
1160 return S_OK;
1161}
1162
1163
1164STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1165{
1166 CheckComArgOutPointerValid(aFirmwareType);
1167
1168 AutoCaller autoCaller(this);
1169 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1170
1171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1172
1173 *aFirmwareType = mHWData->mFirmwareType;
1174
1175 return S_OK;
1176}
1177
1178STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1179{
1180 AutoCaller autoCaller(this);
1181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 HRESULT rc = checkStateDependency(MutableStateDep);
1185 if (FAILED(rc)) return rc;
1186
1187 setModified(IsModified_MachineData);
1188 mHWData.backup();
1189 mHWData->mFirmwareType = aFirmwareType;
1190
1191 return S_OK;
1192}
1193
1194STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1195{
1196 CheckComArgOutPointerValid(aKeyboardHIDType);
1197
1198 AutoCaller autoCaller(this);
1199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1200
1201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1204
1205 return S_OK;
1206}
1207
1208STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1209{
1210 AutoCaller autoCaller(this);
1211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1213
1214 HRESULT rc = checkStateDependency(MutableStateDep);
1215 if (FAILED(rc)) return rc;
1216
1217 setModified(IsModified_MachineData);
1218 mHWData.backup();
1219 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1220
1221 return S_OK;
1222}
1223
1224STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1225{
1226 CheckComArgOutPointerValid(aPointingHIDType);
1227
1228 AutoCaller autoCaller(this);
1229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1230
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 *aPointingHIDType = mHWData->mPointingHIDType;
1234
1235 return S_OK;
1236}
1237
1238STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1239{
1240 AutoCaller autoCaller(this);
1241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1243
1244 HRESULT rc = checkStateDependency(MutableStateDep);
1245 if (FAILED(rc)) return rc;
1246
1247 setModified(IsModified_MachineData);
1248 mHWData.backup();
1249 mHWData->mPointingHIDType = aPointingHIDType;
1250
1251 return S_OK;
1252}
1253
1254STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1255{
1256 CheckComArgOutPointerValid(aChipsetType);
1257
1258 AutoCaller autoCaller(this);
1259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1260
1261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 *aChipsetType = mHWData->mChipsetType;
1264
1265 return S_OK;
1266}
1267
1268STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1269{
1270 AutoCaller autoCaller(this);
1271 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1273
1274 HRESULT rc = checkStateDependency(MutableStateDep);
1275 if (FAILED(rc)) return rc;
1276
1277 if (aChipsetType != mHWData->mChipsetType)
1278 {
1279 setModified(IsModified_MachineData);
1280 mHWData.backup();
1281 mHWData->mChipsetType = aChipsetType;
1282
1283 // Resize network adapter array, to be finalized on commit/rollback.
1284 // We must not throw away entries yet, otherwise settings are lost
1285 // without a way to roll back.
1286 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1287 size_t oldCount = mNetworkAdapters.size();
1288 if (newCount > oldCount)
1289 {
1290 mNetworkAdapters.resize(newCount);
1291 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1292 {
1293 unconst(mNetworkAdapters[slot]).createObject();
1294 mNetworkAdapters[slot]->init(this, slot);
1295 }
1296 }
1297 }
1298
1299 return S_OK;
1300}
1301
1302STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1303{
1304 CheckComArgOutPointerValid(aHWVersion);
1305
1306 AutoCaller autoCaller(this);
1307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1308
1309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1310
1311 mHWData->mHWVersion.cloneTo(aHWVersion);
1312
1313 return S_OK;
1314}
1315
1316STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1317{
1318 /* check known version */
1319 Utf8Str hwVersion = aHWVersion;
1320 if ( hwVersion.compare("1") != 0
1321 && hwVersion.compare("2") != 0)
1322 return setError(E_INVALIDARG,
1323 tr("Invalid hardware version: %ls\n"), aHWVersion);
1324
1325 AutoCaller autoCaller(this);
1326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1327
1328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1329
1330 HRESULT rc = checkStateDependency(MutableStateDep);
1331 if (FAILED(rc)) return rc;
1332
1333 setModified(IsModified_MachineData);
1334 mHWData.backup();
1335 mHWData->mHWVersion = hwVersion;
1336
1337 return S_OK;
1338}
1339
1340STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1341{
1342 CheckComArgOutPointerValid(aUUID);
1343
1344 AutoCaller autoCaller(this);
1345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1346
1347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1348
1349 if (!mHWData->mHardwareUUID.isZero())
1350 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1351 else
1352 mData->mUuid.toUtf16().cloneTo(aUUID);
1353
1354 return S_OK;
1355}
1356
1357STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1358{
1359 Guid hardwareUUID(aUUID);
1360 if (!hardwareUUID.isValid())
1361 return E_INVALIDARG;
1362
1363 AutoCaller autoCaller(this);
1364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1365
1366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1367
1368 HRESULT rc = checkStateDependency(MutableStateDep);
1369 if (FAILED(rc)) return rc;
1370
1371 setModified(IsModified_MachineData);
1372 mHWData.backup();
1373 if (hardwareUUID == mData->mUuid)
1374 mHWData->mHardwareUUID.clear();
1375 else
1376 mHWData->mHardwareUUID = hardwareUUID;
1377
1378 return S_OK;
1379}
1380
1381STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1382{
1383 CheckComArgOutPointerValid(memorySize);
1384
1385 AutoCaller autoCaller(this);
1386 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1387
1388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1389
1390 *memorySize = mHWData->mMemorySize;
1391
1392 return S_OK;
1393}
1394
1395STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1396{
1397 /* check RAM limits */
1398 if ( memorySize < MM_RAM_MIN_IN_MB
1399 || memorySize > MM_RAM_MAX_IN_MB
1400 )
1401 return setError(E_INVALIDARG,
1402 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1403 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1404
1405 AutoCaller autoCaller(this);
1406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1407
1408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 HRESULT rc = checkStateDependency(MutableStateDep);
1411 if (FAILED(rc)) return rc;
1412
1413 setModified(IsModified_MachineData);
1414 mHWData.backup();
1415 mHWData->mMemorySize = memorySize;
1416
1417 return S_OK;
1418}
1419
1420STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1421{
1422 CheckComArgOutPointerValid(CPUCount);
1423
1424 AutoCaller autoCaller(this);
1425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1426
1427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1428
1429 *CPUCount = mHWData->mCPUCount;
1430
1431 return S_OK;
1432}
1433
1434STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1435{
1436 /* check CPU limits */
1437 if ( CPUCount < SchemaDefs::MinCPUCount
1438 || CPUCount > SchemaDefs::MaxCPUCount
1439 )
1440 return setError(E_INVALIDARG,
1441 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1442 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1443
1444 AutoCaller autoCaller(this);
1445 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1446
1447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1448
1449 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1450 if (mHWData->mCPUHotPlugEnabled)
1451 {
1452 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1453 {
1454 if (mHWData->mCPUAttached[idx])
1455 return setError(E_INVALIDARG,
1456 tr("There is still a CPU attached to socket %lu."
1457 "Detach the CPU before removing the socket"),
1458 CPUCount, idx+1);
1459 }
1460 }
1461
1462 HRESULT rc = checkStateDependency(MutableStateDep);
1463 if (FAILED(rc)) return rc;
1464
1465 setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mCPUCount = CPUCount;
1468
1469 return S_OK;
1470}
1471
1472STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1473{
1474 CheckComArgOutPointerValid(aExecutionCap);
1475
1476 AutoCaller autoCaller(this);
1477 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1478
1479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1480
1481 *aExecutionCap = mHWData->mCpuExecutionCap;
1482
1483 return S_OK;
1484}
1485
1486STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1487{
1488 HRESULT rc = S_OK;
1489
1490 /* check throttle limits */
1491 if ( aExecutionCap < 1
1492 || aExecutionCap > 100
1493 )
1494 return setError(E_INVALIDARG,
1495 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1496 aExecutionCap, 1, 100);
1497
1498 AutoCaller autoCaller(this);
1499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1500
1501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1502
1503 alock.release();
1504 rc = onCPUExecutionCapChange(aExecutionCap);
1505 alock.acquire();
1506 if (FAILED(rc)) return rc;
1507
1508 setModified(IsModified_MachineData);
1509 mHWData.backup();
1510 mHWData->mCpuExecutionCap = aExecutionCap;
1511
1512 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1513 if (Global::IsOnline(mData->mMachineState))
1514 saveSettings(NULL);
1515
1516 return S_OK;
1517}
1518
1519
1520STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1521{
1522 CheckComArgOutPointerValid(aEnabled);
1523
1524 AutoCaller autoCaller(this);
1525 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1526
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aEnabled = mHWData->mCPUHotPlugEnabled;
1530
1531 return S_OK;
1532}
1533
1534STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1535{
1536 HRESULT rc = S_OK;
1537
1538 AutoCaller autoCaller(this);
1539 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1540
1541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1542
1543 rc = checkStateDependency(MutableStateDep);
1544 if (FAILED(rc)) return rc;
1545
1546 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1547 {
1548 if (aEnabled)
1549 {
1550 setModified(IsModified_MachineData);
1551 mHWData.backup();
1552
1553 /* Add the amount of CPUs currently attached */
1554 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1555 {
1556 mHWData->mCPUAttached[i] = true;
1557 }
1558 }
1559 else
1560 {
1561 /*
1562 * We can disable hotplug only if the amount of maximum CPUs is equal
1563 * to the amount of attached CPUs
1564 */
1565 unsigned cCpusAttached = 0;
1566 unsigned iHighestId = 0;
1567
1568 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1569 {
1570 if (mHWData->mCPUAttached[i])
1571 {
1572 cCpusAttached++;
1573 iHighestId = i;
1574 }
1575 }
1576
1577 if ( (cCpusAttached != mHWData->mCPUCount)
1578 || (iHighestId >= mHWData->mCPUCount))
1579 return setError(E_INVALIDARG,
1580 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1581
1582 setModified(IsModified_MachineData);
1583 mHWData.backup();
1584 }
1585 }
1586
1587 mHWData->mCPUHotPlugEnabled = aEnabled;
1588
1589 return rc;
1590}
1591
1592STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1593{
1594#ifdef VBOX_WITH_USB_CARDREADER
1595 CheckComArgOutPointerValid(aEnabled);
1596
1597 AutoCaller autoCaller(this);
1598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1599
1600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1601
1602 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1603
1604 return S_OK;
1605#else
1606 NOREF(aEnabled);
1607 return E_NOTIMPL;
1608#endif
1609}
1610
1611STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1612{
1613#ifdef VBOX_WITH_USB_CARDREADER
1614 AutoCaller autoCaller(this);
1615 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 HRESULT rc = checkStateDependency(MutableStateDep);
1619 if (FAILED(rc)) return rc;
1620
1621 setModified(IsModified_MachineData);
1622 mHWData.backup();
1623 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1624
1625 return S_OK;
1626#else
1627 NOREF(aEnabled);
1628 return E_NOTIMPL;
1629#endif
1630}
1631
1632STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1633{
1634 CheckComArgOutPointerValid(aEnabled);
1635
1636 AutoCaller autoCaller(this);
1637 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1639
1640 *aEnabled = mHWData->mHPETEnabled;
1641
1642 return S_OK;
1643}
1644
1645STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1646{
1647 HRESULT rc = S_OK;
1648
1649 AutoCaller autoCaller(this);
1650 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1652
1653 rc = checkStateDependency(MutableStateDep);
1654 if (FAILED(rc)) return rc;
1655
1656 setModified(IsModified_MachineData);
1657 mHWData.backup();
1658
1659 mHWData->mHPETEnabled = aEnabled;
1660
1661 return rc;
1662}
1663
1664STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1665{
1666 AutoCaller autoCaller(this);
1667 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1668
1669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1670
1671 *fEnabled = mHWData->mVideoCaptureEnabled;
1672 return S_OK;
1673}
1674
1675STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1676{
1677 HRESULT rc = S_OK;
1678
1679 AutoCaller autoCaller(this);
1680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 setModified(IsModified_MachineData);
1684 mHWData.backup();
1685 mHWData->mVideoCaptureEnabled = fEnabled;
1686
1687 alock.release();
1688 rc = onVideoCaptureChange();
1689 alock.acquire();
1690 if (FAILED(rc))
1691 {
1692 /*
1693 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1694 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1695 * determine if it should start or stop capturing. Therefore we need to manually
1696 * undo change.
1697 */
1698 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1699 return rc;
1700 }
1701
1702 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1703 if (Global::IsOnline(mData->mMachineState))
1704 saveSettings(NULL);
1705
1706 return rc;
1707}
1708
1709STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1710{
1711 CheckComArgOutSafeArrayPointerValid(aScreens);
1712
1713 AutoCaller autoCaller(this);
1714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1715
1716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1717
1718 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1719 for (unsigned i = 0; i < screens.size(); i++)
1720 screens[i] = mHWData->maVideoCaptureScreens[i];
1721 screens.detachTo(ComSafeArrayOutArg(aScreens));
1722 return S_OK;
1723}
1724
1725STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1726{
1727 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1728 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1729 bool fChanged = false;
1730
1731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1732
1733 for (unsigned i = 0; i < screens.size(); i++)
1734 {
1735 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1736 {
1737 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1738 fChanged = true;
1739 }
1740 }
1741 if (fChanged)
1742 {
1743 alock.release();
1744 HRESULT rc = onVideoCaptureChange();
1745 alock.acquire();
1746 if (FAILED(rc)) return rc;
1747 setModified(IsModified_MachineData);
1748
1749 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1750 if (Global::IsOnline(mData->mMachineState))
1751 saveSettings(NULL);
1752 }
1753
1754 return S_OK;
1755}
1756
1757STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1758{
1759 AutoCaller autoCaller(this);
1760 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1761
1762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1763 if (mHWData->mVideoCaptureFile.isEmpty())
1764 {
1765 Utf8Str defaultFile;
1766 getDefaultVideoCaptureFile(defaultFile);
1767 defaultFile.cloneTo(apFile);
1768 }
1769 else
1770 mHWData->mVideoCaptureFile.cloneTo(apFile);
1771 return S_OK;
1772}
1773
1774STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1775{
1776 Utf8Str strFile(aFile);
1777 AutoCaller autoCaller(this);
1778 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1779
1780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1781
1782 if ( Global::IsOnline(mData->mMachineState)
1783 && mHWData->mVideoCaptureEnabled)
1784 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1785
1786 if (!RTPathStartsWithRoot(strFile.c_str()))
1787 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1788
1789 if (!strFile.isEmpty())
1790 {
1791 Utf8Str defaultFile;
1792 getDefaultVideoCaptureFile(defaultFile);
1793 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1794 strFile.setNull();
1795 }
1796
1797 setModified(IsModified_MachineData);
1798 mHWData.backup();
1799 mHWData->mVideoCaptureFile = strFile;
1800
1801 return S_OK;
1802}
1803
1804STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1805{
1806 AutoCaller autoCaller(this);
1807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1808
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 *aHorzRes = mHWData->mVideoCaptureWidth;
1811 return S_OK;
1812}
1813
1814STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1815{
1816 AutoCaller autoCaller(this);
1817 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1818
1819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1820
1821 if ( Global::IsOnline(mData->mMachineState)
1822 && mHWData->mVideoCaptureEnabled)
1823 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1824
1825 setModified(IsModified_MachineData);
1826 mHWData.backup();
1827 mHWData->mVideoCaptureWidth = aHorzRes;
1828
1829 return S_OK;
1830}
1831
1832STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1833{
1834 AutoCaller autoCaller(this);
1835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1836
1837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1838 *aVertRes = mHWData->mVideoCaptureHeight;
1839 return S_OK;
1840}
1841
1842STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1843{
1844 AutoCaller autoCaller(this);
1845 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1846
1847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1848
1849 if ( Global::IsOnline(mData->mMachineState)
1850 && mHWData->mVideoCaptureEnabled)
1851 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1852
1853 setModified(IsModified_MachineData);
1854 mHWData.backup();
1855 mHWData->mVideoCaptureHeight = aVertRes;
1856
1857 return S_OK;
1858}
1859
1860STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1861{
1862 AutoCaller autoCaller(this);
1863 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1864
1865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1866 *aRate = mHWData->mVideoCaptureRate;
1867 return S_OK;
1868}
1869
1870STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1871{
1872 AutoCaller autoCaller(this);
1873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1874
1875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1876
1877 if ( Global::IsOnline(mData->mMachineState)
1878 && mHWData->mVideoCaptureEnabled)
1879 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1880
1881 setModified(IsModified_MachineData);
1882 mHWData.backup();
1883 mHWData->mVideoCaptureRate = aRate;
1884
1885 return S_OK;
1886}
1887
1888STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1889{
1890 AutoCaller autoCaller(this);
1891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1892
1893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1894 *aFPS = mHWData->mVideoCaptureFPS;
1895 return S_OK;
1896}
1897
1898STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1899{
1900 AutoCaller autoCaller(this);
1901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1902
1903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1904
1905 if ( Global::IsOnline(mData->mMachineState)
1906 && mHWData->mVideoCaptureEnabled)
1907 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1908
1909 setModified(IsModified_MachineData);
1910 mHWData.backup();
1911 mHWData->mVideoCaptureFPS = aFPS;
1912
1913 return S_OK;
1914}
1915
1916STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1917{
1918 CheckComArgOutPointerValid(aGraphicsControllerType);
1919
1920 AutoCaller autoCaller(this);
1921 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1922
1923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1924
1925 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1926
1927 return S_OK;
1928}
1929
1930STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1931{
1932 switch (aGraphicsControllerType)
1933 {
1934 case GraphicsControllerType_Null:
1935 case GraphicsControllerType_VBoxVGA:
1936 break;
1937 default:
1938 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1939 }
1940
1941 AutoCaller autoCaller(this);
1942 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1943
1944 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1945
1946 HRESULT rc = checkStateDependency(MutableStateDep);
1947 if (FAILED(rc)) return rc;
1948
1949 setModified(IsModified_MachineData);
1950 mHWData.backup();
1951 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1952
1953 return S_OK;
1954}
1955
1956STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1957{
1958 CheckComArgOutPointerValid(memorySize);
1959
1960 AutoCaller autoCaller(this);
1961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1962
1963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1964
1965 *memorySize = mHWData->mVRAMSize;
1966
1967 return S_OK;
1968}
1969
1970STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1971{
1972 /* check VRAM limits */
1973 if (memorySize < SchemaDefs::MinGuestVRAM ||
1974 memorySize > SchemaDefs::MaxGuestVRAM)
1975 return setError(E_INVALIDARG,
1976 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1977 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1978
1979 AutoCaller autoCaller(this);
1980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1981
1982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1983
1984 HRESULT rc = checkStateDependency(MutableStateDep);
1985 if (FAILED(rc)) return rc;
1986
1987 setModified(IsModified_MachineData);
1988 mHWData.backup();
1989 mHWData->mVRAMSize = memorySize;
1990
1991 return S_OK;
1992}
1993
1994/** @todo this method should not be public */
1995STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1996{
1997 CheckComArgOutPointerValid(memoryBalloonSize);
1998
1999 AutoCaller autoCaller(this);
2000 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2001
2002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2003
2004 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2005
2006 return S_OK;
2007}
2008
2009/**
2010 * Set the memory balloon size.
2011 *
2012 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2013 * we have to make sure that we never call IGuest from here.
2014 */
2015STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2016{
2017 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2018#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2019 /* check limits */
2020 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2021 return setError(E_INVALIDARG,
2022 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2023 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2024
2025 AutoCaller autoCaller(this);
2026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2027
2028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2029
2030 setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2033
2034 return S_OK;
2035#else
2036 NOREF(memoryBalloonSize);
2037 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2038#endif
2039}
2040
2041STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2042{
2043 CheckComArgOutPointerValid(aEnabled);
2044
2045 AutoCaller autoCaller(this);
2046 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2047
2048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2049
2050 *aEnabled = mHWData->mPageFusionEnabled;
2051 return S_OK;
2052}
2053
2054STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2055{
2056#ifdef VBOX_WITH_PAGE_SHARING
2057 AutoCaller autoCaller(this);
2058 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2059
2060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2061
2062 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2063 setModified(IsModified_MachineData);
2064 mHWData.backup();
2065 mHWData->mPageFusionEnabled = aEnabled;
2066 return S_OK;
2067#else
2068 NOREF(aEnabled);
2069 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2070#endif
2071}
2072
2073STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2074{
2075 CheckComArgOutPointerValid(aEnabled);
2076
2077 AutoCaller autoCaller(this);
2078 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2079
2080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2081
2082 *aEnabled = mHWData->mAccelerate3DEnabled;
2083
2084 return S_OK;
2085}
2086
2087STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2088{
2089 AutoCaller autoCaller(this);
2090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2091
2092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2093
2094 HRESULT rc = checkStateDependency(MutableStateDep);
2095 if (FAILED(rc)) return rc;
2096
2097 /** @todo check validity! */
2098
2099 setModified(IsModified_MachineData);
2100 mHWData.backup();
2101 mHWData->mAccelerate3DEnabled = enable;
2102
2103 return S_OK;
2104}
2105
2106
2107STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2108{
2109 CheckComArgOutPointerValid(aEnabled);
2110
2111 AutoCaller autoCaller(this);
2112 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2113
2114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2115
2116 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2117
2118 return S_OK;
2119}
2120
2121STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2122{
2123 AutoCaller autoCaller(this);
2124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2125
2126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2127
2128 HRESULT rc = checkStateDependency(MutableStateDep);
2129 if (FAILED(rc)) return rc;
2130
2131 /** @todo check validity! */
2132
2133 setModified(IsModified_MachineData);
2134 mHWData.backup();
2135 mHWData->mAccelerate2DVideoEnabled = enable;
2136
2137 return S_OK;
2138}
2139
2140STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2141{
2142 CheckComArgOutPointerValid(monitorCount);
2143
2144 AutoCaller autoCaller(this);
2145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2146
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 *monitorCount = mHWData->mMonitorCount;
2150
2151 return S_OK;
2152}
2153
2154STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2155{
2156 /* make sure monitor count is a sensible number */
2157 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2158 return setError(E_INVALIDARG,
2159 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2160 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2161
2162 AutoCaller autoCaller(this);
2163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2164
2165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2166
2167 HRESULT rc = checkStateDependency(MutableStateDep);
2168 if (FAILED(rc)) return rc;
2169
2170 setModified(IsModified_MachineData);
2171 mHWData.backup();
2172 mHWData->mMonitorCount = monitorCount;
2173
2174 return S_OK;
2175}
2176
2177STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2178{
2179 CheckComArgOutPointerValid(biosSettings);
2180
2181 AutoCaller autoCaller(this);
2182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2183
2184 /* mBIOSSettings is constant during life time, no need to lock */
2185 mBIOSSettings.queryInterfaceTo(biosSettings);
2186
2187 return S_OK;
2188}
2189
2190STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2191{
2192 CheckComArgOutPointerValid(aVal);
2193
2194 AutoCaller autoCaller(this);
2195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2196
2197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2198
2199 switch (property)
2200 {
2201 case CPUPropertyType_PAE:
2202 *aVal = mHWData->mPAEEnabled;
2203 break;
2204
2205 case CPUPropertyType_Synthetic:
2206 *aVal = mHWData->mSyntheticCpu;
2207 break;
2208
2209 case CPUPropertyType_LongMode:
2210 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2211 *aVal = TRUE;
2212 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2213 *aVal = FALSE;
2214#if HC_ARCH_BITS == 64
2215 else
2216 *aVal = TRUE;
2217#else
2218 else
2219 {
2220 *aVal = FALSE;
2221
2222 ComPtr<IGuestOSType> ptrGuestOSType;
2223 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2224 if (SUCCEEDED(hrc2))
2225 {
2226 BOOL fIs64Bit = FALSE;
2227 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2228 if (SUCCEEDED(hrc2) && fIs64Bit)
2229 {
2230 ComObjPtr<Host> ptrHost = mParent->host();
2231 alock.release();
2232
2233 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2234 if (FAILED(hrc2))
2235 *aVal = FALSE;
2236 }
2237 }
2238 }
2239#endif
2240 break;
2241
2242 case CPUPropertyType_TripleFaultReset:
2243 *aVal = mHWData->mTripleFaultReset;
2244 break;
2245
2246 default:
2247 return E_INVALIDARG;
2248 }
2249 return S_OK;
2250}
2251
2252STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2253{
2254 AutoCaller autoCaller(this);
2255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2256
2257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2258
2259 HRESULT rc = checkStateDependency(MutableStateDep);
2260 if (FAILED(rc)) return rc;
2261
2262 switch (property)
2263 {
2264 case CPUPropertyType_PAE:
2265 setModified(IsModified_MachineData);
2266 mHWData.backup();
2267 mHWData->mPAEEnabled = !!aVal;
2268 break;
2269
2270 case CPUPropertyType_Synthetic:
2271 setModified(IsModified_MachineData);
2272 mHWData.backup();
2273 mHWData->mSyntheticCpu = !!aVal;
2274 break;
2275
2276 case CPUPropertyType_LongMode:
2277 setModified(IsModified_MachineData);
2278 mHWData.backup();
2279 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2280 break;
2281
2282 case CPUPropertyType_TripleFaultReset:
2283 setModified(IsModified_MachineData);
2284 mHWData.backup();
2285 mHWData->mTripleFaultReset = !!aVal;
2286 break;
2287
2288 default:
2289 return E_INVALIDARG;
2290 }
2291 return S_OK;
2292}
2293
2294STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2295{
2296 CheckComArgOutPointerValid(aValEax);
2297 CheckComArgOutPointerValid(aValEbx);
2298 CheckComArgOutPointerValid(aValEcx);
2299 CheckComArgOutPointerValid(aValEdx);
2300
2301 AutoCaller autoCaller(this);
2302 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2303
2304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2305
2306 switch(aId)
2307 {
2308 case 0x0:
2309 case 0x1:
2310 case 0x2:
2311 case 0x3:
2312 case 0x4:
2313 case 0x5:
2314 case 0x6:
2315 case 0x7:
2316 case 0x8:
2317 case 0x9:
2318 case 0xA:
2319 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2320 return E_INVALIDARG;
2321
2322 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2323 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2324 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2325 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2326 break;
2327
2328 case 0x80000000:
2329 case 0x80000001:
2330 case 0x80000002:
2331 case 0x80000003:
2332 case 0x80000004:
2333 case 0x80000005:
2334 case 0x80000006:
2335 case 0x80000007:
2336 case 0x80000008:
2337 case 0x80000009:
2338 case 0x8000000A:
2339 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2340 return E_INVALIDARG;
2341
2342 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2343 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2344 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2345 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2346 break;
2347
2348 default:
2349 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2350 }
2351 return S_OK;
2352}
2353
2354STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2355{
2356 AutoCaller autoCaller(this);
2357 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2358
2359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2360
2361 HRESULT rc = checkStateDependency(MutableStateDep);
2362 if (FAILED(rc)) return rc;
2363
2364 switch(aId)
2365 {
2366 case 0x0:
2367 case 0x1:
2368 case 0x2:
2369 case 0x3:
2370 case 0x4:
2371 case 0x5:
2372 case 0x6:
2373 case 0x7:
2374 case 0x8:
2375 case 0x9:
2376 case 0xA:
2377 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2378 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2379 setModified(IsModified_MachineData);
2380 mHWData.backup();
2381 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2382 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2383 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2384 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2385 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2386 break;
2387
2388 case 0x80000000:
2389 case 0x80000001:
2390 case 0x80000002:
2391 case 0x80000003:
2392 case 0x80000004:
2393 case 0x80000005:
2394 case 0x80000006:
2395 case 0x80000007:
2396 case 0x80000008:
2397 case 0x80000009:
2398 case 0x8000000A:
2399 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2400 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2401 setModified(IsModified_MachineData);
2402 mHWData.backup();
2403 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2404 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2405 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2406 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2407 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2408 break;
2409
2410 default:
2411 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2412 }
2413 return S_OK;
2414}
2415
2416STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2417{
2418 AutoCaller autoCaller(this);
2419 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2420
2421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2422
2423 HRESULT rc = checkStateDependency(MutableStateDep);
2424 if (FAILED(rc)) return rc;
2425
2426 switch(aId)
2427 {
2428 case 0x0:
2429 case 0x1:
2430 case 0x2:
2431 case 0x3:
2432 case 0x4:
2433 case 0x5:
2434 case 0x6:
2435 case 0x7:
2436 case 0x8:
2437 case 0x9:
2438 case 0xA:
2439 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2440 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2441 setModified(IsModified_MachineData);
2442 mHWData.backup();
2443 /* Invalidate leaf. */
2444 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2445 break;
2446
2447 case 0x80000000:
2448 case 0x80000001:
2449 case 0x80000002:
2450 case 0x80000003:
2451 case 0x80000004:
2452 case 0x80000005:
2453 case 0x80000006:
2454 case 0x80000007:
2455 case 0x80000008:
2456 case 0x80000009:
2457 case 0x8000000A:
2458 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2459 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2460 setModified(IsModified_MachineData);
2461 mHWData.backup();
2462 /* Invalidate leaf. */
2463 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2464 break;
2465
2466 default:
2467 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2468 }
2469 return S_OK;
2470}
2471
2472STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2473{
2474 AutoCaller autoCaller(this);
2475 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2476
2477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2478
2479 HRESULT rc = checkStateDependency(MutableStateDep);
2480 if (FAILED(rc)) return rc;
2481
2482 setModified(IsModified_MachineData);
2483 mHWData.backup();
2484
2485 /* Invalidate all standard leafs. */
2486 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2487 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2488
2489 /* Invalidate all extended leafs. */
2490 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2491 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2492
2493 return S_OK;
2494}
2495
2496STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2497{
2498 CheckComArgOutPointerValid(aVal);
2499
2500 AutoCaller autoCaller(this);
2501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2502
2503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 switch(property)
2506 {
2507 case HWVirtExPropertyType_Enabled:
2508 *aVal = mHWData->mHWVirtExEnabled;
2509 break;
2510
2511 case HWVirtExPropertyType_VPID:
2512 *aVal = mHWData->mHWVirtExVPIDEnabled;
2513 break;
2514
2515 case HWVirtExPropertyType_NestedPaging:
2516 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2517 break;
2518
2519 case HWVirtExPropertyType_UnrestrictedExecution:
2520 *aVal = mHWData->mHWVirtExUXEnabled;
2521 break;
2522
2523 case HWVirtExPropertyType_LargePages:
2524 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2525#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2526 *aVal = FALSE;
2527#endif
2528 break;
2529
2530 case HWVirtExPropertyType_Force:
2531 *aVal = mHWData->mHWVirtExForceEnabled;
2532 break;
2533
2534 default:
2535 return E_INVALIDARG;
2536 }
2537 return S_OK;
2538}
2539
2540STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2541{
2542 AutoCaller autoCaller(this);
2543 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2544
2545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2546
2547 HRESULT rc = checkStateDependency(MutableStateDep);
2548 if (FAILED(rc)) return rc;
2549
2550 switch(property)
2551 {
2552 case HWVirtExPropertyType_Enabled:
2553 setModified(IsModified_MachineData);
2554 mHWData.backup();
2555 mHWData->mHWVirtExEnabled = !!aVal;
2556 break;
2557
2558 case HWVirtExPropertyType_VPID:
2559 setModified(IsModified_MachineData);
2560 mHWData.backup();
2561 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2562 break;
2563
2564 case HWVirtExPropertyType_NestedPaging:
2565 setModified(IsModified_MachineData);
2566 mHWData.backup();
2567 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2568 break;
2569
2570 case HWVirtExPropertyType_UnrestrictedExecution:
2571 setModified(IsModified_MachineData);
2572 mHWData.backup();
2573 mHWData->mHWVirtExUXEnabled = !!aVal;
2574 break;
2575
2576 case HWVirtExPropertyType_LargePages:
2577 setModified(IsModified_MachineData);
2578 mHWData.backup();
2579 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2580 break;
2581
2582 case HWVirtExPropertyType_Force:
2583 setModified(IsModified_MachineData);
2584 mHWData.backup();
2585 mHWData->mHWVirtExForceEnabled = !!aVal;
2586 break;
2587
2588 default:
2589 return E_INVALIDARG;
2590 }
2591
2592 return S_OK;
2593}
2594
2595STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2596{
2597 CheckComArgOutPointerValid(aSnapshotFolder);
2598
2599 AutoCaller autoCaller(this);
2600 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2601
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 Utf8Str strFullSnapshotFolder;
2605 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2606 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2607
2608 return S_OK;
2609}
2610
2611STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2612{
2613 /* @todo (r=dmik):
2614 * 1. Allow to change the name of the snapshot folder containing snapshots
2615 * 2. Rename the folder on disk instead of just changing the property
2616 * value (to be smart and not to leave garbage). Note that it cannot be
2617 * done here because the change may be rolled back. Thus, the right
2618 * place is #saveSettings().
2619 */
2620
2621 AutoCaller autoCaller(this);
2622 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2623
2624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2625
2626 HRESULT rc = checkStateDependency(MutableStateDep);
2627 if (FAILED(rc)) return rc;
2628
2629 if (!mData->mCurrentSnapshot.isNull())
2630 return setError(E_FAIL,
2631 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2632
2633 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2634
2635 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2636 if (strSnapshotFolder.isEmpty())
2637 strSnapshotFolder = "Snapshots";
2638 int vrc = calculateFullPath(strSnapshotFolder,
2639 strSnapshotFolder);
2640 if (RT_FAILURE(vrc))
2641 return setError(E_FAIL,
2642 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2643 aSnapshotFolder, vrc);
2644
2645 setModified(IsModified_MachineData);
2646 mUserData.backup();
2647
2648 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2649
2650 return S_OK;
2651}
2652
2653STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2654{
2655 CheckComArgOutSafeArrayPointerValid(aAttachments);
2656
2657 AutoCaller autoCaller(this);
2658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2659
2660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2663 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2664
2665 return S_OK;
2666}
2667
2668STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2669{
2670 CheckComArgOutPointerValid(vrdeServer);
2671
2672 AutoCaller autoCaller(this);
2673 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2674
2675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2676
2677 Assert(!!mVRDEServer);
2678 mVRDEServer.queryInterfaceTo(vrdeServer);
2679
2680 return S_OK;
2681}
2682
2683STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2684{
2685 CheckComArgOutPointerValid(audioAdapter);
2686
2687 AutoCaller autoCaller(this);
2688 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2689
2690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 mAudioAdapter.queryInterfaceTo(audioAdapter);
2693 return S_OK;
2694}
2695
2696STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2697{
2698#ifdef VBOX_WITH_VUSB
2699 CheckComArgOutPointerValid(aUSBControllers);
2700
2701 AutoCaller autoCaller(this);
2702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2703
2704 clearError();
2705 MultiResult rc(S_OK);
2706
2707# ifdef VBOX_WITH_USB
2708 rc = mParent->host()->i_checkUSBProxyService();
2709 if (FAILED(rc)) return rc;
2710# endif
2711
2712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2713
2714 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2715 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2716 return S_OK;
2717#else
2718 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2719 * extended error info to indicate that USB is simply not available
2720 * (w/o treating it as a failure), for example, as in OSE */
2721 NOREF(aUSBControllers);
2722 ReturnComNotImplemented();
2723#endif /* VBOX_WITH_VUSB */
2724}
2725
2726STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2727{
2728#ifdef VBOX_WITH_VUSB
2729 CheckComArgOutPointerValid(aUSBDeviceFilters);
2730
2731 AutoCaller autoCaller(this);
2732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2733
2734 clearError();
2735 MultiResult rc(S_OK);
2736
2737# ifdef VBOX_WITH_USB
2738 rc = mParent->host()->i_checkUSBProxyService();
2739 if (FAILED(rc)) return rc;
2740# endif
2741
2742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2743
2744 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2745#else
2746 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2747 * extended error info to indicate that USB is simply not available
2748 * (w/o treating it as a failure), for example, as in OSE */
2749 NOREF(aUSBDeviceFilters);
2750 ReturnComNotImplemented();
2751#endif /* VBOX_WITH_VUSB */
2752}
2753
2754STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2755{
2756 CheckComArgOutPointerValid(aFilePath);
2757
2758 AutoLimitedCaller autoCaller(this);
2759 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2760
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 mData->m_strConfigFileFull.cloneTo(aFilePath);
2764 return S_OK;
2765}
2766
2767STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2768{
2769 CheckComArgOutPointerValid(aModified);
2770
2771 AutoCaller autoCaller(this);
2772 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2773
2774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2775
2776 HRESULT rc = checkStateDependency(MutableStateDep);
2777 if (FAILED(rc)) return rc;
2778
2779 if (!mData->pMachineConfigFile->fileExists())
2780 // this is a new machine, and no config file exists yet:
2781 *aModified = TRUE;
2782 else
2783 *aModified = (mData->flModifications != 0);
2784
2785 return S_OK;
2786}
2787
2788STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2789{
2790 CheckComArgOutPointerValid(aSessionState);
2791
2792 AutoCaller autoCaller(this);
2793 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2794
2795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2796
2797 *aSessionState = mData->mSession.mState;
2798
2799 return S_OK;
2800}
2801
2802STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2803{
2804 CheckComArgOutPointerValid(aSessionType);
2805
2806 AutoCaller autoCaller(this);
2807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2808
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 mData->mSession.mType.cloneTo(aSessionType);
2812
2813 return S_OK;
2814}
2815
2816STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2817{
2818 CheckComArgOutPointerValid(aSessionPID);
2819
2820 AutoCaller autoCaller(this);
2821 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2822
2823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2824
2825 *aSessionPID = mData->mSession.mPID;
2826
2827 return S_OK;
2828}
2829
2830STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2831{
2832 CheckComArgOutPointerValid(machineState);
2833
2834 AutoCaller autoCaller(this);
2835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2836
2837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2838
2839 *machineState = mData->mMachineState;
2840
2841 return S_OK;
2842}
2843
2844STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2845{
2846 CheckComArgOutPointerValid(aLastStateChange);
2847
2848 AutoCaller autoCaller(this);
2849 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2850
2851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2854
2855 return S_OK;
2856}
2857
2858STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2859{
2860 CheckComArgOutPointerValid(aStateFilePath);
2861
2862 AutoCaller autoCaller(this);
2863 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2864
2865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2866
2867 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2868
2869 return S_OK;
2870}
2871
2872STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2873{
2874 CheckComArgOutPointerValid(aLogFolder);
2875
2876 AutoCaller autoCaller(this);
2877 AssertComRCReturnRC(autoCaller.rc());
2878
2879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 Utf8Str logFolder;
2882 getLogFolder(logFolder);
2883 logFolder.cloneTo(aLogFolder);
2884
2885 return S_OK;
2886}
2887
2888STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2889{
2890 CheckComArgOutPointerValid(aCurrentSnapshot);
2891
2892 AutoCaller autoCaller(this);
2893 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2894
2895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2896
2897 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2898
2899 return S_OK;
2900}
2901
2902STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2903{
2904 CheckComArgOutPointerValid(aSnapshotCount);
2905
2906 AutoCaller autoCaller(this);
2907 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2908
2909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2910
2911 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2912 ? 0
2913 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2914
2915 return S_OK;
2916}
2917
2918STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2919{
2920 CheckComArgOutPointerValid(aCurrentStateModified);
2921
2922 AutoCaller autoCaller(this);
2923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2924
2925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 /* Note: for machines with no snapshots, we always return FALSE
2928 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2929 * reasons :) */
2930
2931 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2932 ? FALSE
2933 : mData->mCurrentStateModified;
2934
2935 return S_OK;
2936}
2937
2938STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2939{
2940 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2941
2942 AutoCaller autoCaller(this);
2943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2944
2945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2946
2947 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2948 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2949
2950 return S_OK;
2951}
2952
2953STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2954{
2955 CheckComArgOutPointerValid(aClipboardMode);
2956
2957 AutoCaller autoCaller(this);
2958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2959
2960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2961
2962 *aClipboardMode = mHWData->mClipboardMode;
2963
2964 return S_OK;
2965}
2966
2967STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2968{
2969 HRESULT rc = S_OK;
2970
2971 AutoCaller autoCaller(this);
2972 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2973
2974 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2975
2976 alock.release();
2977 rc = onClipboardModeChange(aClipboardMode);
2978 alock.acquire();
2979 if (FAILED(rc)) return rc;
2980
2981 setModified(IsModified_MachineData);
2982 mHWData.backup();
2983 mHWData->mClipboardMode = aClipboardMode;
2984
2985 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2986 if (Global::IsOnline(mData->mMachineState))
2987 saveSettings(NULL);
2988
2989 return S_OK;
2990}
2991
2992STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2993{
2994 CheckComArgOutPointerValid(aDragAndDropMode);
2995
2996 AutoCaller autoCaller(this);
2997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2998
2999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3000
3001 *aDragAndDropMode = mHWData->mDragAndDropMode;
3002
3003 return S_OK;
3004}
3005
3006STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3007{
3008 HRESULT rc = S_OK;
3009
3010 AutoCaller autoCaller(this);
3011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3012
3013 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3014
3015 alock.release();
3016 rc = onDragAndDropModeChange(aDragAndDropMode);
3017 alock.acquire();
3018 if (FAILED(rc)) return rc;
3019
3020 setModified(IsModified_MachineData);
3021 mHWData.backup();
3022 mHWData->mDragAndDropMode = aDragAndDropMode;
3023
3024 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3025 if (Global::IsOnline(mData->mMachineState))
3026 saveSettings(NULL);
3027
3028 return S_OK;
3029}
3030
3031STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3032{
3033 CheckComArgOutPointerValid(aPatterns);
3034
3035 AutoCaller autoCaller(this);
3036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3037
3038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3039
3040 try
3041 {
3042 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3043 }
3044 catch (...)
3045 {
3046 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3047 }
3048
3049 return S_OK;
3050}
3051
3052STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3053{
3054 AutoCaller autoCaller(this);
3055 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3056
3057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3058
3059 HRESULT rc = checkStateDependency(MutableStateDep);
3060 if (FAILED(rc)) return rc;
3061
3062 setModified(IsModified_MachineData);
3063 mHWData.backup();
3064 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3065 return rc;
3066}
3067
3068STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3069{
3070 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3071
3072 AutoCaller autoCaller(this);
3073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3074
3075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3076
3077 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3078 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3079
3080 return S_OK;
3081}
3082
3083STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3084{
3085 CheckComArgOutPointerValid(aEnabled);
3086
3087 AutoCaller autoCaller(this);
3088 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3089
3090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3091
3092 *aEnabled = mUserData->s.fTeleporterEnabled;
3093
3094 return S_OK;
3095}
3096
3097STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3098{
3099 AutoCaller autoCaller(this);
3100 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3101
3102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3103
3104 /* Only allow it to be set to true when PoweredOff or Aborted.
3105 (Clearing it is always permitted.) */
3106 if ( aEnabled
3107 && mData->mRegistered
3108 && ( !isSessionMachine()
3109 || ( mData->mMachineState != MachineState_PoweredOff
3110 && mData->mMachineState != MachineState_Teleported
3111 && mData->mMachineState != MachineState_Aborted
3112 )
3113 )
3114 )
3115 return setError(VBOX_E_INVALID_VM_STATE,
3116 tr("The machine is not powered off (state is %s)"),
3117 Global::stringifyMachineState(mData->mMachineState));
3118
3119 setModified(IsModified_MachineData);
3120 mUserData.backup();
3121 mUserData->s.fTeleporterEnabled = !!aEnabled;
3122
3123 return S_OK;
3124}
3125
3126STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3127{
3128 CheckComArgOutPointerValid(aPort);
3129
3130 AutoCaller autoCaller(this);
3131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3132
3133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3134
3135 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3136
3137 return S_OK;
3138}
3139
3140STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3141{
3142 if (aPort >= _64K)
3143 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3144
3145 AutoCaller autoCaller(this);
3146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3147
3148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3149
3150 HRESULT rc = checkStateDependency(MutableStateDep);
3151 if (FAILED(rc)) return rc;
3152
3153 setModified(IsModified_MachineData);
3154 mUserData.backup();
3155 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3156
3157 return S_OK;
3158}
3159
3160STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3161{
3162 CheckComArgOutPointerValid(aAddress);
3163
3164 AutoCaller autoCaller(this);
3165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3166
3167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3168
3169 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3170
3171 return S_OK;
3172}
3173
3174STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3175{
3176 AutoCaller autoCaller(this);
3177 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3178
3179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3180
3181 HRESULT rc = checkStateDependency(MutableStateDep);
3182 if (FAILED(rc)) return rc;
3183
3184 setModified(IsModified_MachineData);
3185 mUserData.backup();
3186 mUserData->s.strTeleporterAddress = aAddress;
3187
3188 return S_OK;
3189}
3190
3191STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3192{
3193 CheckComArgOutPointerValid(aPassword);
3194
3195 AutoCaller autoCaller(this);
3196 HRESULT hrc = autoCaller.rc();
3197 if (SUCCEEDED(hrc))
3198 {
3199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3200 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3201 }
3202
3203 return hrc;
3204}
3205
3206STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3207{
3208 /*
3209 * Hash the password first.
3210 */
3211 Utf8Str strPassword(aPassword);
3212 if (!strPassword.isEmpty())
3213 {
3214 if (VBoxIsPasswordHashed(&strPassword))
3215 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3216 VBoxHashPassword(&strPassword);
3217 }
3218
3219 /*
3220 * Do the update.
3221 */
3222 AutoCaller autoCaller(this);
3223 HRESULT hrc = autoCaller.rc();
3224 if (SUCCEEDED(hrc))
3225 {
3226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3227 hrc = checkStateDependency(MutableStateDep);
3228 if (SUCCEEDED(hrc))
3229 {
3230 setModified(IsModified_MachineData);
3231 mUserData.backup();
3232 mUserData->s.strTeleporterPassword = strPassword;
3233 }
3234 }
3235
3236 return hrc;
3237}
3238
3239STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3240{
3241 CheckComArgOutPointerValid(aState);
3242
3243 AutoCaller autoCaller(this);
3244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3245
3246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3247
3248 *aState = mUserData->s.enmFaultToleranceState;
3249 return S_OK;
3250}
3251
3252STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3253{
3254 AutoCaller autoCaller(this);
3255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3256
3257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3258
3259 /* @todo deal with running state change. */
3260 HRESULT rc = checkStateDependency(MutableStateDep);
3261 if (FAILED(rc)) return rc;
3262
3263 setModified(IsModified_MachineData);
3264 mUserData.backup();
3265 mUserData->s.enmFaultToleranceState = aState;
3266 return S_OK;
3267}
3268
3269STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3270{
3271 CheckComArgOutPointerValid(aAddress);
3272
3273 AutoCaller autoCaller(this);
3274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3275
3276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3277
3278 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3279 return S_OK;
3280}
3281
3282STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3283{
3284 AutoCaller autoCaller(this);
3285 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3286
3287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3288
3289 /* @todo deal with running state change. */
3290 HRESULT rc = checkStateDependency(MutableStateDep);
3291 if (FAILED(rc)) return rc;
3292
3293 setModified(IsModified_MachineData);
3294 mUserData.backup();
3295 mUserData->s.strFaultToleranceAddress = aAddress;
3296 return S_OK;
3297}
3298
3299STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3300{
3301 CheckComArgOutPointerValid(aPort);
3302
3303 AutoCaller autoCaller(this);
3304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3305
3306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3307
3308 *aPort = mUserData->s.uFaultTolerancePort;
3309 return S_OK;
3310}
3311
3312STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3313{
3314 AutoCaller autoCaller(this);
3315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3316
3317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3318
3319 /* @todo deal with running state change. */
3320 HRESULT rc = checkStateDependency(MutableStateDep);
3321 if (FAILED(rc)) return rc;
3322
3323 setModified(IsModified_MachineData);
3324 mUserData.backup();
3325 mUserData->s.uFaultTolerancePort = aPort;
3326 return S_OK;
3327}
3328
3329STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3330{
3331 CheckComArgOutPointerValid(aPassword);
3332
3333 AutoCaller autoCaller(this);
3334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3335
3336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3337
3338 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3339
3340 return S_OK;
3341}
3342
3343STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3344{
3345 AutoCaller autoCaller(this);
3346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3347
3348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3349
3350 /* @todo deal with running state change. */
3351 HRESULT rc = checkStateDependency(MutableStateDep);
3352 if (FAILED(rc)) return rc;
3353
3354 setModified(IsModified_MachineData);
3355 mUserData.backup();
3356 mUserData->s.strFaultTolerancePassword = aPassword;
3357
3358 return S_OK;
3359}
3360
3361STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3362{
3363 CheckComArgOutPointerValid(aInterval);
3364
3365 AutoCaller autoCaller(this);
3366 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3367
3368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3369
3370 *aInterval = mUserData->s.uFaultToleranceInterval;
3371 return S_OK;
3372}
3373
3374STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3375{
3376 AutoCaller autoCaller(this);
3377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3378
3379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3380
3381 /* @todo deal with running state change. */
3382 HRESULT rc = checkStateDependency(MutableStateDep);
3383 if (FAILED(rc)) return rc;
3384
3385 setModified(IsModified_MachineData);
3386 mUserData.backup();
3387 mUserData->s.uFaultToleranceInterval = aInterval;
3388 return S_OK;
3389}
3390
3391STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3392{
3393 CheckComArgOutPointerValid(aEnabled);
3394
3395 AutoCaller autoCaller(this);
3396 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3397
3398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3399
3400 *aEnabled = mUserData->s.fRTCUseUTC;
3401
3402 return S_OK;
3403}
3404
3405STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3406{
3407 AutoCaller autoCaller(this);
3408 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3409
3410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3411
3412 /* Only allow it to be set to true when PoweredOff or Aborted.
3413 (Clearing it is always permitted.) */
3414 if ( aEnabled
3415 && mData->mRegistered
3416 && ( !isSessionMachine()
3417 || ( mData->mMachineState != MachineState_PoweredOff
3418 && mData->mMachineState != MachineState_Teleported
3419 && mData->mMachineState != MachineState_Aborted
3420 )
3421 )
3422 )
3423 return setError(VBOX_E_INVALID_VM_STATE,
3424 tr("The machine is not powered off (state is %s)"),
3425 Global::stringifyMachineState(mData->mMachineState));
3426
3427 setModified(IsModified_MachineData);
3428 mUserData.backup();
3429 mUserData->s.fRTCUseUTC = !!aEnabled;
3430
3431 return S_OK;
3432}
3433
3434STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3435{
3436 CheckComArgOutPointerValid(aEnabled);
3437
3438 AutoCaller autoCaller(this);
3439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3440
3441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3442
3443 *aEnabled = mHWData->mIOCacheEnabled;
3444
3445 return S_OK;
3446}
3447
3448STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3449{
3450 AutoCaller autoCaller(this);
3451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3452
3453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3454
3455 HRESULT rc = checkStateDependency(MutableStateDep);
3456 if (FAILED(rc)) return rc;
3457
3458 setModified(IsModified_MachineData);
3459 mHWData.backup();
3460 mHWData->mIOCacheEnabled = aEnabled;
3461
3462 return S_OK;
3463}
3464
3465STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3466{
3467 CheckComArgOutPointerValid(aIOCacheSize);
3468
3469 AutoCaller autoCaller(this);
3470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3471
3472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3473
3474 *aIOCacheSize = mHWData->mIOCacheSize;
3475
3476 return S_OK;
3477}
3478
3479STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3480{
3481 AutoCaller autoCaller(this);
3482 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3483
3484 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3485
3486 HRESULT rc = checkStateDependency(MutableStateDep);
3487 if (FAILED(rc)) return rc;
3488
3489 setModified(IsModified_MachineData);
3490 mHWData.backup();
3491 mHWData->mIOCacheSize = aIOCacheSize;
3492
3493 return S_OK;
3494}
3495
3496
3497/**
3498 * @note Locks objects!
3499 */
3500STDMETHODIMP Machine::LockMachine(ISession *aSession,
3501 LockType_T lockType)
3502{
3503 CheckComArgNotNull(aSession);
3504
3505 AutoCaller autoCaller(this);
3506 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3507
3508 /* check the session state */
3509 SessionState_T state;
3510 HRESULT rc = aSession->COMGETTER(State)(&state);
3511 if (FAILED(rc)) return rc;
3512
3513 if (state != SessionState_Unlocked)
3514 return setError(VBOX_E_INVALID_OBJECT_STATE,
3515 tr("The given session is busy"));
3516
3517 // get the client's IInternalSessionControl interface
3518 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3519 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3520 E_INVALIDARG);
3521
3522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3523
3524 if (!mData->mRegistered)
3525 return setError(E_UNEXPECTED,
3526 tr("The machine '%s' is not registered"),
3527 mUserData->s.strName.c_str());
3528
3529 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3530
3531 SessionState_T oldState = mData->mSession.mState;
3532 /* Hack: in case the session is closing and there is a progress object
3533 * which allows waiting for the session to be closed, take the opportunity
3534 * and do a limited wait (max. 1 second). This helps a lot when the system
3535 * is busy and thus session closing can take a little while. */
3536 if ( mData->mSession.mState == SessionState_Unlocking
3537 && mData->mSession.mProgress)
3538 {
3539 alock.release();
3540 mData->mSession.mProgress->WaitForCompletion(1000);
3541 alock.acquire();
3542 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3543 }
3544
3545 // try again now
3546 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3547 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3548 )
3549 {
3550 // OK, share the session... we are now dealing with three processes:
3551 // 1) VBoxSVC (where this code runs);
3552 // 2) process C: the caller's client process (who wants a shared session);
3553 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3554
3555 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3556 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3557 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3558 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3559 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3560
3561 /*
3562 * Release the lock before calling the client process. It's safe here
3563 * since the only thing to do after we get the lock again is to add
3564 * the remote control to the list (which doesn't directly influence
3565 * anything).
3566 */
3567 alock.release();
3568
3569 // get the console of the session holding the write lock (this is a remote call)
3570 ComPtr<IConsole> pConsoleW;
3571 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3572 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3573 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3574 if (FAILED(rc))
3575 // the failure may occur w/o any error info (from RPC), so provide one
3576 return setError(VBOX_E_VM_ERROR,
3577 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3578
3579 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3580
3581 // share the session machine and W's console with the caller's session
3582 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3583 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3584 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3585
3586 if (FAILED(rc))
3587 // the failure may occur w/o any error info (from RPC), so provide one
3588 return setError(VBOX_E_VM_ERROR,
3589 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3590 alock.acquire();
3591
3592 // need to revalidate the state after acquiring the lock again
3593 if (mData->mSession.mState != SessionState_Locked)
3594 {
3595 pSessionControl->Uninitialize();
3596 return setError(VBOX_E_INVALID_SESSION_STATE,
3597 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3598 mUserData->s.strName.c_str());
3599 }
3600
3601 // add the caller's session to the list
3602 mData->mSession.mRemoteControls.push_back(pSessionControl);
3603 }
3604 else if ( mData->mSession.mState == SessionState_Locked
3605 || mData->mSession.mState == SessionState_Unlocking
3606 )
3607 {
3608 // sharing not permitted, or machine still unlocking:
3609 return setError(VBOX_E_INVALID_OBJECT_STATE,
3610 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3611 mUserData->s.strName.c_str());
3612 }
3613 else
3614 {
3615 // machine is not locked: then write-lock the machine (create the session machine)
3616
3617 // must not be busy
3618 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3619
3620 // get the caller's session PID
3621 RTPROCESS pid = NIL_RTPROCESS;
3622 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3623 pSessionControl->GetPID((ULONG*)&pid);
3624 Assert(pid != NIL_RTPROCESS);
3625
3626 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3627
3628 if (fLaunchingVMProcess)
3629 {
3630 if (mData->mSession.mPID == NIL_RTPROCESS)
3631 {
3632 // two or more clients racing for a lock, the one which set the
3633 // session state to Spawning will win, the others will get an
3634 // error as we can't decide here if waiting a little would help
3635 // (only for shared locks this would avoid an error)
3636 return setError(VBOX_E_INVALID_OBJECT_STATE,
3637 tr("The machine '%s' already has a lock request pending"),
3638 mUserData->s.strName.c_str());
3639 }
3640
3641 // this machine is awaiting for a spawning session to be opened:
3642 // then the calling process must be the one that got started by
3643 // LaunchVMProcess()
3644
3645 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3646 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3647
3648 if (mData->mSession.mPID != pid)
3649 return setError(E_ACCESSDENIED,
3650 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3651 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3652 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3653 }
3654
3655 // create the mutable SessionMachine from the current machine
3656 ComObjPtr<SessionMachine> sessionMachine;
3657 sessionMachine.createObject();
3658 rc = sessionMachine->init(this);
3659 AssertComRC(rc);
3660
3661 /* NOTE: doing return from this function after this point but
3662 * before the end is forbidden since it may call SessionMachine::uninit()
3663 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3664 * lock while still holding the Machine lock in alock so that a deadlock
3665 * is possible due to the wrong lock order. */
3666
3667 if (SUCCEEDED(rc))
3668 {
3669 /*
3670 * Set the session state to Spawning to protect against subsequent
3671 * attempts to open a session and to unregister the machine after
3672 * we release the lock.
3673 */
3674 SessionState_T origState = mData->mSession.mState;
3675 mData->mSession.mState = SessionState_Spawning;
3676
3677#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3678 /* Get the client token ID to be passed to the client process */
3679 Utf8Str strTokenId;
3680 sessionMachine->getTokenId(strTokenId);
3681 Assert(!strTokenId.isEmpty());
3682#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3683 /* Get the client token to be passed to the client process */
3684 ComPtr<IToken> pToken(sessionMachine->getToken());
3685 /* The token is now "owned" by pToken, fix refcount */
3686 if (!pToken.isNull())
3687 pToken->Release();
3688#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3689
3690 /*
3691 * Release the lock before calling the client process -- it will call
3692 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3693 * because the state is Spawning, so that LaunchVMProcess() and
3694 * LockMachine() calls will fail. This method, called before we
3695 * acquire the lock again, will fail because of the wrong PID.
3696 *
3697 * Note that mData->mSession.mRemoteControls accessed outside
3698 * the lock may not be modified when state is Spawning, so it's safe.
3699 */
3700 alock.release();
3701
3702 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3703#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3704 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3705#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3706 rc = pSessionControl->AssignMachine(sessionMachine, lockType, pToken);
3707 /* Now the token is owned by the client process. */
3708 pToken.setNull();
3709#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3710 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3711
3712 /* The failure may occur w/o any error info (from RPC), so provide one */
3713 if (FAILED(rc))
3714 setError(VBOX_E_VM_ERROR,
3715 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3716
3717 if ( SUCCEEDED(rc)
3718 && fLaunchingVMProcess
3719 )
3720 {
3721 /* complete the remote session initialization */
3722
3723 /* get the console from the direct session */
3724 ComPtr<IConsole> console;
3725 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3726 ComAssertComRC(rc);
3727
3728 if (SUCCEEDED(rc) && !console)
3729 {
3730 ComAssert(!!console);
3731 rc = E_FAIL;
3732 }
3733
3734 /* assign machine & console to the remote session */
3735 if (SUCCEEDED(rc))
3736 {
3737 /*
3738 * after LaunchVMProcess(), the first and the only
3739 * entry in remoteControls is that remote session
3740 */
3741 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3742 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3743 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3744
3745 /* The failure may occur w/o any error info (from RPC), so provide one */
3746 if (FAILED(rc))
3747 setError(VBOX_E_VM_ERROR,
3748 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3749 }
3750
3751 if (FAILED(rc))
3752 pSessionControl->Uninitialize();
3753 }
3754
3755 /* acquire the lock again */
3756 alock.acquire();
3757
3758 /* Restore the session state */
3759 mData->mSession.mState = origState;
3760 }
3761
3762 // finalize spawning anyway (this is why we don't return on errors above)
3763 if (fLaunchingVMProcess)
3764 {
3765 /* Note that the progress object is finalized later */
3766 /** @todo Consider checking mData->mSession.mProgress for cancellation
3767 * around here. */
3768
3769 /* We don't reset mSession.mPID here because it is necessary for
3770 * SessionMachine::uninit() to reap the child process later. */
3771
3772 if (FAILED(rc))
3773 {
3774 /* Close the remote session, remove the remote control from the list
3775 * and reset session state to Closed (@note keep the code in sync
3776 * with the relevant part in checkForSpawnFailure()). */
3777
3778 Assert(mData->mSession.mRemoteControls.size() == 1);
3779 if (mData->mSession.mRemoteControls.size() == 1)
3780 {
3781 ErrorInfoKeeper eik;
3782 mData->mSession.mRemoteControls.front()->Uninitialize();
3783 }
3784
3785 mData->mSession.mRemoteControls.clear();
3786 mData->mSession.mState = SessionState_Unlocked;
3787 }
3788 }
3789 else
3790 {
3791 /* memorize PID of the directly opened session */
3792 if (SUCCEEDED(rc))
3793 mData->mSession.mPID = pid;
3794 }
3795
3796 if (SUCCEEDED(rc))
3797 {
3798 /* memorize the direct session control and cache IUnknown for it */
3799 mData->mSession.mDirectControl = pSessionControl;
3800 mData->mSession.mState = SessionState_Locked;
3801 /* associate the SessionMachine with this Machine */
3802 mData->mSession.mMachine = sessionMachine;
3803
3804 /* request an IUnknown pointer early from the remote party for later
3805 * identity checks (it will be internally cached within mDirectControl
3806 * at least on XPCOM) */
3807 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3808 NOREF(unk);
3809 }
3810
3811 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3812 * would break the lock order */
3813 alock.release();
3814
3815 /* uninitialize the created session machine on failure */
3816 if (FAILED(rc))
3817 sessionMachine->uninit();
3818
3819 }
3820
3821 if (SUCCEEDED(rc))
3822 {
3823 /*
3824 * tell the client watcher thread to update the set of
3825 * machines that have open sessions
3826 */
3827 mParent->updateClientWatcher();
3828
3829 if (oldState != SessionState_Locked)
3830 /* fire an event */
3831 mParent->onSessionStateChange(getId(), SessionState_Locked);
3832 }
3833
3834 return rc;
3835}
3836
3837/**
3838 * @note Locks objects!
3839 */
3840STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3841 IN_BSTR aFrontend,
3842 IN_BSTR aEnvironment,
3843 IProgress **aProgress)
3844{
3845 CheckComArgStr(aFrontend);
3846 Utf8Str strFrontend(aFrontend);
3847 Utf8Str strEnvironment(aEnvironment);
3848 /* "emergencystop" doesn't need the session, so skip the checks/interface
3849 * retrieval. This code doesn't quite fit in here, but introducing a
3850 * special API method would be even more effort, and would require explicit
3851 * support by every API client. It's better to hide the feature a bit. */
3852 if (strFrontend != "emergencystop")
3853 CheckComArgNotNull(aSession);
3854 CheckComArgOutPointerValid(aProgress);
3855
3856 AutoCaller autoCaller(this);
3857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3858
3859 HRESULT rc = S_OK;
3860 if (strFrontend.isEmpty())
3861 {
3862 Bstr bstrFrontend;
3863 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3864 if (FAILED(rc))
3865 return rc;
3866 strFrontend = bstrFrontend;
3867 if (strFrontend.isEmpty())
3868 {
3869 ComPtr<ISystemProperties> systemProperties;
3870 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3871 if (FAILED(rc))
3872 return rc;
3873 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3874 if (FAILED(rc))
3875 return rc;
3876 strFrontend = bstrFrontend;
3877 }
3878 /* paranoia - emergencystop is not a valid default */
3879 if (strFrontend == "emergencystop")
3880 strFrontend = Utf8Str::Empty;
3881 }
3882 /* default frontend: Qt GUI */
3883 if (strFrontend.isEmpty())
3884 strFrontend = "GUI/Qt";
3885
3886 if (strFrontend != "emergencystop")
3887 {
3888 /* check the session state */
3889 SessionState_T state;
3890 rc = aSession->COMGETTER(State)(&state);
3891 if (FAILED(rc))
3892 return rc;
3893
3894 if (state != SessionState_Unlocked)
3895 return setError(VBOX_E_INVALID_OBJECT_STATE,
3896 tr("The given session is busy"));
3897
3898 /* get the IInternalSessionControl interface */
3899 ComPtr<IInternalSessionControl> control(aSession);
3900 ComAssertMsgRet(!control.isNull(),
3901 ("No IInternalSessionControl interface"),
3902 E_INVALIDARG);
3903
3904 /* get the teleporter enable state for the progress object init. */
3905 BOOL fTeleporterEnabled;
3906 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3907 if (FAILED(rc))
3908 return rc;
3909
3910 /* create a progress object */
3911 ComObjPtr<ProgressProxy> progress;
3912 progress.createObject();
3913 rc = progress->init(mParent,
3914 static_cast<IMachine*>(this),
3915 Bstr(tr("Starting VM")).raw(),
3916 TRUE /* aCancelable */,
3917 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3918 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3919 2 /* uFirstOperationWeight */,
3920 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3921
3922 if (SUCCEEDED(rc))
3923 {
3924 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3925 if (SUCCEEDED(rc))
3926 {
3927 progress.queryInterfaceTo(aProgress);
3928
3929 /* signal the client watcher thread */
3930 mParent->updateClientWatcher();
3931
3932 /* fire an event */
3933 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3934 }
3935 }
3936 }
3937 else
3938 {
3939 /* no progress object - either instant success or failure */
3940 *aProgress = NULL;
3941
3942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3943
3944 if (mData->mSession.mState != SessionState_Locked)
3945 return setError(VBOX_E_INVALID_OBJECT_STATE,
3946 tr("The machine '%s' is not locked by a session"),
3947 mUserData->s.strName.c_str());
3948
3949 /* must have a VM process associated - do not kill normal API clients
3950 * with an open session */
3951 if (!Global::IsOnline(mData->mMachineState))
3952 return setError(VBOX_E_INVALID_OBJECT_STATE,
3953 tr("The machine '%s' does not have a VM process"),
3954 mUserData->s.strName.c_str());
3955
3956 /* forcibly terminate the VM process */
3957 if (mData->mSession.mPID != NIL_RTPROCESS)
3958 RTProcTerminate(mData->mSession.mPID);
3959
3960 /* signal the client watcher thread, as most likely the client has
3961 * been terminated */
3962 mParent->updateClientWatcher();
3963 }
3964
3965 return rc;
3966}
3967
3968STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3969{
3970 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3971 return setError(E_INVALIDARG,
3972 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3973 aPosition, SchemaDefs::MaxBootPosition);
3974
3975 if (aDevice == DeviceType_USB)
3976 return setError(E_NOTIMPL,
3977 tr("Booting from USB device is currently not supported"));
3978
3979 AutoCaller autoCaller(this);
3980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3981
3982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3983
3984 HRESULT rc = checkStateDependency(MutableStateDep);
3985 if (FAILED(rc)) return rc;
3986
3987 setModified(IsModified_MachineData);
3988 mHWData.backup();
3989 mHWData->mBootOrder[aPosition - 1] = aDevice;
3990
3991 return S_OK;
3992}
3993
3994STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3995{
3996 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3997 return setError(E_INVALIDARG,
3998 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3999 aPosition, SchemaDefs::MaxBootPosition);
4000
4001 AutoCaller autoCaller(this);
4002 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4003
4004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4005
4006 *aDevice = mHWData->mBootOrder[aPosition - 1];
4007
4008 return S_OK;
4009}
4010
4011STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4012 LONG aControllerPort,
4013 LONG aDevice,
4014 DeviceType_T aType,
4015 IMedium *aMedium)
4016{
4017 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4018 aControllerName, aControllerPort, aDevice, aType, aMedium));
4019
4020 CheckComArgStrNotEmptyOrNull(aControllerName);
4021
4022 AutoCaller autoCaller(this);
4023 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4024
4025 // request the host lock first, since might be calling Host methods for getting host drives;
4026 // next, protect the media tree all the while we're in here, as well as our member variables
4027 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4028 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4029
4030 HRESULT rc = checkStateDependency(MutableStateDep);
4031 if (FAILED(rc)) return rc;
4032
4033 /// @todo NEWMEDIA implicit machine registration
4034 if (!mData->mRegistered)
4035 return setError(VBOX_E_INVALID_OBJECT_STATE,
4036 tr("Cannot attach storage devices to an unregistered machine"));
4037
4038 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4039
4040 /* Check for an existing controller. */
4041 ComObjPtr<StorageController> ctl;
4042 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4043 if (FAILED(rc)) return rc;
4044
4045 StorageControllerType_T ctrlType;
4046 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4047 if (FAILED(rc))
4048 return setError(E_FAIL,
4049 tr("Could not get type of controller '%ls'"),
4050 aControllerName);
4051
4052 bool fSilent = false;
4053 Utf8Str strReconfig;
4054
4055 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4056 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4057 if ( mData->mMachineState == MachineState_Paused
4058 && strReconfig == "1")
4059 fSilent = true;
4060
4061 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4062 bool fHotplug = false;
4063 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4064 fHotplug = true;
4065
4066 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4067 return setError(VBOX_E_INVALID_VM_STATE,
4068 tr("Controller '%ls' does not support hotplugging"),
4069 aControllerName);
4070
4071 // check that the port and device are not out of range
4072 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
4073 if (FAILED(rc)) return rc;
4074
4075 /* check if the device slot is already busy */
4076 MediumAttachment *pAttachTemp;
4077 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4078 aControllerName,
4079 aControllerPort,
4080 aDevice)))
4081 {
4082 Medium *pMedium = pAttachTemp->getMedium();
4083 if (pMedium)
4084 {
4085 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4086 return setError(VBOX_E_OBJECT_IN_USE,
4087 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4088 pMedium->getLocationFull().c_str(),
4089 aControllerPort,
4090 aDevice,
4091 aControllerName);
4092 }
4093 else
4094 return setError(VBOX_E_OBJECT_IN_USE,
4095 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4096 aControllerPort, aDevice, aControllerName);
4097 }
4098
4099 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4100 if (aMedium && medium.isNull())
4101 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4102
4103 AutoCaller mediumCaller(medium);
4104 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4105
4106 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4107
4108 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4109 && !medium.isNull()
4110 )
4111 return setError(VBOX_E_OBJECT_IN_USE,
4112 tr("Medium '%s' is already attached to this virtual machine"),
4113 medium->getLocationFull().c_str());
4114
4115 if (!medium.isNull())
4116 {
4117 MediumType_T mtype = medium->getType();
4118 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4119 // For DVDs it's not written to the config file, so needs no global config
4120 // version bump. For floppies it's a new attribute "type", which is ignored
4121 // by older VirtualBox version, so needs no global config version bump either.
4122 // For hard disks this type is not accepted.
4123 if (mtype == MediumType_MultiAttach)
4124 {
4125 // This type is new with VirtualBox 4.0 and therefore requires settings
4126 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4127 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4128 // two reasons: The medium type is a property of the media registry tree, which
4129 // can reside in the global config file (for pre-4.0 media); we would therefore
4130 // possibly need to bump the global config version. We don't want to do that though
4131 // because that might make downgrading to pre-4.0 impossible.
4132 // As a result, we can only use these two new types if the medium is NOT in the
4133 // global registry:
4134 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4135 if ( medium->isInRegistry(uuidGlobalRegistry)
4136 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4137 )
4138 return setError(VBOX_E_INVALID_OBJECT_STATE,
4139 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4140 "to machines that were created with VirtualBox 4.0 or later"),
4141 medium->getLocationFull().c_str());
4142 }
4143 }
4144
4145 bool fIndirect = false;
4146 if (!medium.isNull())
4147 fIndirect = medium->isReadOnly();
4148 bool associate = true;
4149
4150 do
4151 {
4152 if ( aType == DeviceType_HardDisk
4153 && mMediaData.isBackedUp())
4154 {
4155 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4156
4157 /* check if the medium was attached to the VM before we started
4158 * changing attachments in which case the attachment just needs to
4159 * be restored */
4160 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4161 {
4162 AssertReturn(!fIndirect, E_FAIL);
4163
4164 /* see if it's the same bus/channel/device */
4165 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4166 {
4167 /* the simplest case: restore the whole attachment
4168 * and return, nothing else to do */
4169 mMediaData->mAttachments.push_back(pAttachTemp);
4170
4171 /* Reattach the medium to the VM. */
4172 if (fHotplug || fSilent)
4173 {
4174 mediumLock.release();
4175 treeLock.release();
4176 alock.release();
4177
4178 MediumLockList *pMediumLockList(new MediumLockList());
4179
4180 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4181 true /* fMediumLockWrite */,
4182 NULL,
4183 *pMediumLockList);
4184 alock.acquire();
4185 if (FAILED(rc))
4186 delete pMediumLockList;
4187 else
4188 {
4189 mData->mSession.mLockedMedia.Unlock();
4190 alock.release();
4191 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4192 mData->mSession.mLockedMedia.Lock();
4193 alock.acquire();
4194 }
4195 alock.release();
4196
4197 if (SUCCEEDED(rc))
4198 {
4199 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4200 /* Remove lock list in case of error. */
4201 if (FAILED(rc))
4202 {
4203 mData->mSession.mLockedMedia.Unlock();
4204 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4205 mData->mSession.mLockedMedia.Lock();
4206 }
4207 }
4208 }
4209
4210 return S_OK;
4211 }
4212
4213 /* bus/channel/device differ; we need a new attachment object,
4214 * but don't try to associate it again */
4215 associate = false;
4216 break;
4217 }
4218 }
4219
4220 /* go further only if the attachment is to be indirect */
4221 if (!fIndirect)
4222 break;
4223
4224 /* perform the so called smart attachment logic for indirect
4225 * attachments. Note that smart attachment is only applicable to base
4226 * hard disks. */
4227
4228 if (medium->getParent().isNull())
4229 {
4230 /* first, investigate the backup copy of the current hard disk
4231 * attachments to make it possible to re-attach existing diffs to
4232 * another device slot w/o losing their contents */
4233 if (mMediaData.isBackedUp())
4234 {
4235 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4236
4237 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4238 uint32_t foundLevel = 0;
4239
4240 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4241 it != oldAtts.end();
4242 ++it)
4243 {
4244 uint32_t level = 0;
4245 MediumAttachment *pAttach = *it;
4246 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4247 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4248 if (pMedium.isNull())
4249 continue;
4250
4251 if (pMedium->getBase(&level) == medium)
4252 {
4253 /* skip the hard disk if its currently attached (we
4254 * cannot attach the same hard disk twice) */
4255 if (findAttachment(mMediaData->mAttachments,
4256 pMedium))
4257 continue;
4258
4259 /* matched device, channel and bus (i.e. attached to the
4260 * same place) will win and immediately stop the search;
4261 * otherwise the attachment that has the youngest
4262 * descendant of medium will be used
4263 */
4264 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4265 {
4266 /* the simplest case: restore the whole attachment
4267 * and return, nothing else to do */
4268 mMediaData->mAttachments.push_back(*it);
4269
4270 /* Reattach the medium to the VM. */
4271 if (fHotplug || fSilent)
4272 {
4273 mediumLock.release();
4274 treeLock.release();
4275 alock.release();
4276
4277 MediumLockList *pMediumLockList(new MediumLockList());
4278
4279 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4280 true /* fMediumLockWrite */,
4281 NULL,
4282 *pMediumLockList);
4283 alock.acquire();
4284 if (FAILED(rc))
4285 delete pMediumLockList;
4286 else
4287 {
4288 mData->mSession.mLockedMedia.Unlock();
4289 alock.release();
4290 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4291 mData->mSession.mLockedMedia.Lock();
4292 alock.acquire();
4293 }
4294 alock.release();
4295
4296 if (SUCCEEDED(rc))
4297 {
4298 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4299 /* Remove lock list in case of error. */
4300 if (FAILED(rc))
4301 {
4302 mData->mSession.mLockedMedia.Unlock();
4303 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4304 mData->mSession.mLockedMedia.Lock();
4305 }
4306 }
4307 }
4308
4309 return S_OK;
4310 }
4311 else if ( foundIt == oldAtts.end()
4312 || level > foundLevel /* prefer younger */
4313 )
4314 {
4315 foundIt = it;
4316 foundLevel = level;
4317 }
4318 }
4319 }
4320
4321 if (foundIt != oldAtts.end())
4322 {
4323 /* use the previously attached hard disk */
4324 medium = (*foundIt)->getMedium();
4325 mediumCaller.attach(medium);
4326 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4327 mediumLock.attach(medium);
4328 /* not implicit, doesn't require association with this VM */
4329 fIndirect = false;
4330 associate = false;
4331 /* go right to the MediumAttachment creation */
4332 break;
4333 }
4334 }
4335
4336 /* must give up the medium lock and medium tree lock as below we
4337 * go over snapshots, which needs a lock with higher lock order. */
4338 mediumLock.release();
4339 treeLock.release();
4340
4341 /* then, search through snapshots for the best diff in the given
4342 * hard disk's chain to base the new diff on */
4343
4344 ComObjPtr<Medium> base;
4345 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4346 while (snap)
4347 {
4348 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4349
4350 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4351
4352 MediumAttachment *pAttachFound = NULL;
4353 uint32_t foundLevel = 0;
4354
4355 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4356 it != snapAtts.end();
4357 ++it)
4358 {
4359 MediumAttachment *pAttach = *it;
4360 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4361 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4362 if (pMedium.isNull())
4363 continue;
4364
4365 uint32_t level = 0;
4366 if (pMedium->getBase(&level) == medium)
4367 {
4368 /* matched device, channel and bus (i.e. attached to the
4369 * same place) will win and immediately stop the search;
4370 * otherwise the attachment that has the youngest
4371 * descendant of medium will be used
4372 */
4373 if ( pAttach->getDevice() == aDevice
4374 && pAttach->getPort() == aControllerPort
4375 && pAttach->getControllerName() == aControllerName
4376 )
4377 {
4378 pAttachFound = pAttach;
4379 break;
4380 }
4381 else if ( !pAttachFound
4382 || level > foundLevel /* prefer younger */
4383 )
4384 {
4385 pAttachFound = pAttach;
4386 foundLevel = level;
4387 }
4388 }
4389 }
4390
4391 if (pAttachFound)
4392 {
4393 base = pAttachFound->getMedium();
4394 break;
4395 }
4396
4397 snap = snap->getParent();
4398 }
4399
4400 /* re-lock medium tree and the medium, as we need it below */
4401 treeLock.acquire();
4402 mediumLock.acquire();
4403
4404 /* found a suitable diff, use it as a base */
4405 if (!base.isNull())
4406 {
4407 medium = base;
4408 mediumCaller.attach(medium);
4409 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4410 mediumLock.attach(medium);
4411 }
4412 }
4413
4414 Utf8Str strFullSnapshotFolder;
4415 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4416
4417 ComObjPtr<Medium> diff;
4418 diff.createObject();
4419 // store this diff in the same registry as the parent
4420 Guid uuidRegistryParent;
4421 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4422 {
4423 // parent image has no registry: this can happen if we're attaching a new immutable
4424 // image that has not yet been attached (medium then points to the base and we're
4425 // creating the diff image for the immutable, and the parent is not yet registered);
4426 // put the parent in the machine registry then
4427 mediumLock.release();
4428 treeLock.release();
4429 alock.release();
4430 addMediumToRegistry(medium);
4431 alock.acquire();
4432 treeLock.acquire();
4433 mediumLock.acquire();
4434 medium->getFirstRegistryMachineId(uuidRegistryParent);
4435 }
4436 rc = diff->init(mParent,
4437 medium->getPreferredDiffFormat(),
4438 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4439 uuidRegistryParent);
4440 if (FAILED(rc)) return rc;
4441
4442 /* Apply the normal locking logic to the entire chain. */
4443 MediumLockList *pMediumLockList(new MediumLockList());
4444 mediumLock.release();
4445 treeLock.release();
4446 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4447 true /* fMediumLockWrite */,
4448 medium,
4449 *pMediumLockList);
4450 treeLock.acquire();
4451 mediumLock.acquire();
4452 if (SUCCEEDED(rc))
4453 {
4454 mediumLock.release();
4455 treeLock.release();
4456 rc = pMediumLockList->Lock();
4457 treeLock.acquire();
4458 mediumLock.acquire();
4459 if (FAILED(rc))
4460 setError(rc,
4461 tr("Could not lock medium when creating diff '%s'"),
4462 diff->getLocationFull().c_str());
4463 else
4464 {
4465 /* will release the lock before the potentially lengthy
4466 * operation, so protect with the special state */
4467 MachineState_T oldState = mData->mMachineState;
4468 setMachineState(MachineState_SettingUp);
4469
4470 mediumLock.release();
4471 treeLock.release();
4472 alock.release();
4473
4474 rc = medium->createDiffStorage(diff,
4475 MediumVariant_Standard,
4476 pMediumLockList,
4477 NULL /* aProgress */,
4478 true /* aWait */);
4479
4480 alock.acquire();
4481 treeLock.acquire();
4482 mediumLock.acquire();
4483
4484 setMachineState(oldState);
4485 }
4486 }
4487
4488 /* Unlock the media and free the associated memory. */
4489 delete pMediumLockList;
4490
4491 if (FAILED(rc)) return rc;
4492
4493 /* use the created diff for the actual attachment */
4494 medium = diff;
4495 mediumCaller.attach(medium);
4496 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4497 mediumLock.attach(medium);
4498 }
4499 while (0);
4500
4501 ComObjPtr<MediumAttachment> attachment;
4502 attachment.createObject();
4503 rc = attachment->init(this,
4504 medium,
4505 aControllerName,
4506 aControllerPort,
4507 aDevice,
4508 aType,
4509 fIndirect,
4510 false /* fPassthrough */,
4511 false /* fTempEject */,
4512 false /* fNonRotational */,
4513 false /* fDiscard */,
4514 fHotplug /* fHotPluggable */,
4515 Utf8Str::Empty);
4516 if (FAILED(rc)) return rc;
4517
4518 if (associate && !medium.isNull())
4519 {
4520 // as the last step, associate the medium to the VM
4521 rc = medium->addBackReference(mData->mUuid);
4522 // here we can fail because of Deleting, or being in process of creating a Diff
4523 if (FAILED(rc)) return rc;
4524
4525 mediumLock.release();
4526 treeLock.release();
4527 alock.release();
4528 addMediumToRegistry(medium);
4529 alock.acquire();
4530 treeLock.acquire();
4531 mediumLock.acquire();
4532 }
4533
4534 /* success: finally remember the attachment */
4535 setModified(IsModified_Storage);
4536 mMediaData.backup();
4537 mMediaData->mAttachments.push_back(attachment);
4538
4539 mediumLock.release();
4540 treeLock.release();
4541 alock.release();
4542
4543 if (fHotplug || fSilent)
4544 {
4545 if (!medium.isNull())
4546 {
4547 MediumLockList *pMediumLockList(new MediumLockList());
4548
4549 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4550 true /* fMediumLockWrite */,
4551 NULL,
4552 *pMediumLockList);
4553 alock.acquire();
4554 if (FAILED(rc))
4555 delete pMediumLockList;
4556 else
4557 {
4558 mData->mSession.mLockedMedia.Unlock();
4559 alock.release();
4560 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4561 mData->mSession.mLockedMedia.Lock();
4562 alock.acquire();
4563 }
4564 alock.release();
4565 }
4566
4567 if (SUCCEEDED(rc))
4568 {
4569 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4570 /* Remove lock list in case of error. */
4571 if (FAILED(rc))
4572 {
4573 mData->mSession.mLockedMedia.Unlock();
4574 mData->mSession.mLockedMedia.Remove(attachment);
4575 mData->mSession.mLockedMedia.Lock();
4576 }
4577 }
4578 }
4579
4580 mParent->saveModifiedRegistries();
4581
4582 return rc;
4583}
4584
4585STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4586 LONG aDevice)
4587{
4588 CheckComArgStrNotEmptyOrNull(aControllerName);
4589
4590 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4591 aControllerName, aControllerPort, aDevice));
4592
4593 AutoCaller autoCaller(this);
4594 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4595
4596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4597
4598 HRESULT rc = checkStateDependency(MutableStateDep);
4599 if (FAILED(rc)) return rc;
4600
4601 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4602
4603 /* Check for an existing controller. */
4604 ComObjPtr<StorageController> ctl;
4605 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4606 if (FAILED(rc)) return rc;
4607
4608 StorageControllerType_T ctrlType;
4609 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4610 if (FAILED(rc))
4611 return setError(E_FAIL,
4612 tr("Could not get type of controller '%ls'"),
4613 aControllerName);
4614
4615 bool fSilent = false;
4616 Utf8Str strReconfig;
4617
4618 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4619 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4620 if ( mData->mMachineState == MachineState_Paused
4621 && strReconfig == "1")
4622 fSilent = true;
4623
4624 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4625 bool fHotplug = false;
4626 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4627 fHotplug = true;
4628
4629 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4630 return setError(VBOX_E_INVALID_VM_STATE,
4631 tr("Controller '%ls' does not support hotplugging"),
4632 aControllerName);
4633
4634 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4635 aControllerName,
4636 aControllerPort,
4637 aDevice);
4638 if (!pAttach)
4639 return setError(VBOX_E_OBJECT_NOT_FOUND,
4640 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4641 aDevice, aControllerPort, aControllerName);
4642
4643 if (fHotplug && !pAttach->getHotPluggable())
4644 return setError(VBOX_E_NOT_SUPPORTED,
4645 tr("The device slot %d on port %d of controller '%ls' does not support hotplugging"),
4646 aDevice, aControllerPort, aControllerName);
4647
4648 /*
4649 * The VM has to detach the device before we delete any implicit diffs.
4650 * If this fails we can roll back without loosing data.
4651 */
4652 if (fHotplug || fSilent)
4653 {
4654 alock.release();
4655 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4656 alock.acquire();
4657 }
4658 if (FAILED(rc)) return rc;
4659
4660 /* If we are here everything went well and we can delete the implicit now. */
4661 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4662
4663 alock.release();
4664
4665 mParent->saveModifiedRegistries();
4666
4667 return rc;
4668}
4669
4670STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4671 LONG aDevice, BOOL aPassthrough)
4672{
4673 CheckComArgStrNotEmptyOrNull(aControllerName);
4674
4675 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4676 aControllerName, aControllerPort, aDevice, aPassthrough));
4677
4678 AutoCaller autoCaller(this);
4679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4680
4681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4682
4683 HRESULT rc = checkStateDependency(MutableStateDep);
4684 if (FAILED(rc)) return rc;
4685
4686 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4687
4688 if (Global::IsOnlineOrTransient(mData->mMachineState))
4689 return setError(VBOX_E_INVALID_VM_STATE,
4690 tr("Invalid machine state: %s"),
4691 Global::stringifyMachineState(mData->mMachineState));
4692
4693 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4694 aControllerName,
4695 aControllerPort,
4696 aDevice);
4697 if (!pAttach)
4698 return setError(VBOX_E_OBJECT_NOT_FOUND,
4699 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4700 aDevice, aControllerPort, aControllerName);
4701
4702
4703 setModified(IsModified_Storage);
4704 mMediaData.backup();
4705
4706 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4707
4708 if (pAttach->getType() != DeviceType_DVD)
4709 return setError(E_INVALIDARG,
4710 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4711 aDevice, aControllerPort, aControllerName);
4712 pAttach->updatePassthrough(!!aPassthrough);
4713
4714 return S_OK;
4715}
4716
4717STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4718 LONG aDevice, BOOL aTemporaryEject)
4719{
4720 CheckComArgStrNotEmptyOrNull(aControllerName);
4721
4722 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4723 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4724
4725 AutoCaller autoCaller(this);
4726 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4727
4728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4729
4730 HRESULT rc = checkStateDependency(MutableStateDep);
4731 if (FAILED(rc)) return rc;
4732
4733 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4734 aControllerName,
4735 aControllerPort,
4736 aDevice);
4737 if (!pAttach)
4738 return setError(VBOX_E_OBJECT_NOT_FOUND,
4739 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4740 aDevice, aControllerPort, aControllerName);
4741
4742
4743 setModified(IsModified_Storage);
4744 mMediaData.backup();
4745
4746 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4747
4748 if (pAttach->getType() != DeviceType_DVD)
4749 return setError(E_INVALIDARG,
4750 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4751 aDevice, aControllerPort, aControllerName);
4752 pAttach->updateTempEject(!!aTemporaryEject);
4753
4754 return S_OK;
4755}
4756
4757STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4758 LONG aDevice, BOOL aNonRotational)
4759{
4760 CheckComArgStrNotEmptyOrNull(aControllerName);
4761
4762 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4763 aControllerName, aControllerPort, aDevice, aNonRotational));
4764
4765 AutoCaller autoCaller(this);
4766 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4767
4768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4769
4770 HRESULT rc = checkStateDependency(MutableStateDep);
4771 if (FAILED(rc)) return rc;
4772
4773 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4774
4775 if (Global::IsOnlineOrTransient(mData->mMachineState))
4776 return setError(VBOX_E_INVALID_VM_STATE,
4777 tr("Invalid machine state: %s"),
4778 Global::stringifyMachineState(mData->mMachineState));
4779
4780 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4781 aControllerName,
4782 aControllerPort,
4783 aDevice);
4784 if (!pAttach)
4785 return setError(VBOX_E_OBJECT_NOT_FOUND,
4786 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4787 aDevice, aControllerPort, aControllerName);
4788
4789
4790 setModified(IsModified_Storage);
4791 mMediaData.backup();
4792
4793 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4794
4795 if (pAttach->getType() != DeviceType_HardDisk)
4796 return setError(E_INVALIDARG,
4797 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4798 aDevice, aControllerPort, aControllerName);
4799 pAttach->updateNonRotational(!!aNonRotational);
4800
4801 return S_OK;
4802}
4803
4804STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4805 LONG aDevice, BOOL aDiscard)
4806{
4807 CheckComArgStrNotEmptyOrNull(aControllerName);
4808
4809 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4810 aControllerName, aControllerPort, aDevice, aDiscard));
4811
4812 AutoCaller autoCaller(this);
4813 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4814
4815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4816
4817 HRESULT rc = checkStateDependency(MutableStateDep);
4818 if (FAILED(rc)) return rc;
4819
4820 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4821
4822 if (Global::IsOnlineOrTransient(mData->mMachineState))
4823 return setError(VBOX_E_INVALID_VM_STATE,
4824 tr("Invalid machine state: %s"),
4825 Global::stringifyMachineState(mData->mMachineState));
4826
4827 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4828 aControllerName,
4829 aControllerPort,
4830 aDevice);
4831 if (!pAttach)
4832 return setError(VBOX_E_OBJECT_NOT_FOUND,
4833 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4834 aDevice, aControllerPort, aControllerName);
4835
4836
4837 setModified(IsModified_Storage);
4838 mMediaData.backup();
4839
4840 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4841
4842 if (pAttach->getType() != DeviceType_HardDisk)
4843 return setError(E_INVALIDARG,
4844 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4845 aDevice, aControllerPort, aControllerName);
4846 pAttach->updateDiscard(!!aDiscard);
4847
4848 return S_OK;
4849}
4850
4851STDMETHODIMP Machine::SetHotPluggableForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4852 LONG aDevice, BOOL aHotPluggable)
4853{
4854 CheckComArgStrNotEmptyOrNull(aControllerName);
4855
4856 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4857 aControllerName, aControllerPort, aDevice, aHotPluggable));
4858
4859 AutoCaller autoCaller(this);
4860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4861
4862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4863
4864 HRESULT rc = checkStateDependency(MutableStateDep);
4865 if (FAILED(rc)) return rc;
4866
4867 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4868
4869 if (Global::IsOnlineOrTransient(mData->mMachineState))
4870 return setError(VBOX_E_INVALID_VM_STATE,
4871 tr("Invalid machine state: %s"),
4872 Global::stringifyMachineState(mData->mMachineState));
4873
4874 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4875 aControllerName,
4876 aControllerPort,
4877 aDevice);
4878 if (!pAttach)
4879 return setError(VBOX_E_OBJECT_NOT_FOUND,
4880 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4881 aDevice, aControllerPort, aControllerName);
4882
4883 /* Check for an existing controller. */
4884 ComObjPtr<StorageController> ctl;
4885 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4886 if (FAILED(rc)) return rc;
4887
4888 StorageControllerType_T ctrlType;
4889 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4890 if (FAILED(rc))
4891 return setError(E_FAIL,
4892 tr("Could not get type of controller '%ls'"),
4893 aControllerName);
4894
4895 if (!isControllerHotplugCapable(ctrlType))
4896 return setError(VBOX_E_NOT_SUPPORTED,
4897 tr("Controller '%ls' does not support changing the hot-pluggable device flag"),
4898 aControllerName);
4899
4900 setModified(IsModified_Storage);
4901 mMediaData.backup();
4902
4903 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4904
4905 if (pAttach->getType() == DeviceType_Floppy)
4906 return setError(E_INVALIDARG,
4907 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%ls' is a floppy drive"),
4908 aDevice, aControllerPort, aControllerName);
4909 pAttach->updateHotPluggable(!!aHotPluggable);
4910
4911 return S_OK;
4912}
4913
4914STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4915 LONG aDevice)
4916{
4917 int rc = S_OK;
4918 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4919 aControllerName, aControllerPort, aDevice));
4920
4921 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4922
4923 return rc;
4924}
4925
4926STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4927 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4928{
4929 CheckComArgStrNotEmptyOrNull(aControllerName);
4930
4931 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4932 aControllerName, aControllerPort, aDevice));
4933
4934 AutoCaller autoCaller(this);
4935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4936
4937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4938
4939 HRESULT rc = checkStateDependency(MutableStateDep);
4940 if (FAILED(rc)) return rc;
4941
4942 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4943
4944 if (Global::IsOnlineOrTransient(mData->mMachineState))
4945 return setError(VBOX_E_INVALID_VM_STATE,
4946 tr("Invalid machine state: %s"),
4947 Global::stringifyMachineState(mData->mMachineState));
4948
4949 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4950 aControllerName,
4951 aControllerPort,
4952 aDevice);
4953 if (!pAttach)
4954 return setError(VBOX_E_OBJECT_NOT_FOUND,
4955 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4956 aDevice, aControllerPort, aControllerName);
4957
4958
4959 setModified(IsModified_Storage);
4960 mMediaData.backup();
4961
4962 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4963 if (aBandwidthGroup && group.isNull())
4964 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4965
4966 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4967
4968 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4969 if (strBandwidthGroupOld.isNotEmpty())
4970 {
4971 /* Get the bandwidth group object and release it - this must not fail. */
4972 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4973 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4974 Assert(SUCCEEDED(rc));
4975
4976 pBandwidthGroupOld->i_release();
4977 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4978 }
4979
4980 if (!group.isNull())
4981 {
4982 group->i_reference();
4983 pAttach->updateBandwidthGroup(group->i_getName());
4984 }
4985
4986 return S_OK;
4987}
4988
4989STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4990 LONG aControllerPort,
4991 LONG aDevice,
4992 DeviceType_T aType)
4993{
4994 HRESULT rc = S_OK;
4995
4996 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4997 aControllerName, aControllerPort, aDevice, aType));
4998
4999 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
5000
5001 return rc;
5002}
5003
5004
5005
5006STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
5007 LONG aControllerPort,
5008 LONG aDevice,
5009 BOOL aForce)
5010{
5011 int rc = S_OK;
5012 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
5013 aControllerName, aControllerPort, aForce));
5014
5015 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
5016
5017 return rc;
5018}
5019
5020STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
5021 LONG aControllerPort,
5022 LONG aDevice,
5023 IMedium *aMedium,
5024 BOOL aForce)
5025{
5026 int rc = S_OK;
5027 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
5028 aControllerName, aControllerPort, aDevice, aForce));
5029
5030 CheckComArgStrNotEmptyOrNull(aControllerName);
5031
5032 AutoCaller autoCaller(this);
5033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5034
5035 // request the host lock first, since might be calling Host methods for getting host drives;
5036 // next, protect the media tree all the while we're in here, as well as our member variables
5037 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
5038 this->lockHandle(),
5039 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5040
5041 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5042 aControllerName,
5043 aControllerPort,
5044 aDevice);
5045 if (pAttach.isNull())
5046 return setError(VBOX_E_OBJECT_NOT_FOUND,
5047 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
5048 aDevice, aControllerPort, aControllerName);
5049
5050 /* Remember previously mounted medium. The medium before taking the
5051 * backup is not necessarily the same thing. */
5052 ComObjPtr<Medium> oldmedium;
5053 oldmedium = pAttach->getMedium();
5054
5055 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
5056 if (aMedium && pMedium.isNull())
5057 return setError(E_INVALIDARG, "The given medium pointer is invalid");
5058
5059 AutoCaller mediumCaller(pMedium);
5060 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
5061
5062 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5063 if (pMedium)
5064 {
5065 DeviceType_T mediumType = pAttach->getType();
5066 switch (mediumType)
5067 {
5068 case DeviceType_DVD:
5069 case DeviceType_Floppy:
5070 break;
5071
5072 default:
5073 return setError(VBOX_E_INVALID_OBJECT_STATE,
5074 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5075 aControllerPort,
5076 aDevice,
5077 aControllerName);
5078 }
5079 }
5080
5081 setModified(IsModified_Storage);
5082 mMediaData.backup();
5083
5084 {
5085 // The backup operation makes the pAttach reference point to the
5086 // old settings. Re-get the correct reference.
5087 pAttach = findAttachment(mMediaData->mAttachments,
5088 aControllerName,
5089 aControllerPort,
5090 aDevice);
5091 if (!oldmedium.isNull())
5092 oldmedium->removeBackReference(mData->mUuid);
5093 if (!pMedium.isNull())
5094 {
5095 pMedium->addBackReference(mData->mUuid);
5096
5097 mediumLock.release();
5098 multiLock.release();
5099 addMediumToRegistry(pMedium);
5100 multiLock.acquire();
5101 mediumLock.acquire();
5102 }
5103
5104 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5105 pAttach->updateMedium(pMedium);
5106 }
5107
5108 setModified(IsModified_Storage);
5109
5110 mediumLock.release();
5111 multiLock.release();
5112 rc = onMediumChange(pAttach, aForce);
5113 multiLock.acquire();
5114 mediumLock.acquire();
5115
5116 /* On error roll back this change only. */
5117 if (FAILED(rc))
5118 {
5119 if (!pMedium.isNull())
5120 pMedium->removeBackReference(mData->mUuid);
5121 pAttach = findAttachment(mMediaData->mAttachments,
5122 aControllerName,
5123 aControllerPort,
5124 aDevice);
5125 /* If the attachment is gone in the meantime, bail out. */
5126 if (pAttach.isNull())
5127 return rc;
5128 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5129 if (!oldmedium.isNull())
5130 oldmedium->addBackReference(mData->mUuid);
5131 pAttach->updateMedium(oldmedium);
5132 }
5133
5134 mediumLock.release();
5135 multiLock.release();
5136
5137 mParent->saveModifiedRegistries();
5138
5139 return rc;
5140}
5141
5142STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5143 LONG aControllerPort,
5144 LONG aDevice,
5145 IMedium **aMedium)
5146{
5147 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5148 aControllerName, aControllerPort, aDevice));
5149
5150 CheckComArgStrNotEmptyOrNull(aControllerName);
5151 CheckComArgOutPointerValid(aMedium);
5152
5153 AutoCaller autoCaller(this);
5154 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5155
5156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5157
5158 *aMedium = NULL;
5159
5160 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5161 aControllerName,
5162 aControllerPort,
5163 aDevice);
5164 if (pAttach.isNull())
5165 return setError(VBOX_E_OBJECT_NOT_FOUND,
5166 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5167 aDevice, aControllerPort, aControllerName);
5168
5169 pAttach->getMedium().queryInterfaceTo(aMedium);
5170
5171 return S_OK;
5172}
5173
5174STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5175{
5176 CheckComArgOutPointerValid(port);
5177 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5178
5179 AutoCaller autoCaller(this);
5180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5181
5182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5183
5184 mSerialPorts[slot].queryInterfaceTo(port);
5185
5186 return S_OK;
5187}
5188
5189STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5190{
5191 CheckComArgOutPointerValid(port);
5192 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5193
5194 AutoCaller autoCaller(this);
5195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5196
5197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5198
5199 mParallelPorts[slot].queryInterfaceTo(port);
5200
5201 return S_OK;
5202}
5203
5204STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5205{
5206 CheckComArgOutPointerValid(adapter);
5207 /* Do not assert if slot is out of range, just return the advertised
5208 status. testdriver/vbox.py triggers this in logVmInfo. */
5209 if (slot >= mNetworkAdapters.size())
5210 return setError(E_INVALIDARG,
5211 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5212 slot, mNetworkAdapters.size());
5213
5214 AutoCaller autoCaller(this);
5215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5216
5217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5218
5219 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5220
5221 return S_OK;
5222}
5223
5224STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5225{
5226 CheckComArgOutSafeArrayPointerValid(aKeys);
5227
5228 AutoCaller autoCaller(this);
5229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5230
5231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5232
5233 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5234 int i = 0;
5235 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5236 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5237 ++it, ++i)
5238 {
5239 const Utf8Str &strKey = it->first;
5240 strKey.cloneTo(&saKeys[i]);
5241 }
5242 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5243
5244 return S_OK;
5245 }
5246
5247 /**
5248 * @note Locks this object for reading.
5249 */
5250STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5251 BSTR *aValue)
5252{
5253 CheckComArgStrNotEmptyOrNull(aKey);
5254 CheckComArgOutPointerValid(aValue);
5255
5256 AutoCaller autoCaller(this);
5257 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5258
5259 /* start with nothing found */
5260 Bstr bstrResult("");
5261
5262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5263
5264 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5265 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5266 // found:
5267 bstrResult = it->second; // source is a Utf8Str
5268
5269 /* return the result to caller (may be empty) */
5270 bstrResult.cloneTo(aValue);
5271
5272 return S_OK;
5273}
5274
5275 /**
5276 * @note Locks mParent for writing + this object for writing.
5277 */
5278STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5279{
5280 CheckComArgStrNotEmptyOrNull(aKey);
5281
5282 AutoCaller autoCaller(this);
5283 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5284
5285 Utf8Str strKey(aKey);
5286 Utf8Str strValue(aValue);
5287 Utf8Str strOldValue; // empty
5288
5289 // locking note: we only hold the read lock briefly to look up the old value,
5290 // then release it and call the onExtraCanChange callbacks. There is a small
5291 // chance of a race insofar as the callback might be called twice if two callers
5292 // change the same key at the same time, but that's a much better solution
5293 // than the deadlock we had here before. The actual changing of the extradata
5294 // is then performed under the write lock and race-free.
5295
5296 // look up the old value first; if nothing has changed then we need not do anything
5297 {
5298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5299 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5300 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5301 strOldValue = it->second;
5302 }
5303
5304 bool fChanged;
5305 if ((fChanged = (strOldValue != strValue)))
5306 {
5307 // ask for permission from all listeners outside the locks;
5308 // onExtraDataCanChange() only briefly requests the VirtualBox
5309 // lock to copy the list of callbacks to invoke
5310 Bstr error;
5311 Bstr bstrValue(aValue);
5312
5313 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5314 {
5315 const char *sep = error.isEmpty() ? "" : ": ";
5316 CBSTR err = error.raw();
5317 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5318 sep, err));
5319 return setError(E_ACCESSDENIED,
5320 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5321 aKey,
5322 bstrValue.raw(),
5323 sep,
5324 err);
5325 }
5326
5327 // data is changing and change not vetoed: then write it out under the lock
5328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5329
5330 if (isSnapshotMachine())
5331 {
5332 HRESULT rc = checkStateDependency(MutableStateDep);
5333 if (FAILED(rc)) return rc;
5334 }
5335
5336 if (strValue.isEmpty())
5337 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5338 else
5339 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5340 // creates a new key if needed
5341
5342 bool fNeedsGlobalSaveSettings = false;
5343 saveSettings(&fNeedsGlobalSaveSettings);
5344
5345 if (fNeedsGlobalSaveSettings)
5346 {
5347 // save the global settings; for that we should hold only the VirtualBox lock
5348 alock.release();
5349 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5350 mParent->saveSettings();
5351 }
5352 }
5353
5354 // fire notification outside the lock
5355 if (fChanged)
5356 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5357
5358 return S_OK;
5359}
5360
5361STDMETHODIMP Machine::SetSettingsFilePath(IN_BSTR aFilePath, IProgress **aProgress)
5362{
5363 CheckComArgStrNotEmptyOrNull(aFilePath);
5364 CheckComArgOutPointerValid(aProgress);
5365
5366 AutoCaller autoCaller(this);
5367 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5368
5369 *aProgress = NULL;
5370 ReturnComNotImplemented();
5371}
5372
5373STDMETHODIMP Machine::SaveSettings()
5374{
5375 AutoCaller autoCaller(this);
5376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5377
5378 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5379
5380 /* when there was auto-conversion, we want to save the file even if
5381 * the VM is saved */
5382 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5383 if (FAILED(rc)) return rc;
5384
5385 /* the settings file path may never be null */
5386 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5387
5388 /* save all VM data excluding snapshots */
5389 bool fNeedsGlobalSaveSettings = false;
5390 rc = saveSettings(&fNeedsGlobalSaveSettings);
5391 mlock.release();
5392
5393 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5394 {
5395 // save the global settings; for that we should hold only the VirtualBox lock
5396 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5397 rc = mParent->saveSettings();
5398 }
5399
5400 return rc;
5401}
5402
5403STDMETHODIMP Machine::DiscardSettings()
5404{
5405 AutoCaller autoCaller(this);
5406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5407
5408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5409
5410 HRESULT rc = checkStateDependency(MutableStateDep);
5411 if (FAILED(rc)) return rc;
5412
5413 /*
5414 * during this rollback, the session will be notified if data has
5415 * been actually changed
5416 */
5417 rollback(true /* aNotify */);
5418
5419 return S_OK;
5420}
5421
5422/** @note Locks objects! */
5423STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5424 ComSafeArrayOut(IMedium*, aMedia))
5425{
5426 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5427 AutoLimitedCaller autoCaller(this);
5428 AssertComRCReturnRC(autoCaller.rc());
5429
5430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5431
5432 Guid id(getId());
5433
5434 if (mData->mSession.mState != SessionState_Unlocked)
5435 return setError(VBOX_E_INVALID_OBJECT_STATE,
5436 tr("Cannot unregister the machine '%s' while it is locked"),
5437 mUserData->s.strName.c_str());
5438
5439 // wait for state dependents to drop to zero
5440 ensureNoStateDependencies();
5441
5442 if (!mData->mAccessible)
5443 {
5444 // inaccessible maschines can only be unregistered; uninitialize ourselves
5445 // here because currently there may be no unregistered that are inaccessible
5446 // (this state combination is not supported). Note releasing the caller and
5447 // leaving the lock before calling uninit()
5448 alock.release();
5449 autoCaller.release();
5450
5451 uninit();
5452
5453 mParent->unregisterMachine(this, id);
5454 // calls VirtualBox::saveSettings()
5455
5456 return S_OK;
5457 }
5458
5459 HRESULT rc = S_OK;
5460
5461 // discard saved state
5462 if (mData->mMachineState == MachineState_Saved)
5463 {
5464 // add the saved state file to the list of files the caller should delete
5465 Assert(!mSSData->strStateFilePath.isEmpty());
5466 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5467
5468 mSSData->strStateFilePath.setNull();
5469
5470 // unconditionally set the machine state to powered off, we now
5471 // know no session has locked the machine
5472 mData->mMachineState = MachineState_PoweredOff;
5473 }
5474
5475 size_t cSnapshots = 0;
5476 if (mData->mFirstSnapshot)
5477 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5478 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5479 // fail now before we start detaching media
5480 return setError(VBOX_E_INVALID_OBJECT_STATE,
5481 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5482 mUserData->s.strName.c_str(), cSnapshots);
5483
5484 // This list collects the medium objects from all medium attachments
5485 // which we will detach from the machine and its snapshots, in a specific
5486 // order which allows for closing all media without getting "media in use"
5487 // errors, simply by going through the list from the front to the back:
5488 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5489 // and must be closed before the parent media from the snapshots, or closing the parents
5490 // will fail because they still have children);
5491 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5492 // the root ("first") snapshot of the machine.
5493 MediaList llMedia;
5494
5495 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5496 && mMediaData->mAttachments.size()
5497 )
5498 {
5499 // we have media attachments: detach them all and add the Medium objects to our list
5500 if (cleanupMode != CleanupMode_UnregisterOnly)
5501 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5502 else
5503 return setError(VBOX_E_INVALID_OBJECT_STATE,
5504 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5505 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5506 }
5507
5508 if (cSnapshots)
5509 {
5510 // autoCleanup must be true here, or we would have failed above
5511
5512 // add the media from the medium attachments of the snapshots to llMedia
5513 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5514 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5515 // into the children first
5516
5517 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5518 MachineState_T oldState = mData->mMachineState;
5519 mData->mMachineState = MachineState_DeletingSnapshot;
5520
5521 // make a copy of the first snapshot so the refcount does not drop to 0
5522 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5523 // because of the AutoCaller voodoo)
5524 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5525
5526 // GO!
5527 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5528
5529 mData->mMachineState = oldState;
5530 }
5531
5532 if (FAILED(rc))
5533 {
5534 rollbackMedia();
5535 return rc;
5536 }
5537
5538 // commit all the media changes made above
5539 commitMedia();
5540
5541 mData->mRegistered = false;
5542
5543 // machine lock no longer needed
5544 alock.release();
5545
5546 // return media to caller
5547 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5548 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5549
5550 mParent->unregisterMachine(this, id);
5551 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5552
5553 return S_OK;
5554}
5555
5556struct Machine::DeleteTask
5557{
5558 ComObjPtr<Machine> pMachine;
5559 RTCList<ComPtr<IMedium> > llMediums;
5560 StringsList llFilesToDelete;
5561 ComObjPtr<Progress> pProgress;
5562};
5563
5564STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5565{
5566 LogFlowFuncEnter();
5567
5568 AutoCaller autoCaller(this);
5569 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5570
5571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5572
5573 HRESULT rc = checkStateDependency(MutableStateDep);
5574 if (FAILED(rc)) return rc;
5575
5576 if (mData->mRegistered)
5577 return setError(VBOX_E_INVALID_VM_STATE,
5578 tr("Cannot delete settings of a registered machine"));
5579
5580 DeleteTask *pTask = new DeleteTask;
5581 pTask->pMachine = this;
5582 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5583
5584 // collect files to delete
5585 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5586
5587 for (size_t i = 0; i < sfaMedia.size(); ++i)
5588 {
5589 IMedium *pIMedium(sfaMedia[i]);
5590 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5591 if (pMedium.isNull())
5592 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5593 SafeArray<BSTR> ids;
5594 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5595 if (FAILED(rc)) return rc;
5596 /* At this point the medium should not have any back references
5597 * anymore. If it has it is attached to another VM and *must* not
5598 * deleted. */
5599 if (ids.size() < 1)
5600 pTask->llMediums.append(pMedium);
5601 }
5602 if (mData->pMachineConfigFile->fileExists())
5603 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5604
5605 pTask->pProgress.createObject();
5606 pTask->pProgress->init(getVirtualBox(),
5607 static_cast<IMachine*>(this) /* aInitiator */,
5608 Bstr(tr("Deleting files")).raw(),
5609 true /* fCancellable */,
5610 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5611 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5612
5613 int vrc = RTThreadCreate(NULL,
5614 Machine::deleteThread,
5615 (void*)pTask,
5616 0,
5617 RTTHREADTYPE_MAIN_WORKER,
5618 0,
5619 "MachineDelete");
5620
5621 pTask->pProgress.queryInterfaceTo(aProgress);
5622
5623 if (RT_FAILURE(vrc))
5624 {
5625 delete pTask;
5626 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5627 }
5628
5629 LogFlowFuncLeave();
5630
5631 return S_OK;
5632}
5633
5634/**
5635 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5636 * calls Machine::deleteTaskWorker() on the actual machine object.
5637 * @param Thread
5638 * @param pvUser
5639 * @return
5640 */
5641/*static*/
5642DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5643{
5644 LogFlowFuncEnter();
5645
5646 DeleteTask *pTask = (DeleteTask*)pvUser;
5647 Assert(pTask);
5648 Assert(pTask->pMachine);
5649 Assert(pTask->pProgress);
5650
5651 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5652 pTask->pProgress->notifyComplete(rc);
5653
5654 delete pTask;
5655
5656 LogFlowFuncLeave();
5657
5658 NOREF(Thread);
5659
5660 return VINF_SUCCESS;
5661}
5662
5663/**
5664 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5665 * @param task
5666 * @return
5667 */
5668HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5669{
5670 AutoCaller autoCaller(this);
5671 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5672
5673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5674
5675 HRESULT rc = S_OK;
5676
5677 try
5678 {
5679 ULONG uLogHistoryCount = 3;
5680 ComPtr<ISystemProperties> systemProperties;
5681 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5682 if (FAILED(rc)) throw rc;
5683
5684 if (!systemProperties.isNull())
5685 {
5686 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5687 if (FAILED(rc)) throw rc;
5688 }
5689
5690 MachineState_T oldState = mData->mMachineState;
5691 setMachineState(MachineState_SettingUp);
5692 alock.release();
5693 for (size_t i = 0; i < task.llMediums.size(); ++i)
5694 {
5695 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5696 {
5697 AutoCaller mac(pMedium);
5698 if (FAILED(mac.rc())) throw mac.rc();
5699 Utf8Str strLocation = pMedium->getLocationFull();
5700 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5701 if (FAILED(rc)) throw rc;
5702 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5703 }
5704 ComPtr<IProgress> pProgress2;
5705 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5706 if (FAILED(rc)) throw rc;
5707 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5708 if (FAILED(rc)) throw rc;
5709 /* Check the result of the asynchronous process. */
5710 LONG iRc;
5711 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5712 if (FAILED(rc)) throw rc;
5713 /* If the thread of the progress object has an error, then
5714 * retrieve the error info from there, or it'll be lost. */
5715 if (FAILED(iRc))
5716 throw setError(ProgressErrorInfo(pProgress2));
5717
5718 /* Close the medium, deliberately without checking the return
5719 * code, and without leaving any trace in the error info, as
5720 * a failure here is a very minor issue, which shouldn't happen
5721 * as above we even managed to delete the medium. */
5722 {
5723 ErrorInfoKeeper eik;
5724 pMedium->Close();
5725 }
5726
5727 }
5728 setMachineState(oldState);
5729 alock.acquire();
5730
5731 // delete the files pushed on the task list by Machine::Delete()
5732 // (this includes saved states of the machine and snapshots and
5733 // medium storage files from the IMedium list passed in, and the
5734 // machine XML file)
5735 StringsList::const_iterator it = task.llFilesToDelete.begin();
5736 while (it != task.llFilesToDelete.end())
5737 {
5738 const Utf8Str &strFile = *it;
5739 LogFunc(("Deleting file %s\n", strFile.c_str()));
5740 int vrc = RTFileDelete(strFile.c_str());
5741 if (RT_FAILURE(vrc))
5742 throw setError(VBOX_E_IPRT_ERROR,
5743 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5744
5745 ++it;
5746 if (it == task.llFilesToDelete.end())
5747 {
5748 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5749 if (FAILED(rc)) throw rc;
5750 break;
5751 }
5752
5753 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5754 if (FAILED(rc)) throw rc;
5755 }
5756
5757 /* delete the settings only when the file actually exists */
5758 if (mData->pMachineConfigFile->fileExists())
5759 {
5760 /* Delete any backup or uncommitted XML files. Ignore failures.
5761 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5762 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5763 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5764 RTFileDelete(otherXml.c_str());
5765 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5766 RTFileDelete(otherXml.c_str());
5767
5768 /* delete the Logs folder, nothing important should be left
5769 * there (we don't check for errors because the user might have
5770 * some private files there that we don't want to delete) */
5771 Utf8Str logFolder;
5772 getLogFolder(logFolder);
5773 Assert(logFolder.length());
5774 if (RTDirExists(logFolder.c_str()))
5775 {
5776 /* Delete all VBox.log[.N] files from the Logs folder
5777 * (this must be in sync with the rotation logic in
5778 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5779 * files that may have been created by the GUI. */
5780 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5781 logFolder.c_str(), RTPATH_DELIMITER);
5782 RTFileDelete(log.c_str());
5783 log = Utf8StrFmt("%s%cVBox.png",
5784 logFolder.c_str(), RTPATH_DELIMITER);
5785 RTFileDelete(log.c_str());
5786 for (int i = uLogHistoryCount; i > 0; i--)
5787 {
5788 log = Utf8StrFmt("%s%cVBox.log.%d",
5789 logFolder.c_str(), RTPATH_DELIMITER, i);
5790 RTFileDelete(log.c_str());
5791 log = Utf8StrFmt("%s%cVBox.png.%d",
5792 logFolder.c_str(), RTPATH_DELIMITER, i);
5793 RTFileDelete(log.c_str());
5794 }
5795
5796 RTDirRemove(logFolder.c_str());
5797 }
5798
5799 /* delete the Snapshots folder, nothing important should be left
5800 * there (we don't check for errors because the user might have
5801 * some private files there that we don't want to delete) */
5802 Utf8Str strFullSnapshotFolder;
5803 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5804 Assert(!strFullSnapshotFolder.isEmpty());
5805 if (RTDirExists(strFullSnapshotFolder.c_str()))
5806 RTDirRemove(strFullSnapshotFolder.c_str());
5807
5808 // delete the directory that contains the settings file, but only
5809 // if it matches the VM name
5810 Utf8Str settingsDir;
5811 if (isInOwnDir(&settingsDir))
5812 RTDirRemove(settingsDir.c_str());
5813 }
5814
5815 alock.release();
5816
5817 mParent->saveModifiedRegistries();
5818 }
5819 catch (HRESULT aRC) { rc = aRC; }
5820
5821 return rc;
5822}
5823
5824STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5825{
5826 CheckComArgOutPointerValid(aSnapshot);
5827
5828 AutoCaller autoCaller(this);
5829 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5830
5831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5832
5833 ComObjPtr<Snapshot> pSnapshot;
5834 HRESULT rc;
5835
5836 if (!aNameOrId || !*aNameOrId)
5837 // null case (caller wants root snapshot): findSnapshotById() handles this
5838 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5839 else
5840 {
5841 Guid uuid(aNameOrId);
5842 if (uuid.isValid())
5843 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5844 else
5845 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5846 }
5847 pSnapshot.queryInterfaceTo(aSnapshot);
5848
5849 return rc;
5850}
5851
5852STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5853{
5854 CheckComArgStrNotEmptyOrNull(aName);
5855 CheckComArgStrNotEmptyOrNull(aHostPath);
5856
5857 AutoCaller autoCaller(this);
5858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5859
5860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5861
5862 HRESULT rc = checkStateDependency(MutableStateDep);
5863 if (FAILED(rc)) return rc;
5864
5865 Utf8Str strName(aName);
5866
5867 ComObjPtr<SharedFolder> sharedFolder;
5868 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5869 if (SUCCEEDED(rc))
5870 return setError(VBOX_E_OBJECT_IN_USE,
5871 tr("Shared folder named '%s' already exists"),
5872 strName.c_str());
5873
5874 sharedFolder.createObject();
5875 rc = sharedFolder->init(getMachine(),
5876 strName,
5877 aHostPath,
5878 !!aWritable,
5879 !!aAutoMount,
5880 true /* fFailOnError */);
5881 if (FAILED(rc)) return rc;
5882
5883 setModified(IsModified_SharedFolders);
5884 mHWData.backup();
5885 mHWData->mSharedFolders.push_back(sharedFolder);
5886
5887 /* inform the direct session if any */
5888 alock.release();
5889 onSharedFolderChange();
5890
5891 return S_OK;
5892}
5893
5894STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5895{
5896 CheckComArgStrNotEmptyOrNull(aName);
5897
5898 AutoCaller autoCaller(this);
5899 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5900
5901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5902
5903 HRESULT rc = checkStateDependency(MutableStateDep);
5904 if (FAILED(rc)) return rc;
5905
5906 ComObjPtr<SharedFolder> sharedFolder;
5907 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5908 if (FAILED(rc)) return rc;
5909
5910 setModified(IsModified_SharedFolders);
5911 mHWData.backup();
5912 mHWData->mSharedFolders.remove(sharedFolder);
5913
5914 /* inform the direct session if any */
5915 alock.release();
5916 onSharedFolderChange();
5917
5918 return S_OK;
5919}
5920
5921STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5922{
5923 CheckComArgOutPointerValid(aCanShow);
5924
5925 /* start with No */
5926 *aCanShow = FALSE;
5927
5928 AutoCaller autoCaller(this);
5929 AssertComRCReturnRC(autoCaller.rc());
5930
5931 ComPtr<IInternalSessionControl> directControl;
5932 {
5933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5934
5935 if (mData->mSession.mState != SessionState_Locked)
5936 return setError(VBOX_E_INVALID_VM_STATE,
5937 tr("Machine is not locked for session (session state: %s)"),
5938 Global::stringifySessionState(mData->mSession.mState));
5939
5940 directControl = mData->mSession.mDirectControl;
5941 }
5942
5943 /* ignore calls made after #OnSessionEnd() is called */
5944 if (!directControl)
5945 return S_OK;
5946
5947 LONG64 dummy;
5948 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5949}
5950
5951STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5952{
5953 CheckComArgOutPointerValid(aWinId);
5954
5955 AutoCaller autoCaller(this);
5956 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5957
5958 ComPtr<IInternalSessionControl> directControl;
5959 {
5960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5961
5962 if (mData->mSession.mState != SessionState_Locked)
5963 return setError(E_FAIL,
5964 tr("Machine is not locked for session (session state: %s)"),
5965 Global::stringifySessionState(mData->mSession.mState));
5966
5967 directControl = mData->mSession.mDirectControl;
5968 }
5969
5970 /* ignore calls made after #OnSessionEnd() is called */
5971 if (!directControl)
5972 return S_OK;
5973
5974 BOOL dummy;
5975 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5976}
5977
5978#ifdef VBOX_WITH_GUEST_PROPS
5979/**
5980 * Look up a guest property in VBoxSVC's internal structures.
5981 */
5982HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5983 BSTR *aValue,
5984 LONG64 *aTimestamp,
5985 BSTR *aFlags) const
5986{
5987 using namespace guestProp;
5988
5989 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5990 Utf8Str strName(aName);
5991 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5992
5993 if (it != mHWData->mGuestProperties.end())
5994 {
5995 char szFlags[MAX_FLAGS_LEN + 1];
5996 it->second.strValue.cloneTo(aValue);
5997 *aTimestamp = it->second.mTimestamp;
5998 writeFlags(it->second.mFlags, szFlags);
5999 Bstr(szFlags).cloneTo(aFlags);
6000 }
6001
6002 return S_OK;
6003}
6004
6005/**
6006 * Query the VM that a guest property belongs to for the property.
6007 * @returns E_ACCESSDENIED if the VM process is not available or not
6008 * currently handling queries and the lookup should then be done in
6009 * VBoxSVC.
6010 */
6011HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
6012 BSTR *aValue,
6013 LONG64 *aTimestamp,
6014 BSTR *aFlags) const
6015{
6016 HRESULT rc;
6017 ComPtr<IInternalSessionControl> directControl;
6018 directControl = mData->mSession.mDirectControl;
6019
6020 /* fail if we were called after #OnSessionEnd() is called. This is a
6021 * silly race condition. */
6022
6023 /** @todo This code is bothering API clients (like python script clients) with
6024 * the AccessGuestProperty call, creating unncessary IPC. Need to
6025 * have a way of figuring out which kind of direct session it is... */
6026 if (!directControl)
6027 rc = E_ACCESSDENIED;
6028 else
6029 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
6030 false /* isSetter */,
6031 aValue, aTimestamp, aFlags);
6032 return rc;
6033}
6034#endif // VBOX_WITH_GUEST_PROPS
6035
6036STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
6037 BSTR *aValue,
6038 LONG64 *aTimestamp,
6039 BSTR *aFlags)
6040{
6041#ifndef VBOX_WITH_GUEST_PROPS
6042 ReturnComNotImplemented();
6043#else // VBOX_WITH_GUEST_PROPS
6044 CheckComArgStrNotEmptyOrNull(aName);
6045 CheckComArgOutPointerValid(aValue);
6046 CheckComArgOutPointerValid(aTimestamp);
6047 CheckComArgOutPointerValid(aFlags);
6048
6049 AutoCaller autoCaller(this);
6050 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6051
6052 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
6053 if (rc == E_ACCESSDENIED)
6054 /* The VM is not running or the service is not (yet) accessible */
6055 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
6056 return rc;
6057#endif // VBOX_WITH_GUEST_PROPS
6058}
6059
6060STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
6061{
6062 LONG64 dummyTimestamp;
6063 Bstr dummyFlags;
6064 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
6065}
6066
6067STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
6068{
6069 Bstr dummyValue;
6070 Bstr dummyFlags;
6071 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
6072}
6073
6074#ifdef VBOX_WITH_GUEST_PROPS
6075/**
6076 * Set a guest property in VBoxSVC's internal structures.
6077 */
6078HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
6079 IN_BSTR aFlags)
6080{
6081 using namespace guestProp;
6082
6083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6084 HRESULT rc = S_OK;
6085
6086 rc = checkStateDependency(MutableStateDep);
6087 if (FAILED(rc)) return rc;
6088
6089 try
6090 {
6091 Utf8Str utf8Name(aName);
6092 Utf8Str utf8Flags(aFlags);
6093 uint32_t fFlags = NILFLAG;
6094 if ( aFlags != NULL
6095 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6096 return setError(E_INVALIDARG,
6097 tr("Invalid guest property flag values: '%ls'"),
6098 aFlags);
6099
6100 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6101 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6102 if (it == mHWData->mGuestProperties.end())
6103 {
6104 if (!fDelete)
6105 {
6106 setModified(IsModified_MachineData);
6107 mHWData.backupEx();
6108
6109 RTTIMESPEC time;
6110 HWData::GuestProperty prop;
6111 prop.strValue = aValue;
6112 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6113 prop.mFlags = fFlags;
6114 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6115 }
6116 }
6117 else
6118 {
6119 if (it->second.mFlags & (RDONLYHOST))
6120 {
6121 rc = setError(E_ACCESSDENIED,
6122 tr("The property '%ls' cannot be changed by the host"),
6123 aName);
6124 }
6125 else
6126 {
6127 setModified(IsModified_MachineData);
6128 mHWData.backupEx();
6129
6130 /* The backupEx() operation invalidates our iterator,
6131 * so get a new one. */
6132 it = mHWData->mGuestProperties.find(utf8Name);
6133 Assert(it != mHWData->mGuestProperties.end());
6134
6135 if (!fDelete)
6136 {
6137 RTTIMESPEC time;
6138 it->second.strValue = aValue;
6139 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6140 it->second.mFlags = fFlags;
6141 }
6142 else
6143 mHWData->mGuestProperties.erase(it);
6144 }
6145 }
6146
6147 if ( SUCCEEDED(rc)
6148 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6149 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6150 RTSTR_MAX,
6151 utf8Name.c_str(),
6152 RTSTR_MAX,
6153 NULL)
6154 )
6155 )
6156 {
6157 alock.release();
6158
6159 mParent->onGuestPropertyChange(mData->mUuid, aName,
6160 aValue ? aValue : Bstr("").raw(),
6161 aFlags ? aFlags : Bstr("").raw());
6162 }
6163 }
6164 catch (std::bad_alloc &)
6165 {
6166 rc = E_OUTOFMEMORY;
6167 }
6168
6169 return rc;
6170}
6171
6172/**
6173 * Set a property on the VM that that property belongs to.
6174 * @returns E_ACCESSDENIED if the VM process is not available or not
6175 * currently handling queries and the setting should then be done in
6176 * VBoxSVC.
6177 */
6178HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6179 IN_BSTR aFlags)
6180{
6181 HRESULT rc;
6182
6183 try
6184 {
6185 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6186
6187 BSTR dummy = NULL; /* will not be changed (setter) */
6188 LONG64 dummy64;
6189 if (!directControl)
6190 rc = E_ACCESSDENIED;
6191 else
6192 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6193 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6194 true /* isSetter */,
6195 &dummy, &dummy64, &dummy);
6196 }
6197 catch (std::bad_alloc &)
6198 {
6199 rc = E_OUTOFMEMORY;
6200 }
6201
6202 return rc;
6203}
6204#endif // VBOX_WITH_GUEST_PROPS
6205
6206STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6207 IN_BSTR aFlags)
6208{
6209#ifndef VBOX_WITH_GUEST_PROPS
6210 ReturnComNotImplemented();
6211#else // VBOX_WITH_GUEST_PROPS
6212 CheckComArgStrNotEmptyOrNull(aName);
6213 CheckComArgMaybeNull(aFlags);
6214 CheckComArgMaybeNull(aValue);
6215
6216 AutoCaller autoCaller(this);
6217 if (FAILED(autoCaller.rc()))
6218 return autoCaller.rc();
6219
6220 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6221 if (rc == E_ACCESSDENIED)
6222 /* The VM is not running or the service is not (yet) accessible */
6223 rc = setGuestPropertyToService(aName, aValue, aFlags);
6224 return rc;
6225#endif // VBOX_WITH_GUEST_PROPS
6226}
6227
6228STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6229{
6230 return SetGuestProperty(aName, aValue, NULL);
6231}
6232
6233STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6234{
6235 return SetGuestProperty(aName, NULL, NULL);
6236}
6237
6238#ifdef VBOX_WITH_GUEST_PROPS
6239/**
6240 * Enumerate the guest properties in VBoxSVC's internal structures.
6241 */
6242HRESULT Machine::enumerateGuestPropertiesInService
6243 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6244 ComSafeArrayOut(BSTR, aValues),
6245 ComSafeArrayOut(LONG64, aTimestamps),
6246 ComSafeArrayOut(BSTR, aFlags))
6247{
6248 using namespace guestProp;
6249
6250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6251 Utf8Str strPatterns(aPatterns);
6252
6253 HWData::GuestPropertyMap propMap;
6254
6255 /*
6256 * Look for matching patterns and build up a list.
6257 */
6258 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6259 while (it != mHWData->mGuestProperties.end())
6260 {
6261 if ( strPatterns.isEmpty()
6262 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6263 RTSTR_MAX,
6264 it->first.c_str(),
6265 RTSTR_MAX,
6266 NULL)
6267 )
6268 {
6269 propMap.insert(*it);
6270 }
6271
6272 it++;
6273 }
6274
6275 alock.release();
6276
6277 /*
6278 * And build up the arrays for returning the property information.
6279 */
6280 size_t cEntries = propMap.size();
6281 SafeArray<BSTR> names(cEntries);
6282 SafeArray<BSTR> values(cEntries);
6283 SafeArray<LONG64> timestamps(cEntries);
6284 SafeArray<BSTR> flags(cEntries);
6285 size_t iProp = 0;
6286
6287 it = propMap.begin();
6288 while (it != propMap.end())
6289 {
6290 char szFlags[MAX_FLAGS_LEN + 1];
6291 it->first.cloneTo(&names[iProp]);
6292 it->second.strValue.cloneTo(&values[iProp]);
6293 timestamps[iProp] = it->second.mTimestamp;
6294 writeFlags(it->second.mFlags, szFlags);
6295 Bstr(szFlags).cloneTo(&flags[iProp++]);
6296 it++;
6297 }
6298 names.detachTo(ComSafeArrayOutArg(aNames));
6299 values.detachTo(ComSafeArrayOutArg(aValues));
6300 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6301 flags.detachTo(ComSafeArrayOutArg(aFlags));
6302 return S_OK;
6303}
6304
6305/**
6306 * Enumerate the properties managed by a VM.
6307 * @returns E_ACCESSDENIED if the VM process is not available or not
6308 * currently handling queries and the setting should then be done in
6309 * VBoxSVC.
6310 */
6311HRESULT Machine::enumerateGuestPropertiesOnVM
6312 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6313 ComSafeArrayOut(BSTR, aValues),
6314 ComSafeArrayOut(LONG64, aTimestamps),
6315 ComSafeArrayOut(BSTR, aFlags))
6316{
6317 HRESULT rc;
6318 ComPtr<IInternalSessionControl> directControl;
6319 directControl = mData->mSession.mDirectControl;
6320
6321 if (!directControl)
6322 rc = E_ACCESSDENIED;
6323 else
6324 rc = directControl->EnumerateGuestProperties
6325 (aPatterns, ComSafeArrayOutArg(aNames),
6326 ComSafeArrayOutArg(aValues),
6327 ComSafeArrayOutArg(aTimestamps),
6328 ComSafeArrayOutArg(aFlags));
6329 return rc;
6330}
6331#endif // VBOX_WITH_GUEST_PROPS
6332
6333STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6334 ComSafeArrayOut(BSTR, aNames),
6335 ComSafeArrayOut(BSTR, aValues),
6336 ComSafeArrayOut(LONG64, aTimestamps),
6337 ComSafeArrayOut(BSTR, aFlags))
6338{
6339#ifndef VBOX_WITH_GUEST_PROPS
6340 ReturnComNotImplemented();
6341#else // VBOX_WITH_GUEST_PROPS
6342 CheckComArgMaybeNull(aPatterns);
6343 CheckComArgOutSafeArrayPointerValid(aNames);
6344 CheckComArgOutSafeArrayPointerValid(aValues);
6345 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6346 CheckComArgOutSafeArrayPointerValid(aFlags);
6347
6348 AutoCaller autoCaller(this);
6349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6350
6351 HRESULT rc = enumerateGuestPropertiesOnVM
6352 (aPatterns, ComSafeArrayOutArg(aNames),
6353 ComSafeArrayOutArg(aValues),
6354 ComSafeArrayOutArg(aTimestamps),
6355 ComSafeArrayOutArg(aFlags));
6356 if (rc == E_ACCESSDENIED)
6357 /* The VM is not running or the service is not (yet) accessible */
6358 rc = enumerateGuestPropertiesInService
6359 (aPatterns, ComSafeArrayOutArg(aNames),
6360 ComSafeArrayOutArg(aValues),
6361 ComSafeArrayOutArg(aTimestamps),
6362 ComSafeArrayOutArg(aFlags));
6363 return rc;
6364#endif // VBOX_WITH_GUEST_PROPS
6365}
6366
6367STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6368 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6369{
6370 MediaData::AttachmentList atts;
6371
6372 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6373 if (FAILED(rc)) return rc;
6374
6375 SafeIfaceArray<IMediumAttachment> attachments(atts);
6376 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6377
6378 return S_OK;
6379}
6380
6381STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6382 LONG aControllerPort,
6383 LONG aDevice,
6384 IMediumAttachment **aAttachment)
6385{
6386 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6387 aControllerName, aControllerPort, aDevice));
6388
6389 CheckComArgStrNotEmptyOrNull(aControllerName);
6390 CheckComArgOutPointerValid(aAttachment);
6391
6392 AutoCaller autoCaller(this);
6393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6394
6395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6396
6397 *aAttachment = NULL;
6398
6399 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6400 aControllerName,
6401 aControllerPort,
6402 aDevice);
6403 if (pAttach.isNull())
6404 return setError(VBOX_E_OBJECT_NOT_FOUND,
6405 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6406 aDevice, aControllerPort, aControllerName);
6407
6408 pAttach.queryInterfaceTo(aAttachment);
6409
6410 return S_OK;
6411}
6412
6413STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6414 StorageBus_T aConnectionType,
6415 IStorageController **controller)
6416{
6417 CheckComArgStrNotEmptyOrNull(aName);
6418
6419 if ( (aConnectionType <= StorageBus_Null)
6420 || (aConnectionType > StorageBus_USB))
6421 return setError(E_INVALIDARG,
6422 tr("Invalid connection type: %d"),
6423 aConnectionType);
6424
6425 AutoCaller autoCaller(this);
6426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6427
6428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6429
6430 HRESULT rc = checkStateDependency(MutableStateDep);
6431 if (FAILED(rc)) return rc;
6432
6433 /* try to find one with the name first. */
6434 ComObjPtr<StorageController> ctrl;
6435
6436 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6437 if (SUCCEEDED(rc))
6438 return setError(VBOX_E_OBJECT_IN_USE,
6439 tr("Storage controller named '%ls' already exists"),
6440 aName);
6441
6442 ctrl.createObject();
6443
6444 /* get a new instance number for the storage controller */
6445 ULONG ulInstance = 0;
6446 bool fBootable = true;
6447 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6448 it != mStorageControllers->end();
6449 ++it)
6450 {
6451 if ((*it)->i_getStorageBus() == aConnectionType)
6452 {
6453 ULONG ulCurInst = (*it)->i_getInstance();
6454
6455 if (ulCurInst >= ulInstance)
6456 ulInstance = ulCurInst + 1;
6457
6458 /* Only one controller of each type can be marked as bootable. */
6459 if ((*it)->i_getBootable())
6460 fBootable = false;
6461 }
6462 }
6463
6464 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6465 if (FAILED(rc)) return rc;
6466
6467 setModified(IsModified_Storage);
6468 mStorageControllers.backup();
6469 mStorageControllers->push_back(ctrl);
6470
6471 ctrl.queryInterfaceTo(controller);
6472
6473 /* inform the direct session if any */
6474 alock.release();
6475 onStorageControllerChange();
6476
6477 return S_OK;
6478}
6479
6480STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6481 IStorageController **aStorageController)
6482{
6483 CheckComArgStrNotEmptyOrNull(aName);
6484
6485 AutoCaller autoCaller(this);
6486 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6487
6488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6489
6490 ComObjPtr<StorageController> ctrl;
6491
6492 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6493 if (SUCCEEDED(rc))
6494 ctrl.queryInterfaceTo(aStorageController);
6495
6496 return rc;
6497}
6498
6499STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6500 IStorageController **aStorageController)
6501{
6502 AutoCaller autoCaller(this);
6503 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6504
6505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6506
6507 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6508 it != mStorageControllers->end();
6509 ++it)
6510 {
6511 if ((*it)->i_getInstance() == aInstance)
6512 {
6513 (*it).queryInterfaceTo(aStorageController);
6514 return S_OK;
6515 }
6516 }
6517
6518 return setError(VBOX_E_OBJECT_NOT_FOUND,
6519 tr("Could not find a storage controller with instance number '%lu'"),
6520 aInstance);
6521}
6522
6523STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6524{
6525 AutoCaller autoCaller(this);
6526 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6527
6528 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6529
6530 HRESULT rc = checkStateDependency(MutableStateDep);
6531 if (FAILED(rc)) return rc;
6532
6533 ComObjPtr<StorageController> ctrl;
6534
6535 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6536 if (SUCCEEDED(rc))
6537 {
6538 /* Ensure that only one controller of each type is marked as bootable. */
6539 if (fBootable == TRUE)
6540 {
6541 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6542 it != mStorageControllers->end();
6543 ++it)
6544 {
6545 ComObjPtr<StorageController> aCtrl = (*it);
6546
6547 if ( (aCtrl->i_getName() != Utf8Str(aName))
6548 && aCtrl->i_getBootable() == TRUE
6549 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6550 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6551 {
6552 aCtrl->i_setBootable(FALSE);
6553 break;
6554 }
6555 }
6556 }
6557
6558 if (SUCCEEDED(rc))
6559 {
6560 ctrl->i_setBootable(fBootable);
6561 setModified(IsModified_Storage);
6562 }
6563 }
6564
6565 if (SUCCEEDED(rc))
6566 {
6567 /* inform the direct session if any */
6568 alock.release();
6569 onStorageControllerChange();
6570 }
6571
6572 return rc;
6573}
6574
6575STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6576{
6577 CheckComArgStrNotEmptyOrNull(aName);
6578
6579 AutoCaller autoCaller(this);
6580 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6581
6582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6583
6584 HRESULT rc = checkStateDependency(MutableStateDep);
6585 if (FAILED(rc)) return rc;
6586
6587 ComObjPtr<StorageController> ctrl;
6588 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6589 if (FAILED(rc)) return rc;
6590
6591 {
6592 /* find all attached devices to the appropriate storage controller and detach them all */
6593 // make a temporary list because detachDevice invalidates iterators into
6594 // mMediaData->mAttachments
6595 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6596
6597 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6598 it != llAttachments2.end();
6599 ++it)
6600 {
6601 MediumAttachment *pAttachTemp = *it;
6602
6603 AutoCaller localAutoCaller(pAttachTemp);
6604 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6605
6606 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6607
6608 if (pAttachTemp->getControllerName() == aName)
6609 {
6610 rc = detachDevice(pAttachTemp, alock, NULL);
6611 if (FAILED(rc)) return rc;
6612 }
6613 }
6614 }
6615
6616 /* We can remove it now. */
6617 setModified(IsModified_Storage);
6618 mStorageControllers.backup();
6619
6620 ctrl->i_unshare();
6621
6622 mStorageControllers->remove(ctrl);
6623
6624 /* inform the direct session if any */
6625 alock.release();
6626 onStorageControllerChange();
6627
6628 return S_OK;
6629}
6630
6631STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6632 IUSBController **controller)
6633{
6634 if ( (aType <= USBControllerType_Null)
6635 || (aType >= USBControllerType_Last))
6636 return setError(E_INVALIDARG,
6637 tr("Invalid USB controller type: %d"),
6638 aType);
6639
6640 AutoCaller autoCaller(this);
6641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6642
6643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6644
6645 HRESULT rc = checkStateDependency(MutableStateDep);
6646 if (FAILED(rc)) return rc;
6647
6648 /* try to find one with the same type first. */
6649 ComObjPtr<USBController> ctrl;
6650
6651 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6652 if (SUCCEEDED(rc))
6653 return setError(VBOX_E_OBJECT_IN_USE,
6654 tr("USB controller named '%ls' already exists"),
6655 aName);
6656
6657 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6658 ULONG maxInstances;
6659 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6660 if (FAILED(rc))
6661 return rc;
6662
6663 ULONG cInstances = getUSBControllerCountByType(aType);
6664 if (cInstances >= maxInstances)
6665 return setError(E_INVALIDARG,
6666 tr("Too many USB controllers of this type"));
6667
6668 ctrl.createObject();
6669
6670 rc = ctrl->init(this, aName, aType);
6671 if (FAILED(rc)) return rc;
6672
6673 setModified(IsModified_USB);
6674 mUSBControllers.backup();
6675 mUSBControllers->push_back(ctrl);
6676
6677 ctrl.queryInterfaceTo(controller);
6678
6679 /* inform the direct session if any */
6680 alock.release();
6681 onUSBControllerChange();
6682
6683 return S_OK;
6684}
6685
6686STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6687{
6688 CheckComArgStrNotEmptyOrNull(aName);
6689
6690 AutoCaller autoCaller(this);
6691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6692
6693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6694
6695 ComObjPtr<USBController> ctrl;
6696
6697 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6698 if (SUCCEEDED(rc))
6699 ctrl.queryInterfaceTo(aUSBController);
6700
6701 return rc;
6702}
6703
6704STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6705 ULONG *aControllers)
6706{
6707 CheckComArgOutPointerValid(aControllers);
6708
6709 if ( (aType <= USBControllerType_Null)
6710 || (aType >= USBControllerType_Last))
6711 return setError(E_INVALIDARG,
6712 tr("Invalid USB controller type: %d"),
6713 aType);
6714
6715 AutoCaller autoCaller(this);
6716 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6717
6718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6719
6720 ComObjPtr<USBController> ctrl;
6721
6722 *aControllers = getUSBControllerCountByType(aType);
6723
6724 return S_OK;
6725}
6726
6727STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6728{
6729 CheckComArgStrNotEmptyOrNull(aName);
6730
6731 AutoCaller autoCaller(this);
6732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6733
6734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6735
6736 HRESULT rc = checkStateDependency(MutableStateDep);
6737 if (FAILED(rc)) return rc;
6738
6739 ComObjPtr<USBController> ctrl;
6740 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6741 if (FAILED(rc)) return rc;
6742
6743 setModified(IsModified_USB);
6744 mUSBControllers.backup();
6745
6746 ctrl->unshare();
6747
6748 mUSBControllers->remove(ctrl);
6749
6750 /* inform the direct session if any */
6751 alock.release();
6752 onUSBControllerChange();
6753
6754 return S_OK;
6755}
6756
6757STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6758 ULONG *puOriginX,
6759 ULONG *puOriginY,
6760 ULONG *puWidth,
6761 ULONG *puHeight,
6762 BOOL *pfEnabled)
6763{
6764 LogFlowThisFunc(("\n"));
6765
6766 CheckComArgNotNull(puOriginX);
6767 CheckComArgNotNull(puOriginY);
6768 CheckComArgNotNull(puWidth);
6769 CheckComArgNotNull(puHeight);
6770 CheckComArgNotNull(pfEnabled);
6771
6772 uint32_t u32OriginX= 0;
6773 uint32_t u32OriginY= 0;
6774 uint32_t u32Width = 0;
6775 uint32_t u32Height = 0;
6776 uint16_t u16Flags = 0;
6777
6778 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6779 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6780 if (RT_FAILURE(vrc))
6781 {
6782#ifdef RT_OS_WINDOWS
6783 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6784 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6785 * So just assign fEnable to TRUE again.
6786 * The right fix would be to change GUI API wrappers to make sure that parameters
6787 * are changed only if API succeeds.
6788 */
6789 *pfEnabled = TRUE;
6790#endif
6791 return setError(VBOX_E_IPRT_ERROR,
6792 tr("Saved guest size is not available (%Rrc)"),
6793 vrc);
6794 }
6795
6796 *puOriginX = u32OriginX;
6797 *puOriginY = u32OriginY;
6798 *puWidth = u32Width;
6799 *puHeight = u32Height;
6800 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6801
6802 return S_OK;
6803}
6804
6805STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6806{
6807 LogFlowThisFunc(("\n"));
6808
6809 CheckComArgNotNull(aSize);
6810 CheckComArgNotNull(aWidth);
6811 CheckComArgNotNull(aHeight);
6812
6813 if (aScreenId != 0)
6814 return E_NOTIMPL;
6815
6816 AutoCaller autoCaller(this);
6817 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6818
6819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6820
6821 uint8_t *pu8Data = NULL;
6822 uint32_t cbData = 0;
6823 uint32_t u32Width = 0;
6824 uint32_t u32Height = 0;
6825
6826 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6827
6828 if (RT_FAILURE(vrc))
6829 return setError(VBOX_E_IPRT_ERROR,
6830 tr("Saved screenshot data is not available (%Rrc)"),
6831 vrc);
6832
6833 *aSize = cbData;
6834 *aWidth = u32Width;
6835 *aHeight = u32Height;
6836
6837 freeSavedDisplayScreenshot(pu8Data);
6838
6839 return S_OK;
6840}
6841
6842STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6843{
6844 LogFlowThisFunc(("\n"));
6845
6846 CheckComArgNotNull(aWidth);
6847 CheckComArgNotNull(aHeight);
6848 CheckComArgOutSafeArrayPointerValid(aData);
6849
6850 if (aScreenId != 0)
6851 return E_NOTIMPL;
6852
6853 AutoCaller autoCaller(this);
6854 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6855
6856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6857
6858 uint8_t *pu8Data = NULL;
6859 uint32_t cbData = 0;
6860 uint32_t u32Width = 0;
6861 uint32_t u32Height = 0;
6862
6863 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6864
6865 if (RT_FAILURE(vrc))
6866 return setError(VBOX_E_IPRT_ERROR,
6867 tr("Saved screenshot data is not available (%Rrc)"),
6868 vrc);
6869
6870 *aWidth = u32Width;
6871 *aHeight = u32Height;
6872
6873 com::SafeArray<BYTE> bitmap(cbData);
6874 /* Convert pixels to format expected by the API caller. */
6875 if (aBGR)
6876 {
6877 /* [0] B, [1] G, [2] R, [3] A. */
6878 for (unsigned i = 0; i < cbData; i += 4)
6879 {
6880 bitmap[i] = pu8Data[i];
6881 bitmap[i + 1] = pu8Data[i + 1];
6882 bitmap[i + 2] = pu8Data[i + 2];
6883 bitmap[i + 3] = 0xff;
6884 }
6885 }
6886 else
6887 {
6888 /* [0] R, [1] G, [2] B, [3] A. */
6889 for (unsigned i = 0; i < cbData; i += 4)
6890 {
6891 bitmap[i] = pu8Data[i + 2];
6892 bitmap[i + 1] = pu8Data[i + 1];
6893 bitmap[i + 2] = pu8Data[i];
6894 bitmap[i + 3] = 0xff;
6895 }
6896 }
6897 bitmap.detachTo(ComSafeArrayOutArg(aData));
6898
6899 freeSavedDisplayScreenshot(pu8Data);
6900
6901 return S_OK;
6902}
6903
6904
6905STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6906{
6907 LogFlowThisFunc(("\n"));
6908
6909 CheckComArgNotNull(aWidth);
6910 CheckComArgNotNull(aHeight);
6911 CheckComArgOutSafeArrayPointerValid(aData);
6912
6913 if (aScreenId != 0)
6914 return E_NOTIMPL;
6915
6916 AutoCaller autoCaller(this);
6917 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6918
6919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6920
6921 uint8_t *pu8Data = NULL;
6922 uint32_t cbData = 0;
6923 uint32_t u32Width = 0;
6924 uint32_t u32Height = 0;
6925
6926 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6927
6928 if (RT_FAILURE(vrc))
6929 return setError(VBOX_E_IPRT_ERROR,
6930 tr("Saved screenshot data is not available (%Rrc)"),
6931 vrc);
6932
6933 *aWidth = u32Width;
6934 *aHeight = u32Height;
6935
6936 HRESULT rc = S_OK;
6937 uint8_t *pu8PNG = NULL;
6938 uint32_t cbPNG = 0;
6939 uint32_t cxPNG = 0;
6940 uint32_t cyPNG = 0;
6941
6942 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6943
6944 if (RT_SUCCESS(vrc))
6945 {
6946 com::SafeArray<BYTE> screenData(cbPNG);
6947 screenData.initFrom(pu8PNG, cbPNG);
6948 if (pu8PNG)
6949 RTMemFree(pu8PNG);
6950 screenData.detachTo(ComSafeArrayOutArg(aData));
6951 }
6952 else
6953 {
6954 if (pu8PNG)
6955 RTMemFree(pu8PNG);
6956 return setError(VBOX_E_IPRT_ERROR,
6957 tr("Could not convert screenshot to PNG (%Rrc)"),
6958 vrc);
6959 }
6960
6961 freeSavedDisplayScreenshot(pu8Data);
6962
6963 return rc;
6964}
6965
6966STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6967{
6968 LogFlowThisFunc(("\n"));
6969
6970 CheckComArgNotNull(aSize);
6971 CheckComArgNotNull(aWidth);
6972 CheckComArgNotNull(aHeight);
6973
6974 if (aScreenId != 0)
6975 return E_NOTIMPL;
6976
6977 AutoCaller autoCaller(this);
6978 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6979
6980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6981
6982 uint8_t *pu8Data = NULL;
6983 uint32_t cbData = 0;
6984 uint32_t u32Width = 0;
6985 uint32_t u32Height = 0;
6986
6987 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6988
6989 if (RT_FAILURE(vrc))
6990 return setError(VBOX_E_IPRT_ERROR,
6991 tr("Saved screenshot data is not available (%Rrc)"),
6992 vrc);
6993
6994 *aSize = cbData;
6995 *aWidth = u32Width;
6996 *aHeight = u32Height;
6997
6998 freeSavedDisplayScreenshot(pu8Data);
6999
7000 return S_OK;
7001}
7002
7003STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
7004{
7005 LogFlowThisFunc(("\n"));
7006
7007 CheckComArgNotNull(aWidth);
7008 CheckComArgNotNull(aHeight);
7009 CheckComArgOutSafeArrayPointerValid(aData);
7010
7011 if (aScreenId != 0)
7012 return E_NOTIMPL;
7013
7014 AutoCaller autoCaller(this);
7015 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7016
7017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7018
7019 uint8_t *pu8Data = NULL;
7020 uint32_t cbData = 0;
7021 uint32_t u32Width = 0;
7022 uint32_t u32Height = 0;
7023
7024 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
7025
7026 if (RT_FAILURE(vrc))
7027 return setError(VBOX_E_IPRT_ERROR,
7028 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
7029 vrc);
7030
7031 *aWidth = u32Width;
7032 *aHeight = u32Height;
7033
7034 com::SafeArray<BYTE> png(cbData);
7035 png.initFrom(pu8Data, cbData);
7036 png.detachTo(ComSafeArrayOutArg(aData));
7037
7038 freeSavedDisplayScreenshot(pu8Data);
7039
7040 return S_OK;
7041}
7042
7043STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
7044{
7045 HRESULT rc = S_OK;
7046 LogFlowThisFunc(("\n"));
7047
7048 AutoCaller autoCaller(this);
7049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7050
7051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7052
7053 if (!mHWData->mCPUHotPlugEnabled)
7054 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7055
7056 if (aCpu >= mHWData->mCPUCount)
7057 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
7058
7059 if (mHWData->mCPUAttached[aCpu])
7060 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
7061
7062 alock.release();
7063 rc = onCPUChange(aCpu, false);
7064 alock.acquire();
7065 if (FAILED(rc)) return rc;
7066
7067 setModified(IsModified_MachineData);
7068 mHWData.backup();
7069 mHWData->mCPUAttached[aCpu] = true;
7070
7071 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7072 if (Global::IsOnline(mData->mMachineState))
7073 saveSettings(NULL);
7074
7075 return S_OK;
7076}
7077
7078STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
7079{
7080 HRESULT rc = S_OK;
7081 LogFlowThisFunc(("\n"));
7082
7083 AutoCaller autoCaller(this);
7084 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7085
7086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7087
7088 if (!mHWData->mCPUHotPlugEnabled)
7089 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7090
7091 if (aCpu >= SchemaDefs::MaxCPUCount)
7092 return setError(E_INVALIDARG,
7093 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7094 SchemaDefs::MaxCPUCount);
7095
7096 if (!mHWData->mCPUAttached[aCpu])
7097 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7098
7099 /* CPU 0 can't be detached */
7100 if (aCpu == 0)
7101 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7102
7103 alock.release();
7104 rc = onCPUChange(aCpu, true);
7105 alock.acquire();
7106 if (FAILED(rc)) return rc;
7107
7108 setModified(IsModified_MachineData);
7109 mHWData.backup();
7110 mHWData->mCPUAttached[aCpu] = false;
7111
7112 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7113 if (Global::IsOnline(mData->mMachineState))
7114 saveSettings(NULL);
7115
7116 return S_OK;
7117}
7118
7119STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7120{
7121 LogFlowThisFunc(("\n"));
7122
7123 CheckComArgNotNull(aCpuAttached);
7124
7125 *aCpuAttached = false;
7126
7127 AutoCaller autoCaller(this);
7128 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7129
7130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7131
7132 /* If hotplug is enabled the CPU is always enabled. */
7133 if (!mHWData->mCPUHotPlugEnabled)
7134 {
7135 if (aCpu < mHWData->mCPUCount)
7136 *aCpuAttached = true;
7137 }
7138 else
7139 {
7140 if (aCpu < SchemaDefs::MaxCPUCount)
7141 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7142 }
7143
7144 return S_OK;
7145}
7146
7147STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7148{
7149 CheckComArgOutPointerValid(aName);
7150
7151 AutoCaller autoCaller(this);
7152 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7153
7154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7155
7156 Utf8Str log = queryLogFilename(aIdx);
7157 if (!RTFileExists(log.c_str()))
7158 log.setNull();
7159 log.cloneTo(aName);
7160
7161 return S_OK;
7162}
7163
7164STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7165{
7166 LogFlowThisFunc(("\n"));
7167 CheckComArgOutSafeArrayPointerValid(aData);
7168 if (aSize < 0)
7169 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7170
7171 AutoCaller autoCaller(this);
7172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7173
7174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7175
7176 HRESULT rc = S_OK;
7177 Utf8Str log = queryLogFilename(aIdx);
7178
7179 /* do not unnecessarily hold the lock while doing something which does
7180 * not need the lock and potentially takes a long time. */
7181 alock.release();
7182
7183 /* Limit the chunk size to 32K for now, as that gives better performance
7184 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7185 * One byte expands to approx. 25 bytes of breathtaking XML. */
7186 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7187 com::SafeArray<BYTE> logData(cbData);
7188
7189 RTFILE LogFile;
7190 int vrc = RTFileOpen(&LogFile, log.c_str(),
7191 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7192 if (RT_SUCCESS(vrc))
7193 {
7194 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7195 if (RT_SUCCESS(vrc))
7196 logData.resize(cbData);
7197 else
7198 rc = setError(VBOX_E_IPRT_ERROR,
7199 tr("Could not read log file '%s' (%Rrc)"),
7200 log.c_str(), vrc);
7201 RTFileClose(LogFile);
7202 }
7203 else
7204 rc = setError(VBOX_E_IPRT_ERROR,
7205 tr("Could not open log file '%s' (%Rrc)"),
7206 log.c_str(), vrc);
7207
7208 if (FAILED(rc))
7209 logData.resize(0);
7210 logData.detachTo(ComSafeArrayOutArg(aData));
7211
7212 return rc;
7213}
7214
7215
7216/**
7217 * Currently this method doesn't attach device to the running VM,
7218 * just makes sure it's plugged on next VM start.
7219 */
7220STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7221{
7222 AutoCaller autoCaller(this);
7223 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7224
7225 // lock scope
7226 {
7227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7228
7229 HRESULT rc = checkStateDependency(MutableStateDep);
7230 if (FAILED(rc)) return rc;
7231
7232 ChipsetType_T aChipset = ChipsetType_PIIX3;
7233 COMGETTER(ChipsetType)(&aChipset);
7234
7235 if (aChipset != ChipsetType_ICH9)
7236 {
7237 return setError(E_INVALIDARG,
7238 tr("Host PCI attachment only supported with ICH9 chipset"));
7239 }
7240
7241 // check if device with this host PCI address already attached
7242 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7243 it != mHWData->mPCIDeviceAssignments.end();
7244 ++it)
7245 {
7246 LONG iHostAddress = -1;
7247 ComPtr<PCIDeviceAttachment> pAttach;
7248 pAttach = *it;
7249 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7250 if (iHostAddress == hostAddress)
7251 return setError(E_INVALIDARG,
7252 tr("Device with host PCI address already attached to this VM"));
7253 }
7254
7255 ComObjPtr<PCIDeviceAttachment> pda;
7256 char name[32];
7257
7258 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7259 Bstr bname(name);
7260 pda.createObject();
7261 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7262 setModified(IsModified_MachineData);
7263 mHWData.backup();
7264 mHWData->mPCIDeviceAssignments.push_back(pda);
7265 }
7266
7267 return S_OK;
7268}
7269
7270/**
7271 * Currently this method doesn't detach device from the running VM,
7272 * just makes sure it's not plugged on next VM start.
7273 */
7274STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7275{
7276 AutoCaller autoCaller(this);
7277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7278
7279 ComObjPtr<PCIDeviceAttachment> pAttach;
7280 bool fRemoved = false;
7281 HRESULT rc;
7282
7283 // lock scope
7284 {
7285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7286
7287 rc = checkStateDependency(MutableStateDep);
7288 if (FAILED(rc)) return rc;
7289
7290 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7291 it != mHWData->mPCIDeviceAssignments.end();
7292 ++it)
7293 {
7294 LONG iHostAddress = -1;
7295 pAttach = *it;
7296 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7297 if (iHostAddress != -1 && iHostAddress == hostAddress)
7298 {
7299 setModified(IsModified_MachineData);
7300 mHWData.backup();
7301 mHWData->mPCIDeviceAssignments.remove(pAttach);
7302 fRemoved = true;
7303 break;
7304 }
7305 }
7306 }
7307
7308
7309 /* Fire event outside of the lock */
7310 if (fRemoved)
7311 {
7312 Assert(!pAttach.isNull());
7313 ComPtr<IEventSource> es;
7314 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7315 Assert(SUCCEEDED(rc));
7316 Bstr mid;
7317 rc = this->COMGETTER(Id)(mid.asOutParam());
7318 Assert(SUCCEEDED(rc));
7319 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7320 }
7321
7322 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7323 tr("No host PCI device %08x attached"),
7324 hostAddress
7325 );
7326}
7327
7328STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7329{
7330 CheckComArgOutSafeArrayPointerValid(aAssignments);
7331
7332 AutoCaller autoCaller(this);
7333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7334
7335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7336
7337 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7338 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7339
7340 return S_OK;
7341}
7342
7343STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7344{
7345 CheckComArgOutPointerValid(aBandwidthControl);
7346
7347 AutoCaller autoCaller(this);
7348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7349
7350 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7351
7352 return S_OK;
7353}
7354
7355STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7356{
7357 CheckComArgOutPointerValid(pfEnabled);
7358 AutoCaller autoCaller(this);
7359 HRESULT hrc = autoCaller.rc();
7360 if (SUCCEEDED(hrc))
7361 {
7362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7363 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7364 }
7365 return hrc;
7366}
7367
7368STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7369{
7370 AutoCaller autoCaller(this);
7371 HRESULT hrc = autoCaller.rc();
7372 if (SUCCEEDED(hrc))
7373 {
7374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7375 hrc = checkStateDependency(MutableStateDep);
7376 if (SUCCEEDED(hrc))
7377 {
7378 hrc = mHWData.backupEx();
7379 if (SUCCEEDED(hrc))
7380 {
7381 setModified(IsModified_MachineData);
7382 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7383 }
7384 }
7385 }
7386 return hrc;
7387}
7388
7389STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7390{
7391 CheckComArgOutPointerValid(pbstrConfig);
7392 AutoCaller autoCaller(this);
7393 HRESULT hrc = autoCaller.rc();
7394 if (SUCCEEDED(hrc))
7395 {
7396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7397 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7398 }
7399 return hrc;
7400}
7401
7402STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7403{
7404 CheckComArgStr(bstrConfig);
7405 AutoCaller autoCaller(this);
7406 HRESULT hrc = autoCaller.rc();
7407 if (SUCCEEDED(hrc))
7408 {
7409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7410 hrc = checkStateDependency(MutableStateDep);
7411 if (SUCCEEDED(hrc))
7412 {
7413 hrc = mHWData.backupEx();
7414 if (SUCCEEDED(hrc))
7415 {
7416 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7417 if (SUCCEEDED(hrc))
7418 setModified(IsModified_MachineData);
7419 }
7420 }
7421 }
7422 return hrc;
7423
7424}
7425
7426STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7427{
7428 CheckComArgOutPointerValid(pfAllow);
7429 AutoCaller autoCaller(this);
7430 HRESULT hrc = autoCaller.rc();
7431 if (SUCCEEDED(hrc))
7432 {
7433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7434 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7435 }
7436 return hrc;
7437}
7438
7439STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7440{
7441 AutoCaller autoCaller(this);
7442 HRESULT hrc = autoCaller.rc();
7443 if (SUCCEEDED(hrc))
7444 {
7445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7446 hrc = checkStateDependency(MutableStateDep);
7447 if (SUCCEEDED(hrc))
7448 {
7449 hrc = mHWData.backupEx();
7450 if (SUCCEEDED(hrc))
7451 {
7452 setModified(IsModified_MachineData);
7453 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7454 }
7455 }
7456 }
7457 return hrc;
7458}
7459
7460STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7461{
7462 CheckComArgOutPointerValid(pfEnabled);
7463 AutoCaller autoCaller(this);
7464 HRESULT hrc = autoCaller.rc();
7465 if (SUCCEEDED(hrc))
7466 {
7467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7468 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7469 }
7470 return hrc;
7471}
7472
7473STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7474{
7475 AutoCaller autoCaller(this);
7476 HRESULT hrc = autoCaller.rc();
7477 if (SUCCEEDED(hrc))
7478 {
7479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7480 hrc = checkStateDependency(MutableStateDep);
7481 if ( SUCCEEDED(hrc)
7482 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7483 {
7484 AutostartDb *autostartDb = mParent->getAutostartDb();
7485 int vrc;
7486
7487 if (fEnabled)
7488 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7489 else
7490 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7491
7492 if (RT_SUCCESS(vrc))
7493 {
7494 hrc = mHWData.backupEx();
7495 if (SUCCEEDED(hrc))
7496 {
7497 setModified(IsModified_MachineData);
7498 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7499 }
7500 }
7501 else if (vrc == VERR_NOT_SUPPORTED)
7502 hrc = setError(VBOX_E_NOT_SUPPORTED,
7503 tr("The VM autostart feature is not supported on this platform"));
7504 else if (vrc == VERR_PATH_NOT_FOUND)
7505 hrc = setError(E_FAIL,
7506 tr("The path to the autostart database is not set"));
7507 else
7508 hrc = setError(E_UNEXPECTED,
7509 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7510 fEnabled ? "Adding" : "Removing",
7511 mUserData->s.strName.c_str(), vrc);
7512 }
7513 }
7514 return hrc;
7515}
7516
7517STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7518{
7519 CheckComArgOutPointerValid(puDelay);
7520 AutoCaller autoCaller(this);
7521 HRESULT hrc = autoCaller.rc();
7522 if (SUCCEEDED(hrc))
7523 {
7524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7525 *puDelay = mHWData->mAutostart.uAutostartDelay;
7526 }
7527 return hrc;
7528}
7529
7530STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7531{
7532 AutoCaller autoCaller(this);
7533 HRESULT hrc = autoCaller.rc();
7534 if (SUCCEEDED(hrc))
7535 {
7536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7537 hrc = checkStateDependency(MutableStateDep);
7538 if (SUCCEEDED(hrc))
7539 {
7540 hrc = mHWData.backupEx();
7541 if (SUCCEEDED(hrc))
7542 {
7543 setModified(IsModified_MachineData);
7544 mHWData->mAutostart.uAutostartDelay = uDelay;
7545 }
7546 }
7547 }
7548 return hrc;
7549}
7550
7551STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7552{
7553 CheckComArgOutPointerValid(penmAutostopType);
7554 AutoCaller autoCaller(this);
7555 HRESULT hrc = autoCaller.rc();
7556 if (SUCCEEDED(hrc))
7557 {
7558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7559 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7560 }
7561 return hrc;
7562}
7563
7564STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7565{
7566 AutoCaller autoCaller(this);
7567 HRESULT hrc = autoCaller.rc();
7568 if (SUCCEEDED(hrc))
7569 {
7570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7571 hrc = checkStateDependency(MutableStateDep);
7572 if ( SUCCEEDED(hrc)
7573 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7574 {
7575 AutostartDb *autostartDb = mParent->getAutostartDb();
7576 int vrc;
7577
7578 if (enmAutostopType != AutostopType_Disabled)
7579 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7580 else
7581 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7582
7583 if (RT_SUCCESS(vrc))
7584 {
7585 hrc = mHWData.backupEx();
7586 if (SUCCEEDED(hrc))
7587 {
7588 setModified(IsModified_MachineData);
7589 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7590 }
7591 }
7592 else if (vrc == VERR_NOT_SUPPORTED)
7593 hrc = setError(VBOX_E_NOT_SUPPORTED,
7594 tr("The VM autostop feature is not supported on this platform"));
7595 else if (vrc == VERR_PATH_NOT_FOUND)
7596 hrc = setError(E_FAIL,
7597 tr("The path to the autostart database is not set"));
7598 else
7599 hrc = setError(E_UNEXPECTED,
7600 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7601 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7602 mUserData->s.strName.c_str(), vrc);
7603 }
7604 }
7605 return hrc;
7606}
7607
7608STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7609{
7610 CheckComArgOutPointerValid(aDefaultFrontend);
7611 AutoCaller autoCaller(this);
7612 HRESULT hrc = autoCaller.rc();
7613 if (SUCCEEDED(hrc))
7614 {
7615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7616 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7617 }
7618 return hrc;
7619}
7620
7621STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7622{
7623 CheckComArgStr(aDefaultFrontend);
7624 AutoCaller autoCaller(this);
7625 HRESULT hrc = autoCaller.rc();
7626 if (SUCCEEDED(hrc))
7627 {
7628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7629 hrc = checkStateDependency(MutableOrSavedStateDep);
7630 if (SUCCEEDED(hrc))
7631 {
7632 hrc = mHWData.backupEx();
7633 if (SUCCEEDED(hrc))
7634 {
7635 setModified(IsModified_MachineData);
7636 mHWData->mDefaultFrontend = aDefaultFrontend;
7637 }
7638 }
7639 }
7640 return hrc;
7641}
7642
7643STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7644{
7645 CheckComArgSafeArrayNotNull(aIcon);
7646 CheckComArgOutSafeArrayPointerValid(aIcon);
7647 AutoCaller autoCaller(this);
7648 HRESULT hrc = autoCaller.rc();
7649 if (SUCCEEDED(hrc))
7650 {
7651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7652 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7653 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7654 icon.detachTo(ComSafeArrayOutArg(aIcon));
7655 }
7656 return hrc;
7657}
7658
7659STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7660{
7661 CheckComArgSafeArrayNotNull(aIcon);
7662 AutoCaller autoCaller(this);
7663 HRESULT hrc = autoCaller.rc();
7664 if (SUCCEEDED(hrc))
7665 {
7666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7667 hrc = checkStateDependency(MutableOrSavedStateDep);
7668 if (SUCCEEDED(hrc))
7669 {
7670 setModified(IsModified_MachineData);
7671 mUserData.backup();
7672 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7673 mUserData->mIcon.resize(icon.size());
7674 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7675 }
7676 }
7677 return hrc;
7678}
7679
7680STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7681{
7682 CheckComArgOutPointerValid(aAvailable);
7683
7684 AutoCaller autoCaller(this);
7685 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7686
7687#ifdef VBOX_WITH_USB
7688 *aAvailable = true;
7689#else
7690 *aAvailable = false;
7691#endif
7692 return S_OK;
7693}
7694
7695STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7696{
7697 LogFlowFuncEnter();
7698
7699 CheckComArgNotNull(pTarget);
7700 CheckComArgOutPointerValid(pProgress);
7701
7702 /* Convert the options. */
7703 RTCList<CloneOptions_T> optList;
7704 if (options != NULL)
7705 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7706
7707 if (optList.contains(CloneOptions_Link))
7708 {
7709 if (!isSnapshotMachine())
7710 return setError(E_INVALIDARG,
7711 tr("Linked clone can only be created from a snapshot"));
7712 if (mode != CloneMode_MachineState)
7713 return setError(E_INVALIDARG,
7714 tr("Linked clone can only be created for a single machine state"));
7715 }
7716 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7717
7718 AutoCaller autoCaller(this);
7719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7720
7721
7722 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7723
7724 HRESULT rc = pWorker->start(pProgress);
7725
7726 LogFlowFuncLeave();
7727
7728 return rc;
7729}
7730
7731// public methods for internal purposes
7732/////////////////////////////////////////////////////////////////////////////
7733
7734/**
7735 * Adds the given IsModified_* flag to the dirty flags of the machine.
7736 * This must be called either during loadSettings or under the machine write lock.
7737 * @param fl
7738 */
7739void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7740{
7741 mData->flModifications |= fl;
7742 if (fAllowStateModification && isStateModificationAllowed())
7743 mData->mCurrentStateModified = true;
7744}
7745
7746/**
7747 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7748 * care of the write locking.
7749 *
7750 * @param fModifications The flag to add.
7751 */
7752void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7753{
7754 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7755 setModified(fModification, fAllowStateModification);
7756}
7757
7758/**
7759 * Saves the registry entry of this machine to the given configuration node.
7760 *
7761 * @param aEntryNode Node to save the registry entry to.
7762 *
7763 * @note locks this object for reading.
7764 */
7765HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7766{
7767 AutoLimitedCaller autoCaller(this);
7768 AssertComRCReturnRC(autoCaller.rc());
7769
7770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7771
7772 data.uuid = mData->mUuid;
7773 data.strSettingsFile = mData->m_strConfigFile;
7774
7775 return S_OK;
7776}
7777
7778/**
7779 * Calculates the absolute path of the given path taking the directory of the
7780 * machine settings file as the current directory.
7781 *
7782 * @param aPath Path to calculate the absolute path for.
7783 * @param aResult Where to put the result (used only on success, can be the
7784 * same Utf8Str instance as passed in @a aPath).
7785 * @return IPRT result.
7786 *
7787 * @note Locks this object for reading.
7788 */
7789int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7790{
7791 AutoCaller autoCaller(this);
7792 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7793
7794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7795
7796 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7797
7798 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7799
7800 strSettingsDir.stripFilename();
7801 char folder[RTPATH_MAX];
7802 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7803 if (RT_SUCCESS(vrc))
7804 aResult = folder;
7805
7806 return vrc;
7807}
7808
7809/**
7810 * Copies strSource to strTarget, making it relative to the machine folder
7811 * if it is a subdirectory thereof, or simply copying it otherwise.
7812 *
7813 * @param strSource Path to evaluate and copy.
7814 * @param strTarget Buffer to receive target path.
7815 *
7816 * @note Locks this object for reading.
7817 */
7818void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7819 Utf8Str &strTarget)
7820{
7821 AutoCaller autoCaller(this);
7822 AssertComRCReturn(autoCaller.rc(), (void)0);
7823
7824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7825
7826 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7827 // use strTarget as a temporary buffer to hold the machine settings dir
7828 strTarget = mData->m_strConfigFileFull;
7829 strTarget.stripFilename();
7830 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7831 {
7832 // is relative: then append what's left
7833 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7834 // for empty paths (only possible for subdirs) use "." to avoid
7835 // triggering default settings for not present config attributes.
7836 if (strTarget.isEmpty())
7837 strTarget = ".";
7838 }
7839 else
7840 // is not relative: then overwrite
7841 strTarget = strSource;
7842}
7843
7844/**
7845 * Returns the full path to the machine's log folder in the
7846 * \a aLogFolder argument.
7847 */
7848void Machine::getLogFolder(Utf8Str &aLogFolder)
7849{
7850 AutoCaller autoCaller(this);
7851 AssertComRCReturnVoid(autoCaller.rc());
7852
7853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7854
7855 char szTmp[RTPATH_MAX];
7856 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7857 if (RT_SUCCESS(vrc))
7858 {
7859 if (szTmp[0] && !mUserData.isNull())
7860 {
7861 char szTmp2[RTPATH_MAX];
7862 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7863 if (RT_SUCCESS(vrc))
7864 aLogFolder = BstrFmt("%s%c%s",
7865 szTmp2,
7866 RTPATH_DELIMITER,
7867 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7868 }
7869 else
7870 vrc = VERR_PATH_IS_RELATIVE;
7871 }
7872
7873 if (RT_FAILURE(vrc))
7874 {
7875 // fallback if VBOX_USER_LOGHOME is not set or invalid
7876 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7877 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7878 aLogFolder.append(RTPATH_DELIMITER);
7879 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7880 }
7881}
7882
7883/**
7884 * Returns the full path to the machine's log file for an given index.
7885 */
7886Utf8Str Machine::queryLogFilename(ULONG idx)
7887{
7888 Utf8Str logFolder;
7889 getLogFolder(logFolder);
7890 Assert(logFolder.length());
7891 Utf8Str log;
7892 if (idx == 0)
7893 log = Utf8StrFmt("%s%cVBox.log",
7894 logFolder.c_str(), RTPATH_DELIMITER);
7895 else
7896 log = Utf8StrFmt("%s%cVBox.log.%d",
7897 logFolder.c_str(), RTPATH_DELIMITER, idx);
7898 return log;
7899}
7900
7901/**
7902 * Composes a unique saved state filename based on the current system time. The filename is
7903 * granular to the second so this will work so long as no more than one snapshot is taken on
7904 * a machine per second.
7905 *
7906 * Before version 4.1, we used this formula for saved state files:
7907 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7908 * which no longer works because saved state files can now be shared between the saved state of the
7909 * "saved" machine and an online snapshot, and the following would cause problems:
7910 * 1) save machine
7911 * 2) create online snapshot from that machine state --> reusing saved state file
7912 * 3) save machine again --> filename would be reused, breaking the online snapshot
7913 *
7914 * So instead we now use a timestamp.
7915 *
7916 * @param str
7917 */
7918void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7919{
7920 AutoCaller autoCaller(this);
7921 AssertComRCReturnVoid(autoCaller.rc());
7922
7923 {
7924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7925 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7926 }
7927
7928 RTTIMESPEC ts;
7929 RTTimeNow(&ts);
7930 RTTIME time;
7931 RTTimeExplode(&time, &ts);
7932
7933 strStateFilePath += RTPATH_DELIMITER;
7934 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7935 time.i32Year, time.u8Month, time.u8MonthDay,
7936 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7937}
7938
7939/**
7940 * Returns the full path to the default video capture file.
7941 */
7942void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7943{
7944 AutoCaller autoCaller(this);
7945 AssertComRCReturnVoid(autoCaller.rc());
7946
7947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7948
7949 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7950 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7951 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7952}
7953
7954/**
7955 * Returns whether at least one USB controller is present for the VM.
7956 */
7957bool Machine::isUSBControllerPresent()
7958{
7959 AutoCaller autoCaller(this);
7960 AssertComRCReturn(autoCaller.rc(), false);
7961
7962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7963
7964 return (mUSBControllers->size() > 0);
7965}
7966
7967/**
7968 * @note Locks this object for writing, calls the client process
7969 * (inside the lock).
7970 */
7971HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7972 const Utf8Str &strFrontend,
7973 const Utf8Str &strEnvironment,
7974 ProgressProxy *aProgress)
7975{
7976 LogFlowThisFuncEnter();
7977
7978 AssertReturn(aControl, E_FAIL);
7979 AssertReturn(aProgress, E_FAIL);
7980 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7981
7982 AutoCaller autoCaller(this);
7983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7984
7985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7986
7987 if (!mData->mRegistered)
7988 return setError(E_UNEXPECTED,
7989 tr("The machine '%s' is not registered"),
7990 mUserData->s.strName.c_str());
7991
7992 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7993
7994 if ( mData->mSession.mState == SessionState_Locked
7995 || mData->mSession.mState == SessionState_Spawning
7996 || mData->mSession.mState == SessionState_Unlocking)
7997 return setError(VBOX_E_INVALID_OBJECT_STATE,
7998 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7999 mUserData->s.strName.c_str());
8000
8001 /* may not be busy */
8002 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
8003
8004 /* get the path to the executable */
8005 char szPath[RTPATH_MAX];
8006 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
8007 size_t sz = strlen(szPath);
8008 szPath[sz++] = RTPATH_DELIMITER;
8009 szPath[sz] = 0;
8010 char *cmd = szPath + sz;
8011 sz = sizeof(szPath) - sz;
8012
8013 int vrc = VINF_SUCCESS;
8014 RTPROCESS pid = NIL_RTPROCESS;
8015
8016 RTENV env = RTENV_DEFAULT;
8017
8018 if (!strEnvironment.isEmpty())
8019 {
8020 char *newEnvStr = NULL;
8021
8022 do
8023 {
8024 /* clone the current environment */
8025 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
8026 AssertRCBreakStmt(vrc2, vrc = vrc2);
8027
8028 newEnvStr = RTStrDup(strEnvironment.c_str());
8029 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
8030
8031 /* put new variables to the environment
8032 * (ignore empty variable names here since RTEnv API
8033 * intentionally doesn't do that) */
8034 char *var = newEnvStr;
8035 for (char *p = newEnvStr; *p; ++p)
8036 {
8037 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
8038 {
8039 *p = '\0';
8040 if (*var)
8041 {
8042 char *val = strchr(var, '=');
8043 if (val)
8044 {
8045 *val++ = '\0';
8046 vrc2 = RTEnvSetEx(env, var, val);
8047 }
8048 else
8049 vrc2 = RTEnvUnsetEx(env, var);
8050 if (RT_FAILURE(vrc2))
8051 break;
8052 }
8053 var = p + 1;
8054 }
8055 }
8056 if (RT_SUCCESS(vrc2) && *var)
8057 vrc2 = RTEnvPutEx(env, var);
8058
8059 AssertRCBreakStmt(vrc2, vrc = vrc2);
8060 }
8061 while (0);
8062
8063 if (newEnvStr != NULL)
8064 RTStrFree(newEnvStr);
8065 }
8066
8067#ifdef VBOX_WITH_QTGUI
8068 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
8069 {
8070# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
8071 /* Modify the base path so that we don't need to use ".." below. */
8072 RTPathStripTrailingSlash(szPath);
8073 RTPathStripFilename(szPath);
8074 sz = strlen(szPath);
8075 cmd = szPath + sz;
8076 sz = sizeof(szPath) - sz;
8077
8078#define OSX_APP_NAME "VirtualBoxVM"
8079#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
8080
8081 Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
8082 if ( strAppOverride.contains(".")
8083 || strAppOverride.contains("/")
8084 || strAppOverride.contains("\\")
8085 || strAppOverride.contains(":"))
8086 strAppOverride.setNull();
8087 Utf8Str strAppPath;
8088 if (!strAppOverride.isEmpty())
8089 {
8090 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
8091 Utf8Str strFullPath(szPath);
8092 strFullPath.append(strAppPath);
8093 /* there is a race, but people using this deserve the failure */
8094 if (!RTFileExists(strFullPath.c_str()))
8095 strAppOverride.setNull();
8096 }
8097 if (strAppOverride.isEmpty())
8098 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
8099 const char *VirtualBox_exe = strAppPath.c_str();
8100 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
8101# else
8102 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8103 Assert(sz >= sizeof(VirtualBox_exe));
8104# endif
8105 strcpy(cmd, VirtualBox_exe);
8106
8107 Utf8Str idStr = mData->mUuid.toString();
8108 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8109 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8110 }
8111#else /* !VBOX_WITH_QTGUI */
8112 if (0)
8113 ;
8114#endif /* VBOX_WITH_QTGUI */
8115
8116 else
8117
8118#ifdef VBOX_WITH_VBOXSDL
8119 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8120 {
8121 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8122 Assert(sz >= sizeof(VBoxSDL_exe));
8123 strcpy(cmd, VBoxSDL_exe);
8124
8125 Utf8Str idStr = mData->mUuid.toString();
8126 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8127 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8128 }
8129#else /* !VBOX_WITH_VBOXSDL */
8130 if (0)
8131 ;
8132#endif /* !VBOX_WITH_VBOXSDL */
8133
8134 else
8135
8136#ifdef VBOX_WITH_HEADLESS
8137 if ( strFrontend == "headless"
8138 || strFrontend == "capture"
8139 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8140 )
8141 {
8142 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8143 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8144 * and a VM works even if the server has not been installed.
8145 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8146 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8147 * differently in 4.0 and 3.x.
8148 */
8149 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8150 Assert(sz >= sizeof(VBoxHeadless_exe));
8151 strcpy(cmd, VBoxHeadless_exe);
8152
8153 Utf8Str idStr = mData->mUuid.toString();
8154 /* Leave space for "--capture" arg. */
8155 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8156 "--startvm", idStr.c_str(),
8157 "--vrde", "config",
8158 0, /* For "--capture". */
8159 0 };
8160 if (strFrontend == "capture")
8161 {
8162 unsigned pos = RT_ELEMENTS(args) - 2;
8163 args[pos] = "--capture";
8164 }
8165 vrc = RTProcCreate(szPath, args, env,
8166#ifdef RT_OS_WINDOWS
8167 RTPROC_FLAGS_NO_WINDOW
8168#else
8169 0
8170#endif
8171 , &pid);
8172 }
8173#else /* !VBOX_WITH_HEADLESS */
8174 if (0)
8175 ;
8176#endif /* !VBOX_WITH_HEADLESS */
8177 else
8178 {
8179 RTEnvDestroy(env);
8180 return setError(E_INVALIDARG,
8181 tr("Invalid frontend name: '%s'"),
8182 strFrontend.c_str());
8183 }
8184
8185 RTEnvDestroy(env);
8186
8187 if (RT_FAILURE(vrc))
8188 return setError(VBOX_E_IPRT_ERROR,
8189 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8190 mUserData->s.strName.c_str(), vrc);
8191
8192 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8193
8194 /*
8195 * Note that we don't release the lock here before calling the client,
8196 * because it doesn't need to call us back if called with a NULL argument.
8197 * Releasing the lock here is dangerous because we didn't prepare the
8198 * launch data yet, but the client we've just started may happen to be
8199 * too fast and call LockMachine() that will fail (because of PID, etc.),
8200 * so that the Machine will never get out of the Spawning session state.
8201 */
8202
8203 /* inform the session that it will be a remote one */
8204 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8205#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8206 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8207#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8208 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8209#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8210 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8211
8212 if (FAILED(rc))
8213 {
8214 /* restore the session state */
8215 mData->mSession.mState = SessionState_Unlocked;
8216 /* The failure may occur w/o any error info (from RPC), so provide one */
8217 return setError(VBOX_E_VM_ERROR,
8218 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8219 }
8220
8221 /* attach launch data to the machine */
8222 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8223 mData->mSession.mRemoteControls.push_back(aControl);
8224 mData->mSession.mProgress = aProgress;
8225 mData->mSession.mPID = pid;
8226 mData->mSession.mState = SessionState_Spawning;
8227 mData->mSession.mType = strFrontend;
8228
8229 LogFlowThisFuncLeave();
8230 return S_OK;
8231}
8232
8233/**
8234 * Returns @c true if the given session machine instance has an open direct
8235 * session (and optionally also for direct sessions which are closing) and
8236 * returns the session control machine instance if so.
8237 *
8238 * Note that when the method returns @c false, the arguments remain unchanged.
8239 *
8240 * @param aMachine Session machine object.
8241 * @param aControl Direct session control object (optional).
8242 * @param aAllowClosing If true then additionally a session which is currently
8243 * being closed will also be allowed.
8244 *
8245 * @note locks this object for reading.
8246 */
8247bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8248 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8249 bool aAllowClosing /*= false*/)
8250{
8251 AutoLimitedCaller autoCaller(this);
8252 AssertComRCReturn(autoCaller.rc(), false);
8253
8254 /* just return false for inaccessible machines */
8255 if (autoCaller.state() != Ready)
8256 return false;
8257
8258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8259
8260 if ( mData->mSession.mState == SessionState_Locked
8261 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8262 )
8263 {
8264 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8265
8266 aMachine = mData->mSession.mMachine;
8267
8268 if (aControl != NULL)
8269 *aControl = mData->mSession.mDirectControl;
8270
8271 return true;
8272 }
8273
8274 return false;
8275}
8276
8277/**
8278 * Returns @c true if the given machine has an spawning direct session.
8279 *
8280 * @note locks this object for reading.
8281 */
8282bool Machine::isSessionSpawning()
8283{
8284 AutoLimitedCaller autoCaller(this);
8285 AssertComRCReturn(autoCaller.rc(), false);
8286
8287 /* just return false for inaccessible machines */
8288 if (autoCaller.state() != Ready)
8289 return false;
8290
8291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8292
8293 if (mData->mSession.mState == SessionState_Spawning)
8294 return true;
8295
8296 return false;
8297}
8298
8299#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8300/**
8301 * Called from the client watcher thread to check for unexpected client process
8302 * death during Session_Spawning state (e.g. before it successfully opened a
8303 * direct session).
8304 *
8305 * On Win32 and on OS/2, this method is called only when we've got the
8306 * direct client's process termination notification, so it always returns @c
8307 * true.
8308 *
8309 * On other platforms, this method returns @c true if the client process is
8310 * terminated and @c false if it's still alive.
8311 *
8312 * @note Locks this object for writing.
8313 */
8314bool Machine::checkForSpawnFailure()
8315{
8316 AutoCaller autoCaller(this);
8317 if (!autoCaller.isOk())
8318 {
8319 /* nothing to do */
8320 LogFlowThisFunc(("Already uninitialized!\n"));
8321 return true;
8322 }
8323
8324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8325
8326 if (mData->mSession.mState != SessionState_Spawning)
8327 {
8328 /* nothing to do */
8329 LogFlowThisFunc(("Not spawning any more!\n"));
8330 return true;
8331 }
8332
8333 HRESULT rc = S_OK;
8334
8335 /* PID not yet initialized, skip check. */
8336 if (mData->mSession.mPID == NIL_RTPROCESS)
8337 return false;
8338
8339 RTPROCSTATUS status;
8340 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8341
8342 if (vrc != VERR_PROCESS_RUNNING)
8343 {
8344 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8345 rc = setError(E_FAIL,
8346 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8347 getName().c_str(), status.iStatus);
8348 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8349 rc = setError(E_FAIL,
8350 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8351 getName().c_str(), status.iStatus);
8352 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8353 rc = setError(E_FAIL,
8354 tr("The virtual machine '%s' has terminated abnormally"),
8355 getName().c_str(), status.iStatus);
8356 else
8357 rc = setError(E_FAIL,
8358 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8359 getName().c_str(), vrc);
8360 }
8361
8362 if (FAILED(rc))
8363 {
8364 /* Close the remote session, remove the remote control from the list
8365 * and reset session state to Closed (@note keep the code in sync with
8366 * the relevant part in LockMachine()). */
8367
8368 Assert(mData->mSession.mRemoteControls.size() == 1);
8369 if (mData->mSession.mRemoteControls.size() == 1)
8370 {
8371 ErrorInfoKeeper eik;
8372 mData->mSession.mRemoteControls.front()->Uninitialize();
8373 }
8374
8375 mData->mSession.mRemoteControls.clear();
8376 mData->mSession.mState = SessionState_Unlocked;
8377
8378 /* finalize the progress after setting the state */
8379 if (!mData->mSession.mProgress.isNull())
8380 {
8381 mData->mSession.mProgress->notifyComplete(rc);
8382 mData->mSession.mProgress.setNull();
8383 }
8384
8385 mData->mSession.mPID = NIL_RTPROCESS;
8386
8387 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8388 return true;
8389 }
8390
8391 return false;
8392}
8393#endif /* !VBOX_WITH_GENERIC_SESSION_WATCHER */
8394
8395/**
8396 * Checks whether the machine can be registered. If so, commits and saves
8397 * all settings.
8398 *
8399 * @note Must be called from mParent's write lock. Locks this object and
8400 * children for writing.
8401 */
8402HRESULT Machine::prepareRegister()
8403{
8404 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8405
8406 AutoLimitedCaller autoCaller(this);
8407 AssertComRCReturnRC(autoCaller.rc());
8408
8409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8410
8411 /* wait for state dependents to drop to zero */
8412 ensureNoStateDependencies();
8413
8414 if (!mData->mAccessible)
8415 return setError(VBOX_E_INVALID_OBJECT_STATE,
8416 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8417 mUserData->s.strName.c_str(),
8418 mData->mUuid.toString().c_str());
8419
8420 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8421
8422 if (mData->mRegistered)
8423 return setError(VBOX_E_INVALID_OBJECT_STATE,
8424 tr("The machine '%s' with UUID {%s} is already registered"),
8425 mUserData->s.strName.c_str(),
8426 mData->mUuid.toString().c_str());
8427
8428 HRESULT rc = S_OK;
8429
8430 // Ensure the settings are saved. If we are going to be registered and
8431 // no config file exists yet, create it by calling saveSettings() too.
8432 if ( (mData->flModifications)
8433 || (!mData->pMachineConfigFile->fileExists())
8434 )
8435 {
8436 rc = saveSettings(NULL);
8437 // no need to check whether VirtualBox.xml needs saving too since
8438 // we can't have a machine XML file rename pending
8439 if (FAILED(rc)) return rc;
8440 }
8441
8442 /* more config checking goes here */
8443
8444 if (SUCCEEDED(rc))
8445 {
8446 /* we may have had implicit modifications we want to fix on success */
8447 commit();
8448
8449 mData->mRegistered = true;
8450 }
8451 else
8452 {
8453 /* we may have had implicit modifications we want to cancel on failure*/
8454 rollback(false /* aNotify */);
8455 }
8456
8457 return rc;
8458}
8459
8460/**
8461 * Increases the number of objects dependent on the machine state or on the
8462 * registered state. Guarantees that these two states will not change at least
8463 * until #releaseStateDependency() is called.
8464 *
8465 * Depending on the @a aDepType value, additional state checks may be made.
8466 * These checks will set extended error info on failure. See
8467 * #checkStateDependency() for more info.
8468 *
8469 * If this method returns a failure, the dependency is not added and the caller
8470 * is not allowed to rely on any particular machine state or registration state
8471 * value and may return the failed result code to the upper level.
8472 *
8473 * @param aDepType Dependency type to add.
8474 * @param aState Current machine state (NULL if not interested).
8475 * @param aRegistered Current registered state (NULL if not interested).
8476 *
8477 * @note Locks this object for writing.
8478 */
8479HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8480 MachineState_T *aState /* = NULL */,
8481 BOOL *aRegistered /* = NULL */)
8482{
8483 AutoCaller autoCaller(this);
8484 AssertComRCReturnRC(autoCaller.rc());
8485
8486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8487
8488 HRESULT rc = checkStateDependency(aDepType);
8489 if (FAILED(rc)) return rc;
8490
8491 {
8492 if (mData->mMachineStateChangePending != 0)
8493 {
8494 /* ensureNoStateDependencies() is waiting for state dependencies to
8495 * drop to zero so don't add more. It may make sense to wait a bit
8496 * and retry before reporting an error (since the pending state
8497 * transition should be really quick) but let's just assert for
8498 * now to see if it ever happens on practice. */
8499
8500 AssertFailed();
8501
8502 return setError(E_ACCESSDENIED,
8503 tr("Machine state change is in progress. Please retry the operation later."));
8504 }
8505
8506 ++mData->mMachineStateDeps;
8507 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8508 }
8509
8510 if (aState)
8511 *aState = mData->mMachineState;
8512 if (aRegistered)
8513 *aRegistered = mData->mRegistered;
8514
8515 return S_OK;
8516}
8517
8518/**
8519 * Decreases the number of objects dependent on the machine state.
8520 * Must always complete the #addStateDependency() call after the state
8521 * dependency is no more necessary.
8522 */
8523void Machine::releaseStateDependency()
8524{
8525 AutoCaller autoCaller(this);
8526 AssertComRCReturnVoid(autoCaller.rc());
8527
8528 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8529
8530 /* releaseStateDependency() w/o addStateDependency()? */
8531 AssertReturnVoid(mData->mMachineStateDeps != 0);
8532 -- mData->mMachineStateDeps;
8533
8534 if (mData->mMachineStateDeps == 0)
8535 {
8536 /* inform ensureNoStateDependencies() that there are no more deps */
8537 if (mData->mMachineStateChangePending != 0)
8538 {
8539 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8540 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8541 }
8542 }
8543}
8544
8545Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8546{
8547 /* start with nothing found */
8548 Utf8Str strResult("");
8549
8550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8551
8552 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8553 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8554 // found:
8555 strResult = it->second; // source is a Utf8Str
8556
8557 return strResult;
8558}
8559
8560// protected methods
8561/////////////////////////////////////////////////////////////////////////////
8562
8563/**
8564 * Performs machine state checks based on the @a aDepType value. If a check
8565 * fails, this method will set extended error info, otherwise it will return
8566 * S_OK. It is supposed, that on failure, the caller will immediately return
8567 * the return value of this method to the upper level.
8568 *
8569 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8570 *
8571 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8572 * current state of this machine object allows to change settings of the
8573 * machine (i.e. the machine is not registered, or registered but not running
8574 * and not saved). It is useful to call this method from Machine setters
8575 * before performing any change.
8576 *
8577 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8578 * as for MutableStateDep except that if the machine is saved, S_OK is also
8579 * returned. This is useful in setters which allow changing machine
8580 * properties when it is in the saved state.
8581 *
8582 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8583 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8584 * Aborted).
8585 *
8586 * @param aDepType Dependency type to check.
8587 *
8588 * @note Non Machine based classes should use #addStateDependency() and
8589 * #releaseStateDependency() methods or the smart AutoStateDependency
8590 * template.
8591 *
8592 * @note This method must be called from under this object's read or write
8593 * lock.
8594 */
8595HRESULT Machine::checkStateDependency(StateDependency aDepType)
8596{
8597 switch (aDepType)
8598 {
8599 case AnyStateDep:
8600 {
8601 break;
8602 }
8603 case MutableStateDep:
8604 {
8605 if ( mData->mRegistered
8606 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8607 || ( mData->mMachineState != MachineState_Paused
8608 && mData->mMachineState != MachineState_Running
8609 && mData->mMachineState != MachineState_Aborted
8610 && mData->mMachineState != MachineState_Teleported
8611 && mData->mMachineState != MachineState_PoweredOff
8612 )
8613 )
8614 )
8615 return setError(VBOX_E_INVALID_VM_STATE,
8616 tr("The machine is not mutable (state is %s)"),
8617 Global::stringifyMachineState(mData->mMachineState));
8618 break;
8619 }
8620 case MutableOrSavedStateDep:
8621 {
8622 if ( mData->mRegistered
8623 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8624 || ( mData->mMachineState != MachineState_Paused
8625 && mData->mMachineState != MachineState_Running
8626 && mData->mMachineState != MachineState_Aborted
8627 && mData->mMachineState != MachineState_Teleported
8628 && mData->mMachineState != MachineState_Saved
8629 && mData->mMachineState != MachineState_PoweredOff
8630 )
8631 )
8632 )
8633 return setError(VBOX_E_INVALID_VM_STATE,
8634 tr("The machine is not mutable (state is %s)"),
8635 Global::stringifyMachineState(mData->mMachineState));
8636 break;
8637 }
8638 case OfflineStateDep:
8639 {
8640 if ( mData->mRegistered
8641 && ( !isSessionMachine()
8642 || ( mData->mMachineState != MachineState_PoweredOff
8643 && mData->mMachineState != MachineState_Saved
8644 && mData->mMachineState != MachineState_Aborted
8645 && mData->mMachineState != MachineState_Teleported
8646 )
8647 )
8648 )
8649 return setError(VBOX_E_INVALID_VM_STATE,
8650 tr("The machine is not offline (state is %s)"),
8651 Global::stringifyMachineState(mData->mMachineState));
8652 break;
8653 }
8654 }
8655
8656 return S_OK;
8657}
8658
8659/**
8660 * Helper to initialize all associated child objects and allocate data
8661 * structures.
8662 *
8663 * This method must be called as a part of the object's initialization procedure
8664 * (usually done in the #init() method).
8665 *
8666 * @note Must be called only from #init() or from #registeredInit().
8667 */
8668HRESULT Machine::initDataAndChildObjects()
8669{
8670 AutoCaller autoCaller(this);
8671 AssertComRCReturnRC(autoCaller.rc());
8672 AssertComRCReturn(autoCaller.state() == InInit ||
8673 autoCaller.state() == Limited, E_FAIL);
8674
8675 AssertReturn(!mData->mAccessible, E_FAIL);
8676
8677 /* allocate data structures */
8678 mSSData.allocate();
8679 mUserData.allocate();
8680 mHWData.allocate();
8681 mMediaData.allocate();
8682 mStorageControllers.allocate();
8683 mUSBControllers.allocate();
8684
8685 /* initialize mOSTypeId */
8686 mUserData->s.strOsType = mParent->getUnknownOSType()->i_id();
8687
8688 /* create associated BIOS settings object */
8689 unconst(mBIOSSettings).createObject();
8690 mBIOSSettings->init(this);
8691
8692 /* create an associated VRDE object (default is disabled) */
8693 unconst(mVRDEServer).createObject();
8694 mVRDEServer->init(this);
8695
8696 /* create associated serial port objects */
8697 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8698 {
8699 unconst(mSerialPorts[slot]).createObject();
8700 mSerialPorts[slot]->init(this, slot);
8701 }
8702
8703 /* create associated parallel port objects */
8704 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8705 {
8706 unconst(mParallelPorts[slot]).createObject();
8707 mParallelPorts[slot]->init(this, slot);
8708 }
8709
8710 /* create the audio adapter object (always present, default is disabled) */
8711 unconst(mAudioAdapter).createObject();
8712 mAudioAdapter->init(this);
8713
8714 /* create the USB device filters object (always present) */
8715 unconst(mUSBDeviceFilters).createObject();
8716 mUSBDeviceFilters->init(this);
8717
8718 /* create associated network adapter objects */
8719 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8720 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8721 {
8722 unconst(mNetworkAdapters[slot]).createObject();
8723 mNetworkAdapters[slot]->init(this, slot);
8724 }
8725
8726 /* create the bandwidth control */
8727 unconst(mBandwidthControl).createObject();
8728 mBandwidthControl->init(this);
8729
8730 return S_OK;
8731}
8732
8733/**
8734 * Helper to uninitialize all associated child objects and to free all data
8735 * structures.
8736 *
8737 * This method must be called as a part of the object's uninitialization
8738 * procedure (usually done in the #uninit() method).
8739 *
8740 * @note Must be called only from #uninit() or from #registeredInit().
8741 */
8742void Machine::uninitDataAndChildObjects()
8743{
8744 AutoCaller autoCaller(this);
8745 AssertComRCReturnVoid(autoCaller.rc());
8746 AssertComRCReturnVoid( autoCaller.state() == InUninit
8747 || autoCaller.state() == Limited);
8748
8749 /* tell all our other child objects we've been uninitialized */
8750 if (mBandwidthControl)
8751 {
8752 mBandwidthControl->uninit();
8753 unconst(mBandwidthControl).setNull();
8754 }
8755
8756 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8757 {
8758 if (mNetworkAdapters[slot])
8759 {
8760 mNetworkAdapters[slot]->uninit();
8761 unconst(mNetworkAdapters[slot]).setNull();
8762 }
8763 }
8764
8765 if (mUSBDeviceFilters)
8766 {
8767 mUSBDeviceFilters->uninit();
8768 unconst(mUSBDeviceFilters).setNull();
8769 }
8770
8771 if (mAudioAdapter)
8772 {
8773 mAudioAdapter->uninit();
8774 unconst(mAudioAdapter).setNull();
8775 }
8776
8777 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8778 {
8779 if (mParallelPorts[slot])
8780 {
8781 mParallelPorts[slot]->uninit();
8782 unconst(mParallelPorts[slot]).setNull();
8783 }
8784 }
8785
8786 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8787 {
8788 if (mSerialPorts[slot])
8789 {
8790 mSerialPorts[slot]->uninit();
8791 unconst(mSerialPorts[slot]).setNull();
8792 }
8793 }
8794
8795 if (mVRDEServer)
8796 {
8797 mVRDEServer->uninit();
8798 unconst(mVRDEServer).setNull();
8799 }
8800
8801 if (mBIOSSettings)
8802 {
8803 mBIOSSettings->uninit();
8804 unconst(mBIOSSettings).setNull();
8805 }
8806
8807 /* Deassociate media (only when a real Machine or a SnapshotMachine
8808 * instance is uninitialized; SessionMachine instances refer to real
8809 * Machine media). This is necessary for a clean re-initialization of
8810 * the VM after successfully re-checking the accessibility state. Note
8811 * that in case of normal Machine or SnapshotMachine uninitialization (as
8812 * a result of unregistering or deleting the snapshot), outdated media
8813 * attachments will already be uninitialized and deleted, so this
8814 * code will not affect them. */
8815 if ( !!mMediaData
8816 && (!isSessionMachine())
8817 )
8818 {
8819 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8820 it != mMediaData->mAttachments.end();
8821 ++it)
8822 {
8823 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8824 if (pMedium.isNull())
8825 continue;
8826 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8827 AssertComRC(rc);
8828 }
8829 }
8830
8831 if (!isSessionMachine() && !isSnapshotMachine())
8832 {
8833 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8834 if (mData->mFirstSnapshot)
8835 {
8836 // snapshots tree is protected by machine write lock; strictly
8837 // this isn't necessary here since we're deleting the entire
8838 // machine, but otherwise we assert in Snapshot::uninit()
8839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8840 mData->mFirstSnapshot->uninit();
8841 mData->mFirstSnapshot.setNull();
8842 }
8843
8844 mData->mCurrentSnapshot.setNull();
8845 }
8846
8847 /* free data structures (the essential mData structure is not freed here
8848 * since it may be still in use) */
8849 mMediaData.free();
8850 mStorageControllers.free();
8851 mUSBControllers.free();
8852 mHWData.free();
8853 mUserData.free();
8854 mSSData.free();
8855}
8856
8857/**
8858 * Returns a pointer to the Machine object for this machine that acts like a
8859 * parent for complex machine data objects such as shared folders, etc.
8860 *
8861 * For primary Machine objects and for SnapshotMachine objects, returns this
8862 * object's pointer itself. For SessionMachine objects, returns the peer
8863 * (primary) machine pointer.
8864 */
8865Machine* Machine::getMachine()
8866{
8867 if (isSessionMachine())
8868 return (Machine*)mPeer;
8869 return this;
8870}
8871
8872/**
8873 * Makes sure that there are no machine state dependents. If necessary, waits
8874 * for the number of dependents to drop to zero.
8875 *
8876 * Make sure this method is called from under this object's write lock to
8877 * guarantee that no new dependents may be added when this method returns
8878 * control to the caller.
8879 *
8880 * @note Locks this object for writing. The lock will be released while waiting
8881 * (if necessary).
8882 *
8883 * @warning To be used only in methods that change the machine state!
8884 */
8885void Machine::ensureNoStateDependencies()
8886{
8887 AssertReturnVoid(isWriteLockOnCurrentThread());
8888
8889 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8890
8891 /* Wait for all state dependents if necessary */
8892 if (mData->mMachineStateDeps != 0)
8893 {
8894 /* lazy semaphore creation */
8895 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8896 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8897
8898 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8899 mData->mMachineStateDeps));
8900
8901 ++mData->mMachineStateChangePending;
8902
8903 /* reset the semaphore before waiting, the last dependent will signal
8904 * it */
8905 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8906
8907 alock.release();
8908
8909 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8910
8911 alock.acquire();
8912
8913 -- mData->mMachineStateChangePending;
8914 }
8915}
8916
8917/**
8918 * Changes the machine state and informs callbacks.
8919 *
8920 * This method is not intended to fail so it either returns S_OK or asserts (and
8921 * returns a failure).
8922 *
8923 * @note Locks this object for writing.
8924 */
8925HRESULT Machine::setMachineState(MachineState_T aMachineState)
8926{
8927 LogFlowThisFuncEnter();
8928 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8929
8930 AutoCaller autoCaller(this);
8931 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8932
8933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8934
8935 /* wait for state dependents to drop to zero */
8936 ensureNoStateDependencies();
8937
8938 if (mData->mMachineState != aMachineState)
8939 {
8940 mData->mMachineState = aMachineState;
8941
8942 RTTimeNow(&mData->mLastStateChange);
8943
8944 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8945 }
8946
8947 LogFlowThisFuncLeave();
8948 return S_OK;
8949}
8950
8951/**
8952 * Searches for a shared folder with the given logical name
8953 * in the collection of shared folders.
8954 *
8955 * @param aName logical name of the shared folder
8956 * @param aSharedFolder where to return the found object
8957 * @param aSetError whether to set the error info if the folder is
8958 * not found
8959 * @return
8960 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8961 *
8962 * @note
8963 * must be called from under the object's lock!
8964 */
8965HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8966 ComObjPtr<SharedFolder> &aSharedFolder,
8967 bool aSetError /* = false */)
8968{
8969 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8970 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8971 it != mHWData->mSharedFolders.end();
8972 ++it)
8973 {
8974 SharedFolder *pSF = *it;
8975 AutoCaller autoCaller(pSF);
8976 if (pSF->getName() == aName)
8977 {
8978 aSharedFolder = pSF;
8979 rc = S_OK;
8980 break;
8981 }
8982 }
8983
8984 if (aSetError && FAILED(rc))
8985 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8986
8987 return rc;
8988}
8989
8990/**
8991 * Initializes all machine instance data from the given settings structures
8992 * from XML. The exception is the machine UUID which needs special handling
8993 * depending on the caller's use case, so the caller needs to set that herself.
8994 *
8995 * This gets called in several contexts during machine initialization:
8996 *
8997 * -- When machine XML exists on disk already and needs to be loaded into memory,
8998 * for example, from registeredInit() to load all registered machines on
8999 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
9000 * attached to the machine should be part of some media registry already.
9001 *
9002 * -- During OVF import, when a machine config has been constructed from an
9003 * OVF file. In this case, puuidRegistry is set to the machine UUID to
9004 * ensure that the media listed as attachments in the config (which have
9005 * been imported from the OVF) receive the correct registry ID.
9006 *
9007 * -- During VM cloning.
9008 *
9009 * @param config Machine settings from XML.
9010 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
9011 * @return
9012 */
9013HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
9014 const Guid *puuidRegistry)
9015{
9016 // copy name, description, OS type, teleporter, UTC etc.
9017 mUserData->s = config.machineUserData;
9018
9019 // Decode the Icon overide data from config userdata and set onto Machine.
9020 #define DECODE_STR_MAX _1M
9021 const char* pszStr = config.machineUserData.ovIcon.c_str();
9022 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
9023 if (cbOut > DECODE_STR_MAX)
9024 return setError(E_FAIL,
9025 tr("Icon Data too long.'%d' > '%d'"),
9026 cbOut,
9027 DECODE_STR_MAX);
9028 com::SafeArray<BYTE> iconByte(cbOut);
9029 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
9030 if (FAILED(rc))
9031 return setError(E_FAIL,
9032 tr("Failure to Decode Icon Data. '%s' (%d)"),
9033 pszStr,
9034 rc);
9035 mUserData->mIcon.resize(iconByte.size());
9036 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
9037
9038 // look up the object by Id to check it is valid
9039 ComPtr<IGuestOSType> guestOSType;
9040 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
9041 guestOSType.asOutParam());
9042 if (FAILED(rc)) return rc;
9043
9044 // stateFile (optional)
9045 if (config.strStateFile.isEmpty())
9046 mSSData->strStateFilePath.setNull();
9047 else
9048 {
9049 Utf8Str stateFilePathFull(config.strStateFile);
9050 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
9051 if (RT_FAILURE(vrc))
9052 return setError(E_FAIL,
9053 tr("Invalid saved state file path '%s' (%Rrc)"),
9054 config.strStateFile.c_str(),
9055 vrc);
9056 mSSData->strStateFilePath = stateFilePathFull;
9057 }
9058
9059 // snapshot folder needs special processing so set it again
9060 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
9061 if (FAILED(rc)) return rc;
9062
9063 /* Copy the extra data items (Not in any case config is already the same as
9064 * mData->pMachineConfigFile, like when the xml files are read from disk. So
9065 * make sure the extra data map is copied). */
9066 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
9067
9068 /* currentStateModified (optional, default is true) */
9069 mData->mCurrentStateModified = config.fCurrentStateModified;
9070
9071 mData->mLastStateChange = config.timeLastStateChange;
9072
9073 /*
9074 * note: all mUserData members must be assigned prior this point because
9075 * we need to commit changes in order to let mUserData be shared by all
9076 * snapshot machine instances.
9077 */
9078 mUserData.commitCopy();
9079
9080 // machine registry, if present (must be loaded before snapshots)
9081 if (config.canHaveOwnMediaRegistry())
9082 {
9083 // determine machine folder
9084 Utf8Str strMachineFolder = getSettingsFileFull();
9085 strMachineFolder.stripFilename();
9086 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
9087 config.mediaRegistry,
9088 strMachineFolder);
9089 if (FAILED(rc)) return rc;
9090 }
9091
9092 /* Snapshot node (optional) */
9093 size_t cRootSnapshots;
9094 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9095 {
9096 // there must be only one root snapshot
9097 Assert(cRootSnapshots == 1);
9098
9099 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9100
9101 rc = loadSnapshot(snap,
9102 config.uuidCurrentSnapshot,
9103 NULL); // no parent == first snapshot
9104 if (FAILED(rc)) return rc;
9105 }
9106
9107 // hardware data
9108 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9109 if (FAILED(rc)) return rc;
9110
9111 // load storage controllers
9112 rc = loadStorageControllers(config.storageMachine,
9113 puuidRegistry,
9114 NULL /* puuidSnapshot */);
9115 if (FAILED(rc)) return rc;
9116
9117 /*
9118 * NOTE: the assignment below must be the last thing to do,
9119 * otherwise it will be not possible to change the settings
9120 * somewhere in the code above because all setters will be
9121 * blocked by checkStateDependency(MutableStateDep).
9122 */
9123
9124 /* set the machine state to Aborted or Saved when appropriate */
9125 if (config.fAborted)
9126 {
9127 mSSData->strStateFilePath.setNull();
9128
9129 /* no need to use setMachineState() during init() */
9130 mData->mMachineState = MachineState_Aborted;
9131 }
9132 else if (!mSSData->strStateFilePath.isEmpty())
9133 {
9134 /* no need to use setMachineState() during init() */
9135 mData->mMachineState = MachineState_Saved;
9136 }
9137
9138 // after loading settings, we are no longer different from the XML on disk
9139 mData->flModifications = 0;
9140
9141 return S_OK;
9142}
9143
9144/**
9145 * Recursively loads all snapshots starting from the given.
9146 *
9147 * @param aNode <Snapshot> node.
9148 * @param aCurSnapshotId Current snapshot ID from the settings file.
9149 * @param aParentSnapshot Parent snapshot.
9150 */
9151HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9152 const Guid &aCurSnapshotId,
9153 Snapshot *aParentSnapshot)
9154{
9155 AssertReturn(!isSnapshotMachine(), E_FAIL);
9156 AssertReturn(!isSessionMachine(), E_FAIL);
9157
9158 HRESULT rc = S_OK;
9159
9160 Utf8Str strStateFile;
9161 if (!data.strStateFile.isEmpty())
9162 {
9163 /* optional */
9164 strStateFile = data.strStateFile;
9165 int vrc = calculateFullPath(strStateFile, strStateFile);
9166 if (RT_FAILURE(vrc))
9167 return setError(E_FAIL,
9168 tr("Invalid saved state file path '%s' (%Rrc)"),
9169 strStateFile.c_str(),
9170 vrc);
9171 }
9172
9173 /* create a snapshot machine object */
9174 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9175 pSnapshotMachine.createObject();
9176 rc = pSnapshotMachine->initFromSettings(this,
9177 data.hardware,
9178 &data.debugging,
9179 &data.autostart,
9180 data.storage,
9181 data.uuid.ref(),
9182 strStateFile);
9183 if (FAILED(rc)) return rc;
9184
9185 /* create a snapshot object */
9186 ComObjPtr<Snapshot> pSnapshot;
9187 pSnapshot.createObject();
9188 /* initialize the snapshot */
9189 rc = pSnapshot->init(mParent, // VirtualBox object
9190 data.uuid,
9191 data.strName,
9192 data.strDescription,
9193 data.timestamp,
9194 pSnapshotMachine,
9195 aParentSnapshot);
9196 if (FAILED(rc)) return rc;
9197
9198 /* memorize the first snapshot if necessary */
9199 if (!mData->mFirstSnapshot)
9200 mData->mFirstSnapshot = pSnapshot;
9201
9202 /* memorize the current snapshot when appropriate */
9203 if ( !mData->mCurrentSnapshot
9204 && pSnapshot->getId() == aCurSnapshotId
9205 )
9206 mData->mCurrentSnapshot = pSnapshot;
9207
9208 // now create the children
9209 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9210 it != data.llChildSnapshots.end();
9211 ++it)
9212 {
9213 const settings::Snapshot &childData = *it;
9214 // recurse
9215 rc = loadSnapshot(childData,
9216 aCurSnapshotId,
9217 pSnapshot); // parent = the one we created above
9218 if (FAILED(rc)) return rc;
9219 }
9220
9221 return rc;
9222}
9223
9224/**
9225 * Loads settings into mHWData.
9226 *
9227 * @param data Reference to the hardware settings.
9228 * @param pDbg Pointer to the debugging settings.
9229 * @param pAutostart Pointer to the autostart settings.
9230 */
9231HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9232 const settings::Autostart *pAutostart)
9233{
9234 AssertReturn(!isSessionMachine(), E_FAIL);
9235
9236 HRESULT rc = S_OK;
9237
9238 try
9239 {
9240 /* The hardware version attribute (optional). */
9241 mHWData->mHWVersion = data.strVersion;
9242 mHWData->mHardwareUUID = data.uuid;
9243
9244 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9245 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9246 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9247 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9248 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9249 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9250 mHWData->mPAEEnabled = data.fPAE;
9251 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9252 mHWData->mLongMode = data.enmLongMode;
9253 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9254 mHWData->mCPUCount = data.cCPUs;
9255 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9256 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9257
9258 // cpu
9259 if (mHWData->mCPUHotPlugEnabled)
9260 {
9261 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9262 it != data.llCpus.end();
9263 ++it)
9264 {
9265 const settings::Cpu &cpu = *it;
9266
9267 mHWData->mCPUAttached[cpu.ulId] = true;
9268 }
9269 }
9270
9271 // cpuid leafs
9272 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9273 it != data.llCpuIdLeafs.end();
9274 ++it)
9275 {
9276 const settings::CpuIdLeaf &leaf = *it;
9277
9278 switch (leaf.ulId)
9279 {
9280 case 0x0:
9281 case 0x1:
9282 case 0x2:
9283 case 0x3:
9284 case 0x4:
9285 case 0x5:
9286 case 0x6:
9287 case 0x7:
9288 case 0x8:
9289 case 0x9:
9290 case 0xA:
9291 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9292 break;
9293
9294 case 0x80000000:
9295 case 0x80000001:
9296 case 0x80000002:
9297 case 0x80000003:
9298 case 0x80000004:
9299 case 0x80000005:
9300 case 0x80000006:
9301 case 0x80000007:
9302 case 0x80000008:
9303 case 0x80000009:
9304 case 0x8000000A:
9305 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9306 break;
9307
9308 default:
9309 /* just ignore */
9310 break;
9311 }
9312 }
9313
9314 mHWData->mMemorySize = data.ulMemorySizeMB;
9315 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9316
9317 // boot order
9318 for (size_t i = 0;
9319 i < RT_ELEMENTS(mHWData->mBootOrder);
9320 i++)
9321 {
9322 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9323 if (it == data.mapBootOrder.end())
9324 mHWData->mBootOrder[i] = DeviceType_Null;
9325 else
9326 mHWData->mBootOrder[i] = it->second;
9327 }
9328
9329 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9330 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9331 mHWData->mMonitorCount = data.cMonitors;
9332 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9333 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9334 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9335 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9336 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9337 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9338 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9339 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9340 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9341 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9342 if (!data.strVideoCaptureFile.isEmpty())
9343 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9344 else
9345 mHWData->mVideoCaptureFile.setNull();
9346 mHWData->mFirmwareType = data.firmwareType;
9347 mHWData->mPointingHIDType = data.pointingHIDType;
9348 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9349 mHWData->mChipsetType = data.chipsetType;
9350 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9351 mHWData->mHPETEnabled = data.fHPETEnabled;
9352
9353 /* VRDEServer */
9354 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9355 if (FAILED(rc)) return rc;
9356
9357 /* BIOS */
9358 rc = mBIOSSettings->loadSettings(data.biosSettings);
9359 if (FAILED(rc)) return rc;
9360
9361 // Bandwidth control (must come before network adapters)
9362 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9363 if (FAILED(rc)) return rc;
9364
9365 /* Shared folders */
9366 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9367 it != data.usbSettings.llUSBControllers.end();
9368 ++it)
9369 {
9370 const settings::USBController &settingsCtrl = *it;
9371 ComObjPtr<USBController> newCtrl;
9372
9373 newCtrl.createObject();
9374 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9375 mUSBControllers->push_back(newCtrl);
9376 }
9377
9378 /* USB device filters */
9379 rc = mUSBDeviceFilters->loadSettings(data.usbSettings);
9380 if (FAILED(rc)) return rc;
9381
9382 // network adapters
9383 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9384 uint32_t oldCount = mNetworkAdapters.size();
9385 if (newCount > oldCount)
9386 {
9387 mNetworkAdapters.resize(newCount);
9388 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9389 {
9390 unconst(mNetworkAdapters[slot]).createObject();
9391 mNetworkAdapters[slot]->init(this, slot);
9392 }
9393 }
9394 else if (newCount < oldCount)
9395 mNetworkAdapters.resize(newCount);
9396 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9397 it != data.llNetworkAdapters.end();
9398 ++it)
9399 {
9400 const settings::NetworkAdapter &nic = *it;
9401
9402 /* slot unicity is guaranteed by XML Schema */
9403 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9404 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9405 if (FAILED(rc)) return rc;
9406 }
9407
9408 // serial ports
9409 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9410 it != data.llSerialPorts.end();
9411 ++it)
9412 {
9413 const settings::SerialPort &s = *it;
9414
9415 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9416 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9417 if (FAILED(rc)) return rc;
9418 }
9419
9420 // parallel ports (optional)
9421 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9422 it != data.llParallelPorts.end();
9423 ++it)
9424 {
9425 const settings::ParallelPort &p = *it;
9426
9427 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9428 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9429 if (FAILED(rc)) return rc;
9430 }
9431
9432 /* AudioAdapter */
9433 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9434 if (FAILED(rc)) return rc;
9435
9436 /* Shared folders */
9437 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9438 it != data.llSharedFolders.end();
9439 ++it)
9440 {
9441 const settings::SharedFolder &sf = *it;
9442
9443 ComObjPtr<SharedFolder> sharedFolder;
9444 /* Check for double entries. Not allowed! */
9445 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9446 if (SUCCEEDED(rc))
9447 return setError(VBOX_E_OBJECT_IN_USE,
9448 tr("Shared folder named '%s' already exists"),
9449 sf.strName.c_str());
9450
9451 /* Create the new shared folder. Don't break on error. This will be
9452 * reported when the machine starts. */
9453 sharedFolder.createObject();
9454 rc = sharedFolder->init(getMachine(),
9455 sf.strName,
9456 sf.strHostPath,
9457 RT_BOOL(sf.fWritable),
9458 RT_BOOL(sf.fAutoMount),
9459 false /* fFailOnError */);
9460 if (FAILED(rc)) return rc;
9461 mHWData->mSharedFolders.push_back(sharedFolder);
9462 }
9463
9464 // Clipboard
9465 mHWData->mClipboardMode = data.clipboardMode;
9466
9467 // drag'n'drop
9468 mHWData->mDragAndDropMode = data.dragAndDropMode;
9469
9470 // guest settings
9471 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9472
9473 // IO settings
9474 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9475 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9476
9477 // Host PCI devices
9478 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9479 it != data.pciAttachments.end();
9480 ++it)
9481 {
9482 const settings::HostPCIDeviceAttachment &hpda = *it;
9483 ComObjPtr<PCIDeviceAttachment> pda;
9484
9485 pda.createObject();
9486 pda->loadSettings(this, hpda);
9487 mHWData->mPCIDeviceAssignments.push_back(pda);
9488 }
9489
9490 /*
9491 * (The following isn't really real hardware, but it lives in HWData
9492 * for reasons of convenience.)
9493 */
9494
9495#ifdef VBOX_WITH_GUEST_PROPS
9496 /* Guest properties (optional) */
9497 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9498 it != data.llGuestProperties.end();
9499 ++it)
9500 {
9501 const settings::GuestProperty &prop = *it;
9502 uint32_t fFlags = guestProp::NILFLAG;
9503 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9504 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9505 mHWData->mGuestProperties[prop.strName] = property;
9506 }
9507
9508 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9509#endif /* VBOX_WITH_GUEST_PROPS defined */
9510
9511 rc = loadDebugging(pDbg);
9512 if (FAILED(rc))
9513 return rc;
9514
9515 mHWData->mAutostart = *pAutostart;
9516
9517 /* default frontend */
9518 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9519 }
9520 catch(std::bad_alloc &)
9521 {
9522 return E_OUTOFMEMORY;
9523 }
9524
9525 AssertComRC(rc);
9526 return rc;
9527}
9528
9529/**
9530 * Called from Machine::loadHardware() to load the debugging settings of the
9531 * machine.
9532 *
9533 * @param pDbg Pointer to the settings.
9534 */
9535HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9536{
9537 mHWData->mDebugging = *pDbg;
9538 /* no more processing currently required, this will probably change. */
9539 return S_OK;
9540}
9541
9542/**
9543 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9544 *
9545 * @param data
9546 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9547 * @param puuidSnapshot
9548 * @return
9549 */
9550HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9551 const Guid *puuidRegistry,
9552 const Guid *puuidSnapshot)
9553{
9554 AssertReturn(!isSessionMachine(), E_FAIL);
9555
9556 HRESULT rc = S_OK;
9557
9558 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9559 it != data.llStorageControllers.end();
9560 ++it)
9561 {
9562 const settings::StorageController &ctlData = *it;
9563
9564 ComObjPtr<StorageController> pCtl;
9565 /* Try to find one with the name first. */
9566 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9567 if (SUCCEEDED(rc))
9568 return setError(VBOX_E_OBJECT_IN_USE,
9569 tr("Storage controller named '%s' already exists"),
9570 ctlData.strName.c_str());
9571
9572 pCtl.createObject();
9573 rc = pCtl->init(this,
9574 ctlData.strName,
9575 ctlData.storageBus,
9576 ctlData.ulInstance,
9577 ctlData.fBootable);
9578 if (FAILED(rc)) return rc;
9579
9580 mStorageControllers->push_back(pCtl);
9581
9582 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9583 if (FAILED(rc)) return rc;
9584
9585 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9586 if (FAILED(rc)) return rc;
9587
9588 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9589 if (FAILED(rc)) return rc;
9590
9591 /* Set IDE emulation settings (only for AHCI controller). */
9592 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9593 {
9594 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9595 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9596 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9597 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9598 )
9599 return rc;
9600 }
9601
9602 /* Load the attached devices now. */
9603 rc = loadStorageDevices(pCtl,
9604 ctlData,
9605 puuidRegistry,
9606 puuidSnapshot);
9607 if (FAILED(rc)) return rc;
9608 }
9609
9610 return S_OK;
9611}
9612
9613/**
9614 * Called from loadStorageControllers for a controller's devices.
9615 *
9616 * @param aStorageController
9617 * @param data
9618 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9619 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9620 * @return
9621 */
9622HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9623 const settings::StorageController &data,
9624 const Guid *puuidRegistry,
9625 const Guid *puuidSnapshot)
9626{
9627 HRESULT rc = S_OK;
9628
9629 /* paranoia: detect duplicate attachments */
9630 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9631 it != data.llAttachedDevices.end();
9632 ++it)
9633 {
9634 const settings::AttachedDevice &ad = *it;
9635
9636 for (settings::AttachedDevicesList::const_iterator it2 = it;
9637 it2 != data.llAttachedDevices.end();
9638 ++it2)
9639 {
9640 if (it == it2)
9641 continue;
9642
9643 const settings::AttachedDevice &ad2 = *it2;
9644
9645 if ( ad.lPort == ad2.lPort
9646 && ad.lDevice == ad2.lDevice)
9647 {
9648 return setError(E_FAIL,
9649 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9650 aStorageController->i_getName().c_str(),
9651 ad.lPort,
9652 ad.lDevice,
9653 mUserData->s.strName.c_str());
9654 }
9655 }
9656 }
9657
9658 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9659 it != data.llAttachedDevices.end();
9660 ++it)
9661 {
9662 const settings::AttachedDevice &dev = *it;
9663 ComObjPtr<Medium> medium;
9664
9665 switch (dev.deviceType)
9666 {
9667 case DeviceType_Floppy:
9668 case DeviceType_DVD:
9669 if (dev.strHostDriveSrc.isNotEmpty())
9670 rc = mParent->host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9671 else
9672 rc = mParent->findRemoveableMedium(dev.deviceType,
9673 dev.uuid,
9674 false /* fRefresh */,
9675 false /* aSetError */,
9676 medium);
9677 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9678 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9679 rc = S_OK;
9680 break;
9681
9682 case DeviceType_HardDisk:
9683 {
9684 /* find a hard disk by UUID */
9685 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9686 if (FAILED(rc))
9687 {
9688 if (isSnapshotMachine())
9689 {
9690 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9691 // so the user knows that the bad disk is in a snapshot somewhere
9692 com::ErrorInfo info;
9693 return setError(E_FAIL,
9694 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9695 puuidSnapshot->raw(),
9696 info.getText().raw());
9697 }
9698 else
9699 return rc;
9700 }
9701
9702 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9703
9704 if (medium->getType() == MediumType_Immutable)
9705 {
9706 if (isSnapshotMachine())
9707 return setError(E_FAIL,
9708 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9709 "of the virtual machine '%s' ('%s')"),
9710 medium->getLocationFull().c_str(),
9711 dev.uuid.raw(),
9712 puuidSnapshot->raw(),
9713 mUserData->s.strName.c_str(),
9714 mData->m_strConfigFileFull.c_str());
9715
9716 return setError(E_FAIL,
9717 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9718 medium->getLocationFull().c_str(),
9719 dev.uuid.raw(),
9720 mUserData->s.strName.c_str(),
9721 mData->m_strConfigFileFull.c_str());
9722 }
9723
9724 if (medium->getType() == MediumType_MultiAttach)
9725 {
9726 if (isSnapshotMachine())
9727 return setError(E_FAIL,
9728 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9729 "of the virtual machine '%s' ('%s')"),
9730 medium->getLocationFull().c_str(),
9731 dev.uuid.raw(),
9732 puuidSnapshot->raw(),
9733 mUserData->s.strName.c_str(),
9734 mData->m_strConfigFileFull.c_str());
9735
9736 return setError(E_FAIL,
9737 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9738 medium->getLocationFull().c_str(),
9739 dev.uuid.raw(),
9740 mUserData->s.strName.c_str(),
9741 mData->m_strConfigFileFull.c_str());
9742 }
9743
9744 if ( !isSnapshotMachine()
9745 && medium->getChildren().size() != 0
9746 )
9747 return setError(E_FAIL,
9748 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9749 "because it has %d differencing child hard disks"),
9750 medium->getLocationFull().c_str(),
9751 dev.uuid.raw(),
9752 mUserData->s.strName.c_str(),
9753 mData->m_strConfigFileFull.c_str(),
9754 medium->getChildren().size());
9755
9756 if (findAttachment(mMediaData->mAttachments,
9757 medium))
9758 return setError(E_FAIL,
9759 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9760 medium->getLocationFull().c_str(),
9761 dev.uuid.raw(),
9762 mUserData->s.strName.c_str(),
9763 mData->m_strConfigFileFull.c_str());
9764
9765 break;
9766 }
9767
9768 default:
9769 return setError(E_FAIL,
9770 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9771 medium->getLocationFull().c_str(),
9772 mUserData->s.strName.c_str(),
9773 mData->m_strConfigFileFull.c_str());
9774 }
9775
9776 if (FAILED(rc))
9777 break;
9778
9779 /* Bandwidth groups are loaded at this point. */
9780 ComObjPtr<BandwidthGroup> pBwGroup;
9781
9782 if (!dev.strBwGroup.isEmpty())
9783 {
9784 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9785 if (FAILED(rc))
9786 return setError(E_FAIL,
9787 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9788 medium->getLocationFull().c_str(),
9789 dev.strBwGroup.c_str(),
9790 mUserData->s.strName.c_str(),
9791 mData->m_strConfigFileFull.c_str());
9792 pBwGroup->i_reference();
9793 }
9794
9795 const Bstr controllerName = aStorageController->i_getName();
9796 ComObjPtr<MediumAttachment> pAttachment;
9797 pAttachment.createObject();
9798 rc = pAttachment->init(this,
9799 medium,
9800 controllerName,
9801 dev.lPort,
9802 dev.lDevice,
9803 dev.deviceType,
9804 false,
9805 dev.fPassThrough,
9806 dev.fTempEject,
9807 dev.fNonRotational,
9808 dev.fDiscard,
9809 dev.fHotPluggable,
9810 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9811 if (FAILED(rc)) break;
9812
9813 /* associate the medium with this machine and snapshot */
9814 if (!medium.isNull())
9815 {
9816 AutoCaller medCaller(medium);
9817 if (FAILED(medCaller.rc())) return medCaller.rc();
9818 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9819
9820 if (isSnapshotMachine())
9821 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9822 else
9823 rc = medium->addBackReference(mData->mUuid);
9824 /* If the medium->addBackReference fails it sets an appropriate
9825 * error message, so no need to do any guesswork here. */
9826
9827 if (puuidRegistry)
9828 // caller wants registry ID to be set on all attached media (OVF import case)
9829 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9830 }
9831
9832 if (FAILED(rc))
9833 break;
9834
9835 /* back up mMediaData to let registeredInit() properly rollback on failure
9836 * (= limited accessibility) */
9837 setModified(IsModified_Storage);
9838 mMediaData.backup();
9839 mMediaData->mAttachments.push_back(pAttachment);
9840 }
9841
9842 return rc;
9843}
9844
9845/**
9846 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9847 *
9848 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9849 * @param aSnapshot where to return the found snapshot
9850 * @param aSetError true to set extended error info on failure
9851 */
9852HRESULT Machine::findSnapshotById(const Guid &aId,
9853 ComObjPtr<Snapshot> &aSnapshot,
9854 bool aSetError /* = false */)
9855{
9856 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9857
9858 if (!mData->mFirstSnapshot)
9859 {
9860 if (aSetError)
9861 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9862 return E_FAIL;
9863 }
9864
9865 if (aId.isZero())
9866 aSnapshot = mData->mFirstSnapshot;
9867 else
9868 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9869
9870 if (!aSnapshot)
9871 {
9872 if (aSetError)
9873 return setError(E_FAIL,
9874 tr("Could not find a snapshot with UUID {%s}"),
9875 aId.toString().c_str());
9876 return E_FAIL;
9877 }
9878
9879 return S_OK;
9880}
9881
9882/**
9883 * Returns the snapshot with the given name or fails of no such snapshot.
9884 *
9885 * @param aName snapshot name to find
9886 * @param aSnapshot where to return the found snapshot
9887 * @param aSetError true to set extended error info on failure
9888 */
9889HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9890 ComObjPtr<Snapshot> &aSnapshot,
9891 bool aSetError /* = false */)
9892{
9893 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9894
9895 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9896
9897 if (!mData->mFirstSnapshot)
9898 {
9899 if (aSetError)
9900 return setError(VBOX_E_OBJECT_NOT_FOUND,
9901 tr("This machine does not have any snapshots"));
9902 return VBOX_E_OBJECT_NOT_FOUND;
9903 }
9904
9905 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9906
9907 if (!aSnapshot)
9908 {
9909 if (aSetError)
9910 return setError(VBOX_E_OBJECT_NOT_FOUND,
9911 tr("Could not find a snapshot named '%s'"), strName.c_str());
9912 return VBOX_E_OBJECT_NOT_FOUND;
9913 }
9914
9915 return S_OK;
9916}
9917
9918/**
9919 * Returns a storage controller object with the given name.
9920 *
9921 * @param aName storage controller name to find
9922 * @param aStorageController where to return the found storage controller
9923 * @param aSetError true to set extended error info on failure
9924 */
9925HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9926 ComObjPtr<StorageController> &aStorageController,
9927 bool aSetError /* = false */)
9928{
9929 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9930
9931 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9932 it != mStorageControllers->end();
9933 ++it)
9934 {
9935 if ((*it)->i_getName() == aName)
9936 {
9937 aStorageController = (*it);
9938 return S_OK;
9939 }
9940 }
9941
9942 if (aSetError)
9943 return setError(VBOX_E_OBJECT_NOT_FOUND,
9944 tr("Could not find a storage controller named '%s'"),
9945 aName.c_str());
9946 return VBOX_E_OBJECT_NOT_FOUND;
9947}
9948
9949/**
9950 * Returns a USB controller object with the given name.
9951 *
9952 * @param aName USB controller name to find
9953 * @param aUSBController where to return the found USB controller
9954 * @param aSetError true to set extended error info on failure
9955 */
9956HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9957 ComObjPtr<USBController> &aUSBController,
9958 bool aSetError /* = false */)
9959{
9960 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9961
9962 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9963 it != mUSBControllers->end();
9964 ++it)
9965 {
9966 if ((*it)->getName() == aName)
9967 {
9968 aUSBController = (*it);
9969 return S_OK;
9970 }
9971 }
9972
9973 if (aSetError)
9974 return setError(VBOX_E_OBJECT_NOT_FOUND,
9975 tr("Could not find a storage controller named '%s'"),
9976 aName.c_str());
9977 return VBOX_E_OBJECT_NOT_FOUND;
9978}
9979
9980/**
9981 * Returns the number of USB controller instance of the given type.
9982 *
9983 * @param enmType USB controller type.
9984 */
9985ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9986{
9987 ULONG cCtrls = 0;
9988
9989 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9990 it != mUSBControllers->end();
9991 ++it)
9992 {
9993 if ((*it)->getControllerType() == enmType)
9994 cCtrls++;
9995 }
9996
9997 return cCtrls;
9998}
9999
10000HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
10001 MediaData::AttachmentList &atts)
10002{
10003 AutoCaller autoCaller(this);
10004 if (FAILED(autoCaller.rc())) return autoCaller.rc();
10005
10006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
10007
10008 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
10009 it != mMediaData->mAttachments.end();
10010 ++it)
10011 {
10012 const ComObjPtr<MediumAttachment> &pAtt = *it;
10013
10014 // should never happen, but deal with NULL pointers in the list.
10015 AssertStmt(!pAtt.isNull(), continue);
10016
10017 // getControllerName() needs caller+read lock
10018 AutoCaller autoAttCaller(pAtt);
10019 if (FAILED(autoAttCaller.rc()))
10020 {
10021 atts.clear();
10022 return autoAttCaller.rc();
10023 }
10024 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
10025
10026 if (pAtt->getControllerName() == aName)
10027 atts.push_back(pAtt);
10028 }
10029
10030 return S_OK;
10031}
10032
10033/**
10034 * Helper for #saveSettings. Cares about renaming the settings directory and
10035 * file if the machine name was changed and about creating a new settings file
10036 * if this is a new machine.
10037 *
10038 * @note Must be never called directly but only from #saveSettings().
10039 */
10040HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
10041{
10042 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10043
10044 HRESULT rc = S_OK;
10045
10046 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
10047
10048 /// @todo need to handle primary group change, too
10049
10050 /* attempt to rename the settings file if machine name is changed */
10051 if ( mUserData->s.fNameSync
10052 && mUserData.isBackedUp()
10053 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
10054 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
10055 )
10056 {
10057 bool dirRenamed = false;
10058 bool fileRenamed = false;
10059
10060 Utf8Str configFile, newConfigFile;
10061 Utf8Str configFilePrev, newConfigFilePrev;
10062 Utf8Str configDir, newConfigDir;
10063
10064 do
10065 {
10066 int vrc = VINF_SUCCESS;
10067
10068 Utf8Str name = mUserData.backedUpData()->s.strName;
10069 Utf8Str newName = mUserData->s.strName;
10070 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
10071 if (group == "/")
10072 group.setNull();
10073 Utf8Str newGroup = mUserData->s.llGroups.front();
10074 if (newGroup == "/")
10075 newGroup.setNull();
10076
10077 configFile = mData->m_strConfigFileFull;
10078
10079 /* first, rename the directory if it matches the group and machine name */
10080 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
10081 group.c_str(), RTPATH_DELIMITER, name.c_str());
10082 /** @todo hack, make somehow use of ComposeMachineFilename */
10083 if (mUserData->s.fDirectoryIncludesUUID)
10084 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10085 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
10086 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
10087 /** @todo hack, make somehow use of ComposeMachineFilename */
10088 if (mUserData->s.fDirectoryIncludesUUID)
10089 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10090 configDir = configFile;
10091 configDir.stripFilename();
10092 newConfigDir = configDir;
10093 if ( configDir.length() >= groupPlusName.length()
10094 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
10095 {
10096 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10097 Utf8Str newConfigBaseDir(newConfigDir);
10098 newConfigDir.append(newGroupPlusName);
10099 /* consistency: use \ if appropriate on the platform */
10100 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10101 /* new dir and old dir cannot be equal here because of 'if'
10102 * above and because name != newName */
10103 Assert(configDir != newConfigDir);
10104 if (!fSettingsFileIsNew)
10105 {
10106 /* perform real rename only if the machine is not new */
10107 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10108 if ( vrc == VERR_FILE_NOT_FOUND
10109 || vrc == VERR_PATH_NOT_FOUND)
10110 {
10111 /* create the parent directory, then retry renaming */
10112 Utf8Str parent(newConfigDir);
10113 parent.stripFilename();
10114 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10115 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10116 }
10117 if (RT_FAILURE(vrc))
10118 {
10119 rc = setError(E_FAIL,
10120 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10121 configDir.c_str(),
10122 newConfigDir.c_str(),
10123 vrc);
10124 break;
10125 }
10126 /* delete subdirectories which are no longer needed */
10127 Utf8Str dir(configDir);
10128 dir.stripFilename();
10129 while (dir != newConfigBaseDir && dir != ".")
10130 {
10131 vrc = RTDirRemove(dir.c_str());
10132 if (RT_FAILURE(vrc))
10133 break;
10134 dir.stripFilename();
10135 }
10136 dirRenamed = true;
10137 }
10138 }
10139
10140 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10141 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10142
10143 /* then try to rename the settings file itself */
10144 if (newConfigFile != configFile)
10145 {
10146 /* get the path to old settings file in renamed directory */
10147 configFile = Utf8StrFmt("%s%c%s",
10148 newConfigDir.c_str(),
10149 RTPATH_DELIMITER,
10150 RTPathFilename(configFile.c_str()));
10151 if (!fSettingsFileIsNew)
10152 {
10153 /* perform real rename only if the machine is not new */
10154 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10155 if (RT_FAILURE(vrc))
10156 {
10157 rc = setError(E_FAIL,
10158 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10159 configFile.c_str(),
10160 newConfigFile.c_str(),
10161 vrc);
10162 break;
10163 }
10164 fileRenamed = true;
10165 configFilePrev = configFile;
10166 configFilePrev += "-prev";
10167 newConfigFilePrev = newConfigFile;
10168 newConfigFilePrev += "-prev";
10169 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10170 }
10171 }
10172
10173 // update m_strConfigFileFull amd mConfigFile
10174 mData->m_strConfigFileFull = newConfigFile;
10175 // compute the relative path too
10176 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10177
10178 // store the old and new so that VirtualBox::saveSettings() can update
10179 // the media registry
10180 if ( mData->mRegistered
10181 && configDir != newConfigDir)
10182 {
10183 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10184
10185 if (pfNeedsGlobalSaveSettings)
10186 *pfNeedsGlobalSaveSettings = true;
10187 }
10188
10189 // in the saved state file path, replace the old directory with the new directory
10190 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10191 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10192
10193 // and do the same thing for the saved state file paths of all the online snapshots
10194 if (mData->mFirstSnapshot)
10195 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
10196 newConfigDir.c_str());
10197 }
10198 while (0);
10199
10200 if (FAILED(rc))
10201 {
10202 /* silently try to rename everything back */
10203 if (fileRenamed)
10204 {
10205 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10206 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10207 }
10208 if (dirRenamed)
10209 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10210 }
10211
10212 if (FAILED(rc)) return rc;
10213 }
10214
10215 if (fSettingsFileIsNew)
10216 {
10217 /* create a virgin config file */
10218 int vrc = VINF_SUCCESS;
10219
10220 /* ensure the settings directory exists */
10221 Utf8Str path(mData->m_strConfigFileFull);
10222 path.stripFilename();
10223 if (!RTDirExists(path.c_str()))
10224 {
10225 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10226 if (RT_FAILURE(vrc))
10227 {
10228 return setError(E_FAIL,
10229 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10230 path.c_str(),
10231 vrc);
10232 }
10233 }
10234
10235 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10236 path = Utf8Str(mData->m_strConfigFileFull);
10237 RTFILE f = NIL_RTFILE;
10238 vrc = RTFileOpen(&f, path.c_str(),
10239 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10240 if (RT_FAILURE(vrc))
10241 return setError(E_FAIL,
10242 tr("Could not create the settings file '%s' (%Rrc)"),
10243 path.c_str(),
10244 vrc);
10245 RTFileClose(f);
10246 }
10247
10248 return rc;
10249}
10250
10251/**
10252 * Saves and commits machine data, user data and hardware data.
10253 *
10254 * Note that on failure, the data remains uncommitted.
10255 *
10256 * @a aFlags may combine the following flags:
10257 *
10258 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10259 * Used when saving settings after an operation that makes them 100%
10260 * correspond to the settings from the current snapshot.
10261 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10262 * #isReallyModified() returns false. This is necessary for cases when we
10263 * change machine data directly, not through the backup()/commit() mechanism.
10264 * - SaveS_Force: settings will be saved without doing a deep compare of the
10265 * settings structures. This is used when this is called because snapshots
10266 * have changed to avoid the overhead of the deep compare.
10267 *
10268 * @note Must be called from under this object's write lock. Locks children for
10269 * writing.
10270 *
10271 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10272 * initialized to false and that will be set to true by this function if
10273 * the caller must invoke VirtualBox::saveSettings() because the global
10274 * settings have changed. This will happen if a machine rename has been
10275 * saved and the global machine and media registries will therefore need
10276 * updating.
10277 */
10278HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10279 int aFlags /*= 0*/)
10280{
10281 LogFlowThisFuncEnter();
10282
10283 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10284
10285 /* make sure child objects are unable to modify the settings while we are
10286 * saving them */
10287 ensureNoStateDependencies();
10288
10289 AssertReturn(!isSnapshotMachine(),
10290 E_FAIL);
10291
10292 HRESULT rc = S_OK;
10293 bool fNeedsWrite = false;
10294
10295 /* First, prepare to save settings. It will care about renaming the
10296 * settings directory and file if the machine name was changed and about
10297 * creating a new settings file if this is a new machine. */
10298 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10299 if (FAILED(rc)) return rc;
10300
10301 // keep a pointer to the current settings structures
10302 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10303 settings::MachineConfigFile *pNewConfig = NULL;
10304
10305 try
10306 {
10307 // make a fresh one to have everyone write stuff into
10308 pNewConfig = new settings::MachineConfigFile(NULL);
10309 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10310
10311 // now go and copy all the settings data from COM to the settings structures
10312 // (this calles saveSettings() on all the COM objects in the machine)
10313 copyMachineDataToSettings(*pNewConfig);
10314
10315 if (aFlags & SaveS_ResetCurStateModified)
10316 {
10317 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10318 mData->mCurrentStateModified = FALSE;
10319 fNeedsWrite = true; // always, no need to compare
10320 }
10321 else if (aFlags & SaveS_Force)
10322 {
10323 fNeedsWrite = true; // always, no need to compare
10324 }
10325 else
10326 {
10327 if (!mData->mCurrentStateModified)
10328 {
10329 // do a deep compare of the settings that we just saved with the settings
10330 // previously stored in the config file; this invokes MachineConfigFile::operator==
10331 // which does a deep compare of all the settings, which is expensive but less expensive
10332 // than writing out XML in vain
10333 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10334
10335 // could still be modified if any settings changed
10336 mData->mCurrentStateModified = fAnySettingsChanged;
10337
10338 fNeedsWrite = fAnySettingsChanged;
10339 }
10340 else
10341 fNeedsWrite = true;
10342 }
10343
10344 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10345
10346 if (fNeedsWrite)
10347 // now spit it all out!
10348 pNewConfig->write(mData->m_strConfigFileFull);
10349
10350 mData->pMachineConfigFile = pNewConfig;
10351 delete pOldConfig;
10352 commit();
10353
10354 // after saving settings, we are no longer different from the XML on disk
10355 mData->flModifications = 0;
10356 }
10357 catch (HRESULT err)
10358 {
10359 // we assume that error info is set by the thrower
10360 rc = err;
10361
10362 // restore old config
10363 delete pNewConfig;
10364 mData->pMachineConfigFile = pOldConfig;
10365 }
10366 catch (...)
10367 {
10368 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10369 }
10370
10371 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10372 {
10373 /* Fire the data change event, even on failure (since we've already
10374 * committed all data). This is done only for SessionMachines because
10375 * mutable Machine instances are always not registered (i.e. private
10376 * to the client process that creates them) and thus don't need to
10377 * inform callbacks. */
10378 if (isSessionMachine())
10379 mParent->onMachineDataChange(mData->mUuid);
10380 }
10381
10382 LogFlowThisFunc(("rc=%08X\n", rc));
10383 LogFlowThisFuncLeave();
10384 return rc;
10385}
10386
10387/**
10388 * Implementation for saving the machine settings into the given
10389 * settings::MachineConfigFile instance. This copies machine extradata
10390 * from the previous machine config file in the instance data, if any.
10391 *
10392 * This gets called from two locations:
10393 *
10394 * -- Machine::saveSettings(), during the regular XML writing;
10395 *
10396 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10397 * exported to OVF and we write the VirtualBox proprietary XML
10398 * into a <vbox:Machine> tag.
10399 *
10400 * This routine fills all the fields in there, including snapshots, *except*
10401 * for the following:
10402 *
10403 * -- fCurrentStateModified. There is some special logic associated with that.
10404 *
10405 * The caller can then call MachineConfigFile::write() or do something else
10406 * with it.
10407 *
10408 * Caller must hold the machine lock!
10409 *
10410 * This throws XML errors and HRESULT, so the caller must have a catch block!
10411 */
10412void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10413{
10414 // deep copy extradata
10415 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10416
10417 config.uuid = mData->mUuid;
10418
10419 // copy name, description, OS type, teleport, UTC etc.
10420 config.machineUserData = mUserData->s;
10421
10422 // Encode the Icon Override data from Machine and store on config userdata.
10423 com::SafeArray<BYTE> iconByte;
10424 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10425 ssize_t cbData = iconByte.size();
10426 if (cbData > 0)
10427 {
10428 ssize_t cchOut = RTBase64EncodedLength(cbData);
10429 Utf8Str strIconData;
10430 strIconData.reserve(cchOut+1);
10431 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10432 strIconData.mutableRaw(), strIconData.capacity(),
10433 NULL);
10434 if (RT_FAILURE(vrc))
10435 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10436 strIconData.jolt();
10437 config.machineUserData.ovIcon = strIconData;
10438 }
10439 else
10440 config.machineUserData.ovIcon.setNull();
10441
10442 if ( mData->mMachineState == MachineState_Saved
10443 || mData->mMachineState == MachineState_Restoring
10444 // when deleting a snapshot we may or may not have a saved state in the current state,
10445 // so let's not assert here please
10446 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10447 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10448 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10449 && (!mSSData->strStateFilePath.isEmpty())
10450 )
10451 )
10452 {
10453 Assert(!mSSData->strStateFilePath.isEmpty());
10454 /* try to make the file name relative to the settings file dir */
10455 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10456 }
10457 else
10458 {
10459 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10460 config.strStateFile.setNull();
10461 }
10462
10463 if (mData->mCurrentSnapshot)
10464 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10465 else
10466 config.uuidCurrentSnapshot.clear();
10467
10468 config.timeLastStateChange = mData->mLastStateChange;
10469 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10470 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10471
10472 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10473 if (FAILED(rc)) throw rc;
10474
10475 rc = saveStorageControllers(config.storageMachine);
10476 if (FAILED(rc)) throw rc;
10477
10478 // save machine's media registry if this is VirtualBox 4.0 or later
10479 if (config.canHaveOwnMediaRegistry())
10480 {
10481 // determine machine folder
10482 Utf8Str strMachineFolder = getSettingsFileFull();
10483 strMachineFolder.stripFilename();
10484 mParent->saveMediaRegistry(config.mediaRegistry,
10485 getId(), // only media with registry ID == machine UUID
10486 strMachineFolder);
10487 // this throws HRESULT
10488 }
10489
10490 // save snapshots
10491 rc = saveAllSnapshots(config);
10492 if (FAILED(rc)) throw rc;
10493}
10494
10495/**
10496 * Saves all snapshots of the machine into the given machine config file. Called
10497 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10498 * @param config
10499 * @return
10500 */
10501HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10502{
10503 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10504
10505 HRESULT rc = S_OK;
10506
10507 try
10508 {
10509 config.llFirstSnapshot.clear();
10510
10511 if (mData->mFirstSnapshot)
10512 {
10513 settings::Snapshot snapNew;
10514 config.llFirstSnapshot.push_back(snapNew);
10515
10516 // get reference to the fresh copy of the snapshot on the list and
10517 // work on that copy directly to avoid excessive copying later
10518 settings::Snapshot &snap = config.llFirstSnapshot.front();
10519
10520 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10521 if (FAILED(rc)) throw rc;
10522 }
10523
10524// if (mType == IsSessionMachine)
10525// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10526
10527 }
10528 catch (HRESULT err)
10529 {
10530 /* we assume that error info is set by the thrower */
10531 rc = err;
10532 }
10533 catch (...)
10534 {
10535 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10536 }
10537
10538 return rc;
10539}
10540
10541/**
10542 * Saves the VM hardware configuration. It is assumed that the
10543 * given node is empty.
10544 *
10545 * @param data Reference to the settings object for the hardware config.
10546 * @param pDbg Pointer to the settings object for the debugging config
10547 * which happens to live in mHWData.
10548 * @param pAutostart Pointer to the settings object for the autostart config
10549 * which happens to live in mHWData.
10550 */
10551HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10552 settings::Autostart *pAutostart)
10553{
10554 HRESULT rc = S_OK;
10555
10556 try
10557 {
10558 /* The hardware version attribute (optional).
10559 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10560 if ( mHWData->mHWVersion == "1"
10561 && mSSData->strStateFilePath.isEmpty()
10562 )
10563 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. */
10564
10565 data.strVersion = mHWData->mHWVersion;
10566 data.uuid = mHWData->mHardwareUUID;
10567
10568 // CPU
10569 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10570 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10571 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10572 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10573 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10574 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10575 data.fPAE = !!mHWData->mPAEEnabled;
10576 data.enmLongMode = mHWData->mLongMode;
10577 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10578 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10579
10580 /* Standard and Extended CPUID leafs. */
10581 data.llCpuIdLeafs.clear();
10582 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10583 {
10584 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10585 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10586 }
10587 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10588 {
10589 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10590 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10591 }
10592
10593 data.cCPUs = mHWData->mCPUCount;
10594 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10595 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10596
10597 data.llCpus.clear();
10598 if (data.fCpuHotPlug)
10599 {
10600 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10601 {
10602 if (mHWData->mCPUAttached[idx])
10603 {
10604 settings::Cpu cpu;
10605 cpu.ulId = idx;
10606 data.llCpus.push_back(cpu);
10607 }
10608 }
10609 }
10610
10611 // memory
10612 data.ulMemorySizeMB = mHWData->mMemorySize;
10613 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10614
10615 // firmware
10616 data.firmwareType = mHWData->mFirmwareType;
10617
10618 // HID
10619 data.pointingHIDType = mHWData->mPointingHIDType;
10620 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10621
10622 // chipset
10623 data.chipsetType = mHWData->mChipsetType;
10624
10625 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10626
10627 // HPET
10628 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10629
10630 // boot order
10631 data.mapBootOrder.clear();
10632 for (size_t i = 0;
10633 i < RT_ELEMENTS(mHWData->mBootOrder);
10634 ++i)
10635 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10636
10637 // display
10638 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10639 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10640 data.cMonitors = mHWData->mMonitorCount;
10641 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10642 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10643 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10644 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10645 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10646 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10647 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10648 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10649 {
10650 if (mHWData->maVideoCaptureScreens[i])
10651 ASMBitSet(&data.u64VideoCaptureScreens, i);
10652 else
10653 ASMBitClear(&data.u64VideoCaptureScreens, i);
10654 }
10655 /* store relative video capture file if possible */
10656 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10657
10658 /* VRDEServer settings (optional) */
10659 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10660 if (FAILED(rc)) throw rc;
10661
10662 /* BIOS (required) */
10663 rc = mBIOSSettings->saveSettings(data.biosSettings);
10664 if (FAILED(rc)) throw rc;
10665
10666 /* USB Controller (required) */
10667 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10668 it != mUSBControllers->end();
10669 ++it)
10670 {
10671 ComObjPtr<USBController> ctrl = *it;
10672 settings::USBController settingsCtrl;
10673
10674 settingsCtrl.strName = ctrl->getName();
10675 settingsCtrl.enmType = ctrl->getControllerType();
10676
10677 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10678 }
10679
10680 /* USB device filters (required) */
10681 rc = mUSBDeviceFilters->saveSettings(data.usbSettings);
10682 if (FAILED(rc)) throw rc;
10683
10684 /* Network adapters (required) */
10685 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10686 data.llNetworkAdapters.clear();
10687 /* Write out only the nominal number of network adapters for this
10688 * chipset type. Since Machine::commit() hasn't been called there
10689 * may be extra NIC settings in the vector. */
10690 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10691 {
10692 settings::NetworkAdapter nic;
10693 nic.ulSlot = slot;
10694 /* paranoia check... must not be NULL, but must not crash either. */
10695 if (mNetworkAdapters[slot])
10696 {
10697 rc = mNetworkAdapters[slot]->saveSettings(nic);
10698 if (FAILED(rc)) throw rc;
10699
10700 data.llNetworkAdapters.push_back(nic);
10701 }
10702 }
10703
10704 /* Serial ports */
10705 data.llSerialPorts.clear();
10706 for (ULONG slot = 0;
10707 slot < RT_ELEMENTS(mSerialPorts);
10708 ++slot)
10709 {
10710 settings::SerialPort s;
10711 s.ulSlot = slot;
10712 rc = mSerialPorts[slot]->i_saveSettings(s);
10713 if (FAILED(rc)) return rc;
10714
10715 data.llSerialPorts.push_back(s);
10716 }
10717
10718 /* Parallel ports */
10719 data.llParallelPorts.clear();
10720 for (ULONG slot = 0;
10721 slot < RT_ELEMENTS(mParallelPorts);
10722 ++slot)
10723 {
10724 settings::ParallelPort p;
10725 p.ulSlot = slot;
10726 rc = mParallelPorts[slot]->saveSettings(p);
10727 if (FAILED(rc)) return rc;
10728
10729 data.llParallelPorts.push_back(p);
10730 }
10731
10732 /* Audio adapter */
10733 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10734 if (FAILED(rc)) return rc;
10735
10736 /* Shared folders */
10737 data.llSharedFolders.clear();
10738 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10739 it != mHWData->mSharedFolders.end();
10740 ++it)
10741 {
10742 SharedFolder *pSF = *it;
10743 AutoCaller sfCaller(pSF);
10744 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10745 settings::SharedFolder sf;
10746 sf.strName = pSF->getName();
10747 sf.strHostPath = pSF->getHostPath();
10748 sf.fWritable = !!pSF->isWritable();
10749 sf.fAutoMount = !!pSF->isAutoMounted();
10750
10751 data.llSharedFolders.push_back(sf);
10752 }
10753
10754 // clipboard
10755 data.clipboardMode = mHWData->mClipboardMode;
10756
10757 // drag'n'drop
10758 data.dragAndDropMode = mHWData->mDragAndDropMode;
10759
10760 /* Guest */
10761 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10762
10763 // IO settings
10764 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10765 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10766
10767 /* BandwidthControl (required) */
10768 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10769 if (FAILED(rc)) throw rc;
10770
10771 /* Host PCI devices */
10772 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10773 it != mHWData->mPCIDeviceAssignments.end();
10774 ++it)
10775 {
10776 ComObjPtr<PCIDeviceAttachment> pda = *it;
10777 settings::HostPCIDeviceAttachment hpda;
10778
10779 rc = pda->saveSettings(hpda);
10780 if (FAILED(rc)) throw rc;
10781
10782 data.pciAttachments.push_back(hpda);
10783 }
10784
10785
10786 // guest properties
10787 data.llGuestProperties.clear();
10788#ifdef VBOX_WITH_GUEST_PROPS
10789 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10790 it != mHWData->mGuestProperties.end();
10791 ++it)
10792 {
10793 HWData::GuestProperty property = it->second;
10794
10795 /* Remove transient guest properties at shutdown unless we
10796 * are saving state */
10797 if ( ( mData->mMachineState == MachineState_PoweredOff
10798 || mData->mMachineState == MachineState_Aborted
10799 || mData->mMachineState == MachineState_Teleported)
10800 && ( property.mFlags & guestProp::TRANSIENT
10801 || property.mFlags & guestProp::TRANSRESET))
10802 continue;
10803 settings::GuestProperty prop;
10804 prop.strName = it->first;
10805 prop.strValue = property.strValue;
10806 prop.timestamp = property.mTimestamp;
10807 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10808 guestProp::writeFlags(property.mFlags, szFlags);
10809 prop.strFlags = szFlags;
10810
10811 data.llGuestProperties.push_back(prop);
10812 }
10813
10814 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10815 /* I presume this doesn't require a backup(). */
10816 mData->mGuestPropertiesModified = FALSE;
10817#endif /* VBOX_WITH_GUEST_PROPS defined */
10818
10819 *pDbg = mHWData->mDebugging;
10820 *pAutostart = mHWData->mAutostart;
10821
10822 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10823 }
10824 catch(std::bad_alloc &)
10825 {
10826 return E_OUTOFMEMORY;
10827 }
10828
10829 AssertComRC(rc);
10830 return rc;
10831}
10832
10833/**
10834 * Saves the storage controller configuration.
10835 *
10836 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10837 */
10838HRESULT Machine::saveStorageControllers(settings::Storage &data)
10839{
10840 data.llStorageControllers.clear();
10841
10842 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10843 it != mStorageControllers->end();
10844 ++it)
10845 {
10846 HRESULT rc;
10847 ComObjPtr<StorageController> pCtl = *it;
10848
10849 settings::StorageController ctl;
10850 ctl.strName = pCtl->i_getName();
10851 ctl.controllerType = pCtl->i_getControllerType();
10852 ctl.storageBus = pCtl->i_getStorageBus();
10853 ctl.ulInstance = pCtl->i_getInstance();
10854 ctl.fBootable = pCtl->i_getBootable();
10855
10856 /* Save the port count. */
10857 ULONG portCount;
10858 rc = pCtl->COMGETTER(PortCount)(&portCount);
10859 ComAssertComRCRet(rc, rc);
10860 ctl.ulPortCount = portCount;
10861
10862 /* Save fUseHostIOCache */
10863 BOOL fUseHostIOCache;
10864 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10865 ComAssertComRCRet(rc, rc);
10866 ctl.fUseHostIOCache = !!fUseHostIOCache;
10867
10868 /* Save IDE emulation settings. */
10869 if (ctl.controllerType == StorageControllerType_IntelAhci)
10870 {
10871 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10872 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10873 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10874 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10875 )
10876 ComAssertComRCRet(rc, rc);
10877 }
10878
10879 /* save the devices now. */
10880 rc = saveStorageDevices(pCtl, ctl);
10881 ComAssertComRCRet(rc, rc);
10882
10883 data.llStorageControllers.push_back(ctl);
10884 }
10885
10886 return S_OK;
10887}
10888
10889/**
10890 * Saves the hard disk configuration.
10891 */
10892HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10893 settings::StorageController &data)
10894{
10895 MediaData::AttachmentList atts;
10896
10897 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->i_getName()).raw(), atts);
10898 if (FAILED(rc)) return rc;
10899
10900 data.llAttachedDevices.clear();
10901 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10902 it != atts.end();
10903 ++it)
10904 {
10905 settings::AttachedDevice dev;
10906
10907 MediumAttachment *pAttach = *it;
10908 Medium *pMedium = pAttach->getMedium();
10909
10910 dev.deviceType = pAttach->getType();
10911 dev.lPort = pAttach->getPort();
10912 dev.lDevice = pAttach->getDevice();
10913 dev.fPassThrough = pAttach->getPassthrough();
10914 dev.fHotPluggable = pAttach->getHotPluggable();
10915 if (pMedium)
10916 {
10917 if (pMedium->isHostDrive())
10918 dev.strHostDriveSrc = pMedium->getLocationFull();
10919 else
10920 dev.uuid = pMedium->getId();
10921 dev.fTempEject = pAttach->getTempEject();
10922 dev.fNonRotational = pAttach->getNonRotational();
10923 dev.fDiscard = pAttach->getDiscard();
10924 }
10925
10926 dev.strBwGroup = pAttach->getBandwidthGroup();
10927
10928 data.llAttachedDevices.push_back(dev);
10929 }
10930
10931 return S_OK;
10932}
10933
10934/**
10935 * Saves machine state settings as defined by aFlags
10936 * (SaveSTS_* values).
10937 *
10938 * @param aFlags Combination of SaveSTS_* flags.
10939 *
10940 * @note Locks objects for writing.
10941 */
10942HRESULT Machine::saveStateSettings(int aFlags)
10943{
10944 if (aFlags == 0)
10945 return S_OK;
10946
10947 AutoCaller autoCaller(this);
10948 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10949
10950 /* This object's write lock is also necessary to serialize file access
10951 * (prevent concurrent reads and writes) */
10952 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10953
10954 HRESULT rc = S_OK;
10955
10956 Assert(mData->pMachineConfigFile);
10957
10958 try
10959 {
10960 if (aFlags & SaveSTS_CurStateModified)
10961 mData->pMachineConfigFile->fCurrentStateModified = true;
10962
10963 if (aFlags & SaveSTS_StateFilePath)
10964 {
10965 if (!mSSData->strStateFilePath.isEmpty())
10966 /* try to make the file name relative to the settings file dir */
10967 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10968 else
10969 mData->pMachineConfigFile->strStateFile.setNull();
10970 }
10971
10972 if (aFlags & SaveSTS_StateTimeStamp)
10973 {
10974 Assert( mData->mMachineState != MachineState_Aborted
10975 || mSSData->strStateFilePath.isEmpty());
10976
10977 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10978
10979 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10980//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10981 }
10982
10983 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10984 }
10985 catch (...)
10986 {
10987 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10988 }
10989
10990 return rc;
10991}
10992
10993/**
10994 * Ensures that the given medium is added to a media registry. If this machine
10995 * was created with 4.0 or later, then the machine registry is used. Otherwise
10996 * the global VirtualBox media registry is used.
10997 *
10998 * Caller must NOT hold machine lock, media tree or any medium locks!
10999 *
11000 * @param pMedium
11001 */
11002void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
11003{
11004 /* Paranoia checks: do not hold machine or media tree locks. */
11005 AssertReturnVoid(!isWriteLockOnCurrentThread());
11006 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
11007
11008 ComObjPtr<Medium> pBase;
11009 {
11010 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11011 pBase = pMedium->getBase();
11012 }
11013
11014 /* Paranoia checks: do not hold medium locks. */
11015 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
11016 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
11017
11018 // decide which medium registry to use now that the medium is attached:
11019 Guid uuid;
11020 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
11021 // machine XML is VirtualBox 4.0 or higher:
11022 uuid = getId(); // machine UUID
11023 else
11024 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
11025
11026 if (pMedium->addRegistry(uuid, false /* fRecurse */))
11027 mParent->markRegistryModified(uuid);
11028
11029 /* For more complex hard disk structures it can happen that the base
11030 * medium isn't yet associated with any medium registry. Do that now. */
11031 if (pMedium != pBase)
11032 {
11033 if (pBase->addRegistry(uuid, true /* fRecurse */))
11034 mParent->markRegistryModified(uuid);
11035 }
11036}
11037
11038/**
11039 * Creates differencing hard disks for all normal hard disks attached to this
11040 * machine and a new set of attachments to refer to created disks.
11041 *
11042 * Used when taking a snapshot or when deleting the current state. Gets called
11043 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
11044 *
11045 * This method assumes that mMediaData contains the original hard disk attachments
11046 * it needs to create diffs for. On success, these attachments will be replaced
11047 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
11048 * called to delete created diffs which will also rollback mMediaData and restore
11049 * whatever was backed up before calling this method.
11050 *
11051 * Attachments with non-normal hard disks are left as is.
11052 *
11053 * If @a aOnline is @c false then the original hard disks that require implicit
11054 * diffs will be locked for reading. Otherwise it is assumed that they are
11055 * already locked for writing (when the VM was started). Note that in the latter
11056 * case it is responsibility of the caller to lock the newly created diffs for
11057 * writing if this method succeeds.
11058 *
11059 * @param aProgress Progress object to run (must contain at least as
11060 * many operations left as the number of hard disks
11061 * attached).
11062 * @param aOnline Whether the VM was online prior to this operation.
11063 *
11064 * @note The progress object is not marked as completed, neither on success nor
11065 * on failure. This is a responsibility of the caller.
11066 *
11067 * @note Locks this object and the media tree for writing.
11068 */
11069HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
11070 ULONG aWeight,
11071 bool aOnline)
11072{
11073 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11074
11075 AutoCaller autoCaller(this);
11076 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11077
11078 AutoMultiWriteLock2 alock(this->lockHandle(),
11079 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11080
11081 /* must be in a protective state because we release the lock below */
11082 AssertReturn( mData->mMachineState == MachineState_Saving
11083 || mData->mMachineState == MachineState_LiveSnapshotting
11084 || mData->mMachineState == MachineState_RestoringSnapshot
11085 || mData->mMachineState == MachineState_DeletingSnapshot
11086 , E_FAIL);
11087
11088 HRESULT rc = S_OK;
11089
11090 // use appropriate locked media map (online or offline)
11091 MediumLockListMap lockedMediaOffline;
11092 MediumLockListMap *lockedMediaMap;
11093 if (aOnline)
11094 lockedMediaMap = &mData->mSession.mLockedMedia;
11095 else
11096 lockedMediaMap = &lockedMediaOffline;
11097
11098 try
11099 {
11100 if (!aOnline)
11101 {
11102 /* lock all attached hard disks early to detect "in use"
11103 * situations before creating actual diffs */
11104 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11105 it != mMediaData->mAttachments.end();
11106 ++it)
11107 {
11108 MediumAttachment* pAtt = *it;
11109 if (pAtt->getType() == DeviceType_HardDisk)
11110 {
11111 Medium* pMedium = pAtt->getMedium();
11112 Assert(pMedium);
11113
11114 MediumLockList *pMediumLockList(new MediumLockList());
11115 alock.release();
11116 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11117 false /* fMediumLockWrite */,
11118 NULL,
11119 *pMediumLockList);
11120 alock.acquire();
11121 if (FAILED(rc))
11122 {
11123 delete pMediumLockList;
11124 throw rc;
11125 }
11126 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11127 if (FAILED(rc))
11128 {
11129 throw setError(rc,
11130 tr("Collecting locking information for all attached media failed"));
11131 }
11132 }
11133 }
11134
11135 /* Now lock all media. If this fails, nothing is locked. */
11136 alock.release();
11137 rc = lockedMediaMap->Lock();
11138 alock.acquire();
11139 if (FAILED(rc))
11140 {
11141 throw setError(rc,
11142 tr("Locking of attached media failed"));
11143 }
11144 }
11145
11146 /* remember the current list (note that we don't use backup() since
11147 * mMediaData may be already backed up) */
11148 MediaData::AttachmentList atts = mMediaData->mAttachments;
11149
11150 /* start from scratch */
11151 mMediaData->mAttachments.clear();
11152
11153 /* go through remembered attachments and create diffs for normal hard
11154 * disks and attach them */
11155 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11156 it != atts.end();
11157 ++it)
11158 {
11159 MediumAttachment* pAtt = *it;
11160
11161 DeviceType_T devType = pAtt->getType();
11162 Medium* pMedium = pAtt->getMedium();
11163
11164 if ( devType != DeviceType_HardDisk
11165 || pMedium == NULL
11166 || pMedium->getType() != MediumType_Normal)
11167 {
11168 /* copy the attachment as is */
11169
11170 /** @todo the progress object created in Console::TakeSnaphot
11171 * only expects operations for hard disks. Later other
11172 * device types need to show up in the progress as well. */
11173 if (devType == DeviceType_HardDisk)
11174 {
11175 if (pMedium == NULL)
11176 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11177 aWeight); // weight
11178 else
11179 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11180 pMedium->getBase()->getName().c_str()).raw(),
11181 aWeight); // weight
11182 }
11183
11184 mMediaData->mAttachments.push_back(pAtt);
11185 continue;
11186 }
11187
11188 /* need a diff */
11189 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11190 pMedium->getBase()->getName().c_str()).raw(),
11191 aWeight); // weight
11192
11193 Utf8Str strFullSnapshotFolder;
11194 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11195
11196 ComObjPtr<Medium> diff;
11197 diff.createObject();
11198 // store the diff in the same registry as the parent
11199 // (this cannot fail here because we can't create implicit diffs for
11200 // unregistered images)
11201 Guid uuidRegistryParent;
11202 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
11203 Assert(fInRegistry); NOREF(fInRegistry);
11204 rc = diff->init(mParent,
11205 pMedium->getPreferredDiffFormat(),
11206 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11207 uuidRegistryParent);
11208 if (FAILED(rc)) throw rc;
11209
11210 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11211 * the push_back? Looks like we're going to release medium with the
11212 * wrong kind of lock (general issue with if we fail anywhere at all)
11213 * and an orphaned VDI in the snapshots folder. */
11214
11215 /* update the appropriate lock list */
11216 MediumLockList *pMediumLockList;
11217 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11218 AssertComRCThrowRC(rc);
11219 if (aOnline)
11220 {
11221 alock.release();
11222 /* The currently attached medium will be read-only, change
11223 * the lock type to read. */
11224 rc = pMediumLockList->Update(pMedium, false);
11225 alock.acquire();
11226 AssertComRCThrowRC(rc);
11227 }
11228
11229 /* release the locks before the potentially lengthy operation */
11230 alock.release();
11231 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
11232 pMediumLockList,
11233 NULL /* aProgress */,
11234 true /* aWait */);
11235 alock.acquire();
11236 if (FAILED(rc)) throw rc;
11237
11238 /* actual lock list update is done in Medium::commitMedia */
11239
11240 rc = diff->addBackReference(mData->mUuid);
11241 AssertComRCThrowRC(rc);
11242
11243 /* add a new attachment */
11244 ComObjPtr<MediumAttachment> attachment;
11245 attachment.createObject();
11246 rc = attachment->init(this,
11247 diff,
11248 pAtt->getControllerName(),
11249 pAtt->getPort(),
11250 pAtt->getDevice(),
11251 DeviceType_HardDisk,
11252 true /* aImplicit */,
11253 false /* aPassthrough */,
11254 false /* aTempEject */,
11255 pAtt->getNonRotational(),
11256 pAtt->getDiscard(),
11257 pAtt->getHotPluggable(),
11258 pAtt->getBandwidthGroup());
11259 if (FAILED(rc)) throw rc;
11260
11261 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11262 AssertComRCThrowRC(rc);
11263 mMediaData->mAttachments.push_back(attachment);
11264 }
11265 }
11266 catch (HRESULT aRC) { rc = aRC; }
11267
11268 /* unlock all hard disks we locked when there is no VM */
11269 if (!aOnline)
11270 {
11271 ErrorInfoKeeper eik;
11272
11273 HRESULT rc1 = lockedMediaMap->Clear();
11274 AssertComRC(rc1);
11275 }
11276
11277 return rc;
11278}
11279
11280/**
11281 * Deletes implicit differencing hard disks created either by
11282 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11283 *
11284 * Note that to delete hard disks created by #AttachDevice() this method is
11285 * called from #fixupMedia() when the changes are rolled back.
11286 *
11287 * @note Locks this object and the media tree for writing.
11288 */
11289HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11290{
11291 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11292
11293 AutoCaller autoCaller(this);
11294 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11295
11296 AutoMultiWriteLock2 alock(this->lockHandle(),
11297 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11298
11299 /* We absolutely must have backed up state. */
11300 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11301
11302 /* Check if there are any implicitly created diff images. */
11303 bool fImplicitDiffs = false;
11304 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11305 it != mMediaData->mAttachments.end();
11306 ++it)
11307 {
11308 const ComObjPtr<MediumAttachment> &pAtt = *it;
11309 if (pAtt->isImplicit())
11310 {
11311 fImplicitDiffs = true;
11312 break;
11313 }
11314 }
11315 /* If there is nothing to do, leave early. This saves lots of image locking
11316 * effort. It also avoids a MachineStateChanged event without real reason.
11317 * This is important e.g. when loading a VM config, because there should be
11318 * no events. Otherwise API clients can become thoroughly confused for
11319 * inaccessible VMs (the code for loading VM configs uses this method for
11320 * cleanup if the config makes no sense), as they take such events as an
11321 * indication that the VM is alive, and they would force the VM config to
11322 * be reread, leading to an endless loop. */
11323 if (!fImplicitDiffs)
11324 return S_OK;
11325
11326 HRESULT rc = S_OK;
11327 MachineState_T oldState = mData->mMachineState;
11328
11329 /* will release the lock before the potentially lengthy operation,
11330 * so protect with the special state (unless already protected) */
11331 if ( oldState != MachineState_Saving
11332 && oldState != MachineState_LiveSnapshotting
11333 && oldState != MachineState_RestoringSnapshot
11334 && oldState != MachineState_DeletingSnapshot
11335 && oldState != MachineState_DeletingSnapshotOnline
11336 && oldState != MachineState_DeletingSnapshotPaused
11337 )
11338 setMachineState(MachineState_SettingUp);
11339
11340 // use appropriate locked media map (online or offline)
11341 MediumLockListMap lockedMediaOffline;
11342 MediumLockListMap *lockedMediaMap;
11343 if (aOnline)
11344 lockedMediaMap = &mData->mSession.mLockedMedia;
11345 else
11346 lockedMediaMap = &lockedMediaOffline;
11347
11348 try
11349 {
11350 if (!aOnline)
11351 {
11352 /* lock all attached hard disks early to detect "in use"
11353 * situations before deleting actual diffs */
11354 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11355 it != mMediaData->mAttachments.end();
11356 ++it)
11357 {
11358 MediumAttachment* pAtt = *it;
11359 if (pAtt->getType() == DeviceType_HardDisk)
11360 {
11361 Medium* pMedium = pAtt->getMedium();
11362 Assert(pMedium);
11363
11364 MediumLockList *pMediumLockList(new MediumLockList());
11365 alock.release();
11366 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11367 false /* fMediumLockWrite */,
11368 NULL,
11369 *pMediumLockList);
11370 alock.acquire();
11371
11372 if (FAILED(rc))
11373 {
11374 delete pMediumLockList;
11375 throw rc;
11376 }
11377
11378 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11379 if (FAILED(rc))
11380 throw rc;
11381 }
11382 }
11383
11384 if (FAILED(rc))
11385 throw rc;
11386 } // end of offline
11387
11388 /* Lock lists are now up to date and include implicitly created media */
11389
11390 /* Go through remembered attachments and delete all implicitly created
11391 * diffs and fix up the attachment information */
11392 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11393 MediaData::AttachmentList implicitAtts;
11394 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11395 it != mMediaData->mAttachments.end();
11396 ++it)
11397 {
11398 ComObjPtr<MediumAttachment> pAtt = *it;
11399 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11400 if (pMedium.isNull())
11401 continue;
11402
11403 // Implicit attachments go on the list for deletion and back references are removed.
11404 if (pAtt->isImplicit())
11405 {
11406 /* Deassociate and mark for deletion */
11407 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11408 rc = pMedium->removeBackReference(mData->mUuid);
11409 if (FAILED(rc))
11410 throw rc;
11411 implicitAtts.push_back(pAtt);
11412 continue;
11413 }
11414
11415 /* Was this medium attached before? */
11416 if (!findAttachment(oldAtts, pMedium))
11417 {
11418 /* no: de-associate */
11419 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11420 rc = pMedium->removeBackReference(mData->mUuid);
11421 if (FAILED(rc))
11422 throw rc;
11423 continue;
11424 }
11425 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11426 }
11427
11428 /* If there are implicit attachments to delete, throw away the lock
11429 * map contents (which will unlock all media) since the medium
11430 * attachments will be rolled back. Below we need to completely
11431 * recreate the lock map anyway since it is infinitely complex to
11432 * do this incrementally (would need reconstructing each attachment
11433 * change, which would be extremely hairy). */
11434 if (implicitAtts.size() != 0)
11435 {
11436 ErrorInfoKeeper eik;
11437
11438 HRESULT rc1 = lockedMediaMap->Clear();
11439 AssertComRC(rc1);
11440 }
11441
11442 /* rollback hard disk changes */
11443 mMediaData.rollback();
11444
11445 MultiResult mrc(S_OK);
11446
11447 // Delete unused implicit diffs.
11448 if (implicitAtts.size() != 0)
11449 {
11450 alock.release();
11451
11452 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11453 it != implicitAtts.end();
11454 ++it)
11455 {
11456 // Remove medium associated with this attachment.
11457 ComObjPtr<MediumAttachment> pAtt = *it;
11458 Assert(pAtt);
11459 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11460 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11461 Assert(pMedium);
11462
11463 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11464 // continue on delete failure, just collect error messages
11465 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11466 mrc = rc;
11467 }
11468
11469 alock.acquire();
11470
11471 /* if there is a VM recreate media lock map as mentioned above,
11472 * otherwise it is a waste of time and we leave things unlocked */
11473 if (aOnline)
11474 {
11475 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11476 /* must never be NULL, but better safe than sorry */
11477 if (!pMachine.isNull())
11478 {
11479 alock.release();
11480 rc = mData->mSession.mMachine->lockMedia();
11481 alock.acquire();
11482 if (FAILED(rc))
11483 throw rc;
11484 }
11485 }
11486 }
11487 }
11488 catch (HRESULT aRC) {rc = aRC;}
11489
11490 if (mData->mMachineState == MachineState_SettingUp)
11491 setMachineState(oldState);
11492
11493 /* unlock all hard disks we locked when there is no VM */
11494 if (!aOnline)
11495 {
11496 ErrorInfoKeeper eik;
11497
11498 HRESULT rc1 = lockedMediaMap->Clear();
11499 AssertComRC(rc1);
11500 }
11501
11502 return rc;
11503}
11504
11505
11506/**
11507 * Looks through the given list of media attachments for one with the given parameters
11508 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11509 * can be searched as well if needed.
11510 *
11511 * @param list
11512 * @param aControllerName
11513 * @param aControllerPort
11514 * @param aDevice
11515 * @return
11516 */
11517MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11518 IN_BSTR aControllerName,
11519 LONG aControllerPort,
11520 LONG aDevice)
11521{
11522 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11523 it != ll.end();
11524 ++it)
11525 {
11526 MediumAttachment *pAttach = *it;
11527 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11528 return pAttach;
11529 }
11530
11531 return NULL;
11532}
11533
11534/**
11535 * Looks through the given list of media attachments for one with the given parameters
11536 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11537 * can be searched as well if needed.
11538 *
11539 * @param list
11540 * @param aControllerName
11541 * @param aControllerPort
11542 * @param aDevice
11543 * @return
11544 */
11545MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11546 ComObjPtr<Medium> pMedium)
11547{
11548 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11549 it != ll.end();
11550 ++it)
11551 {
11552 MediumAttachment *pAttach = *it;
11553 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11554 if (pMediumThis == pMedium)
11555 return pAttach;
11556 }
11557
11558 return NULL;
11559}
11560
11561/**
11562 * Looks through the given list of media attachments for one with the given parameters
11563 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11564 * can be searched as well if needed.
11565 *
11566 * @param list
11567 * @param aControllerName
11568 * @param aControllerPort
11569 * @param aDevice
11570 * @return
11571 */
11572MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11573 Guid &id)
11574{
11575 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11576 it != ll.end();
11577 ++it)
11578 {
11579 MediumAttachment *pAttach = *it;
11580 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11581 if (pMediumThis->getId() == id)
11582 return pAttach;
11583 }
11584
11585 return NULL;
11586}
11587
11588/**
11589 * Main implementation for Machine::DetachDevice. This also gets called
11590 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11591 *
11592 * @param pAttach Medium attachment to detach.
11593 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11594 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11595 * @return
11596 */
11597HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11598 AutoWriteLock &writeLock,
11599 Snapshot *pSnapshot)
11600{
11601 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11602 DeviceType_T mediumType = pAttach->getType();
11603
11604 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11605
11606 if (pAttach->isImplicit())
11607 {
11608 /* attempt to implicitly delete the implicitly created diff */
11609
11610 /// @todo move the implicit flag from MediumAttachment to Medium
11611 /// and forbid any hard disk operation when it is implicit. Or maybe
11612 /// a special media state for it to make it even more simple.
11613
11614 Assert(mMediaData.isBackedUp());
11615
11616 /* will release the lock before the potentially lengthy operation, so
11617 * protect with the special state */
11618 MachineState_T oldState = mData->mMachineState;
11619 setMachineState(MachineState_SettingUp);
11620
11621 writeLock.release();
11622
11623 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11624 true /*aWait*/);
11625
11626 writeLock.acquire();
11627
11628 setMachineState(oldState);
11629
11630 if (FAILED(rc)) return rc;
11631 }
11632
11633 setModified(IsModified_Storage);
11634 mMediaData.backup();
11635 mMediaData->mAttachments.remove(pAttach);
11636
11637 if (!oldmedium.isNull())
11638 {
11639 // if this is from a snapshot, do not defer detachment to commitMedia()
11640 if (pSnapshot)
11641 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11642 // else if non-hard disk media, do not defer detachment to commitMedia() either
11643 else if (mediumType != DeviceType_HardDisk)
11644 oldmedium->removeBackReference(mData->mUuid);
11645 }
11646
11647 return S_OK;
11648}
11649
11650/**
11651 * Goes thru all media of the given list and
11652 *
11653 * 1) calls detachDevice() on each of them for this machine and
11654 * 2) adds all Medium objects found in the process to the given list,
11655 * depending on cleanupMode.
11656 *
11657 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11658 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11659 * media to the list.
11660 *
11661 * This gets called from Machine::Unregister, both for the actual Machine and
11662 * the SnapshotMachine objects that might be found in the snapshots.
11663 *
11664 * Requires caller and locking. The machine lock must be passed in because it
11665 * will be passed on to detachDevice which needs it for temporary unlocking.
11666 *
11667 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11668 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11669 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11670 * otherwise no media get added.
11671 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11672 * @return
11673 */
11674HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11675 Snapshot *pSnapshot,
11676 CleanupMode_T cleanupMode,
11677 MediaList &llMedia)
11678{
11679 Assert(isWriteLockOnCurrentThread());
11680
11681 HRESULT rc;
11682
11683 // make a temporary list because detachDevice invalidates iterators into
11684 // mMediaData->mAttachments
11685 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11686
11687 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11688 it != llAttachments2.end();
11689 ++it)
11690 {
11691 ComObjPtr<MediumAttachment> &pAttach = *it;
11692 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11693
11694 if (!pMedium.isNull())
11695 {
11696 AutoCaller mac(pMedium);
11697 if (FAILED(mac.rc())) return mac.rc();
11698 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11699 DeviceType_T devType = pMedium->getDeviceType();
11700 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11701 && devType == DeviceType_HardDisk)
11702 || (cleanupMode == CleanupMode_Full)
11703 )
11704 {
11705 llMedia.push_back(pMedium);
11706 ComObjPtr<Medium> pParent = pMedium->getParent();
11707 /*
11708 * Search for medias which are not attached to any machine, but
11709 * in the chain to an attached disk. Mediums are only consided
11710 * if they are:
11711 * - have only one child
11712 * - no references to any machines
11713 * - are of normal medium type
11714 */
11715 while (!pParent.isNull())
11716 {
11717 AutoCaller mac1(pParent);
11718 if (FAILED(mac1.rc())) return mac1.rc();
11719 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11720 if (pParent->getChildren().size() == 1)
11721 {
11722 if ( pParent->getMachineBackRefCount() == 0
11723 && pParent->getType() == MediumType_Normal
11724 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11725 llMedia.push_back(pParent);
11726 }
11727 else
11728 break;
11729 pParent = pParent->getParent();
11730 }
11731 }
11732 }
11733
11734 // real machine: then we need to use the proper method
11735 rc = detachDevice(pAttach, writeLock, pSnapshot);
11736
11737 if (FAILED(rc))
11738 return rc;
11739 }
11740
11741 return S_OK;
11742}
11743
11744/**
11745 * Perform deferred hard disk detachments.
11746 *
11747 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11748 * backed up).
11749 *
11750 * If @a aOnline is @c true then this method will also unlock the old hard disks
11751 * for which the new implicit diffs were created and will lock these new diffs for
11752 * writing.
11753 *
11754 * @param aOnline Whether the VM was online prior to this operation.
11755 *
11756 * @note Locks this object for writing!
11757 */
11758void Machine::commitMedia(bool aOnline /*= false*/)
11759{
11760 AutoCaller autoCaller(this);
11761 AssertComRCReturnVoid(autoCaller.rc());
11762
11763 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11764
11765 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11766
11767 HRESULT rc = S_OK;
11768
11769 /* no attach/detach operations -- nothing to do */
11770 if (!mMediaData.isBackedUp())
11771 return;
11772
11773 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11774 bool fMediaNeedsLocking = false;
11775
11776 /* enumerate new attachments */
11777 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11778 it != mMediaData->mAttachments.end();
11779 ++it)
11780 {
11781 MediumAttachment *pAttach = *it;
11782
11783 pAttach->commit();
11784
11785 Medium* pMedium = pAttach->getMedium();
11786 bool fImplicit = pAttach->isImplicit();
11787
11788 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11789 (pMedium) ? pMedium->getName().c_str() : "NULL",
11790 fImplicit));
11791
11792 /** @todo convert all this Machine-based voodoo to MediumAttachment
11793 * based commit logic. */
11794 if (fImplicit)
11795 {
11796 /* convert implicit attachment to normal */
11797 pAttach->setImplicit(false);
11798
11799 if ( aOnline
11800 && pMedium
11801 && pAttach->getType() == DeviceType_HardDisk
11802 )
11803 {
11804 ComObjPtr<Medium> parent = pMedium->getParent();
11805 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11806
11807 /* update the appropriate lock list */
11808 MediumLockList *pMediumLockList;
11809 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11810 AssertComRC(rc);
11811 if (pMediumLockList)
11812 {
11813 /* unlock if there's a need to change the locking */
11814 if (!fMediaNeedsLocking)
11815 {
11816 rc = mData->mSession.mLockedMedia.Unlock();
11817 AssertComRC(rc);
11818 fMediaNeedsLocking = true;
11819 }
11820 rc = pMediumLockList->Update(parent, false);
11821 AssertComRC(rc);
11822 rc = pMediumLockList->Append(pMedium, true);
11823 AssertComRC(rc);
11824 }
11825 }
11826
11827 continue;
11828 }
11829
11830 if (pMedium)
11831 {
11832 /* was this medium attached before? */
11833 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11834 oldIt != oldAtts.end();
11835 ++oldIt)
11836 {
11837 MediumAttachment *pOldAttach = *oldIt;
11838 if (pOldAttach->getMedium() == pMedium)
11839 {
11840 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11841
11842 /* yes: remove from old to avoid de-association */
11843 oldAtts.erase(oldIt);
11844 break;
11845 }
11846 }
11847 }
11848 }
11849
11850 /* enumerate remaining old attachments and de-associate from the
11851 * current machine state */
11852 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11853 it != oldAtts.end();
11854 ++it)
11855 {
11856 MediumAttachment *pAttach = *it;
11857 Medium* pMedium = pAttach->getMedium();
11858
11859 /* Detach only hard disks, since DVD/floppy media is detached
11860 * instantly in MountMedium. */
11861 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11862 {
11863 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11864
11865 /* now de-associate from the current machine state */
11866 rc = pMedium->removeBackReference(mData->mUuid);
11867 AssertComRC(rc);
11868
11869 if (aOnline)
11870 {
11871 /* unlock since medium is not used anymore */
11872 MediumLockList *pMediumLockList;
11873 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11874 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11875 {
11876 /* this happens for online snapshots, there the attachment
11877 * is changing, but only to a diff image created under
11878 * the old one, so there is no separate lock list */
11879 Assert(!pMediumLockList);
11880 }
11881 else
11882 {
11883 AssertComRC(rc);
11884 if (pMediumLockList)
11885 {
11886 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11887 AssertComRC(rc);
11888 }
11889 }
11890 }
11891 }
11892 }
11893
11894 /* take media locks again so that the locking state is consistent */
11895 if (fMediaNeedsLocking)
11896 {
11897 Assert(aOnline);
11898 rc = mData->mSession.mLockedMedia.Lock();
11899 AssertComRC(rc);
11900 }
11901
11902 /* commit the hard disk changes */
11903 mMediaData.commit();
11904
11905 if (isSessionMachine())
11906 {
11907 /*
11908 * Update the parent machine to point to the new owner.
11909 * This is necessary because the stored parent will point to the
11910 * session machine otherwise and cause crashes or errors later
11911 * when the session machine gets invalid.
11912 */
11913 /** @todo Change the MediumAttachment class to behave like any other
11914 * class in this regard by creating peer MediumAttachment
11915 * objects for session machines and share the data with the peer
11916 * machine.
11917 */
11918 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11919 it != mMediaData->mAttachments.end();
11920 ++it)
11921 {
11922 (*it)->updateParentMachine(mPeer);
11923 }
11924
11925 /* attach new data to the primary machine and reshare it */
11926 mPeer->mMediaData.attach(mMediaData);
11927 }
11928
11929 return;
11930}
11931
11932/**
11933 * Perform deferred deletion of implicitly created diffs.
11934 *
11935 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11936 * backed up).
11937 *
11938 * @note Locks this object for writing!
11939 */
11940void Machine::rollbackMedia()
11941{
11942 AutoCaller autoCaller(this);
11943 AssertComRCReturnVoid(autoCaller.rc());
11944
11945 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11946 LogFlowThisFunc(("Entering rollbackMedia\n"));
11947
11948 HRESULT rc = S_OK;
11949
11950 /* no attach/detach operations -- nothing to do */
11951 if (!mMediaData.isBackedUp())
11952 return;
11953
11954 /* enumerate new attachments */
11955 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11956 it != mMediaData->mAttachments.end();
11957 ++it)
11958 {
11959 MediumAttachment *pAttach = *it;
11960 /* Fix up the backrefs for DVD/floppy media. */
11961 if (pAttach->getType() != DeviceType_HardDisk)
11962 {
11963 Medium* pMedium = pAttach->getMedium();
11964 if (pMedium)
11965 {
11966 rc = pMedium->removeBackReference(mData->mUuid);
11967 AssertComRC(rc);
11968 }
11969 }
11970
11971 (*it)->rollback();
11972
11973 pAttach = *it;
11974 /* Fix up the backrefs for DVD/floppy media. */
11975 if (pAttach->getType() != DeviceType_HardDisk)
11976 {
11977 Medium* pMedium = pAttach->getMedium();
11978 if (pMedium)
11979 {
11980 rc = pMedium->addBackReference(mData->mUuid);
11981 AssertComRC(rc);
11982 }
11983 }
11984 }
11985
11986 /** @todo convert all this Machine-based voodoo to MediumAttachment
11987 * based rollback logic. */
11988 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11989
11990 return;
11991}
11992
11993/**
11994 * Returns true if the settings file is located in the directory named exactly
11995 * as the machine; this means, among other things, that the machine directory
11996 * should be auto-renamed.
11997 *
11998 * @param aSettingsDir if not NULL, the full machine settings file directory
11999 * name will be assigned there.
12000 *
12001 * @note Doesn't lock anything.
12002 * @note Not thread safe (must be called from this object's lock).
12003 */
12004bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
12005{
12006 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12007 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
12008 if (aSettingsDir)
12009 *aSettingsDir = strMachineDirName;
12010 strMachineDirName.stripPath(); // vmname
12011 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
12012 strConfigFileOnly.stripPath() // vmname.vbox
12013 .stripSuffix(); // vmname
12014 /** @todo hack, make somehow use of ComposeMachineFilename */
12015 if (mUserData->s.fDirectoryIncludesUUID)
12016 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
12017
12018 AssertReturn(!strMachineDirName.isEmpty(), false);
12019 AssertReturn(!strConfigFileOnly.isEmpty(), false);
12020
12021 return strMachineDirName == strConfigFileOnly;
12022}
12023
12024/**
12025 * Discards all changes to machine settings.
12026 *
12027 * @param aNotify Whether to notify the direct session about changes or not.
12028 *
12029 * @note Locks objects for writing!
12030 */
12031void Machine::rollback(bool aNotify)
12032{
12033 AutoCaller autoCaller(this);
12034 AssertComRCReturn(autoCaller.rc(), (void)0);
12035
12036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12037
12038 if (!mStorageControllers.isNull())
12039 {
12040 if (mStorageControllers.isBackedUp())
12041 {
12042 /* unitialize all new devices (absent in the backed up list). */
12043 StorageControllerList::const_iterator it = mStorageControllers->begin();
12044 StorageControllerList *backedList = mStorageControllers.backedUpData();
12045 while (it != mStorageControllers->end())
12046 {
12047 if ( std::find(backedList->begin(), backedList->end(), *it)
12048 == backedList->end()
12049 )
12050 {
12051 (*it)->uninit();
12052 }
12053 ++it;
12054 }
12055
12056 /* restore the list */
12057 mStorageControllers.rollback();
12058 }
12059
12060 /* rollback any changes to devices after restoring the list */
12061 if (mData->flModifications & IsModified_Storage)
12062 {
12063 StorageControllerList::const_iterator it = mStorageControllers->begin();
12064 while (it != mStorageControllers->end())
12065 {
12066 (*it)->i_rollback();
12067 ++it;
12068 }
12069 }
12070 }
12071
12072 if (!mUSBControllers.isNull())
12073 {
12074 if (mUSBControllers.isBackedUp())
12075 {
12076 /* unitialize all new devices (absent in the backed up list). */
12077 USBControllerList::const_iterator it = mUSBControllers->begin();
12078 USBControllerList *backedList = mUSBControllers.backedUpData();
12079 while (it != mUSBControllers->end())
12080 {
12081 if ( std::find(backedList->begin(), backedList->end(), *it)
12082 == backedList->end()
12083 )
12084 {
12085 (*it)->uninit();
12086 }
12087 ++it;
12088 }
12089
12090 /* restore the list */
12091 mUSBControllers.rollback();
12092 }
12093
12094 /* rollback any changes to devices after restoring the list */
12095 if (mData->flModifications & IsModified_USB)
12096 {
12097 USBControllerList::const_iterator it = mUSBControllers->begin();
12098 while (it != mUSBControllers->end())
12099 {
12100 (*it)->rollback();
12101 ++it;
12102 }
12103 }
12104 }
12105
12106 mUserData.rollback();
12107
12108 mHWData.rollback();
12109
12110 if (mData->flModifications & IsModified_Storage)
12111 rollbackMedia();
12112
12113 if (mBIOSSettings)
12114 mBIOSSettings->rollback();
12115
12116 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12117 mVRDEServer->i_rollback();
12118
12119 if (mAudioAdapter)
12120 mAudioAdapter->rollback();
12121
12122 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12123 mUSBDeviceFilters->rollback();
12124
12125 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12126 mBandwidthControl->i_rollback();
12127
12128 if (!mHWData.isNull())
12129 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12130 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12131 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12132 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12133
12134 if (mData->flModifications & IsModified_NetworkAdapters)
12135 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12136 if ( mNetworkAdapters[slot]
12137 && mNetworkAdapters[slot]->isModified())
12138 {
12139 mNetworkAdapters[slot]->rollback();
12140 networkAdapters[slot] = mNetworkAdapters[slot];
12141 }
12142
12143 if (mData->flModifications & IsModified_SerialPorts)
12144 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12145 if ( mSerialPorts[slot]
12146 && mSerialPorts[slot]->i_isModified())
12147 {
12148 mSerialPorts[slot]->i_rollback();
12149 serialPorts[slot] = mSerialPorts[slot];
12150 }
12151
12152 if (mData->flModifications & IsModified_ParallelPorts)
12153 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12154 if ( mParallelPorts[slot]
12155 && mParallelPorts[slot]->isModified())
12156 {
12157 mParallelPorts[slot]->rollback();
12158 parallelPorts[slot] = mParallelPorts[slot];
12159 }
12160
12161 if (aNotify)
12162 {
12163 /* inform the direct session about changes */
12164
12165 ComObjPtr<Machine> that = this;
12166 uint32_t flModifications = mData->flModifications;
12167 alock.release();
12168
12169 if (flModifications & IsModified_SharedFolders)
12170 that->onSharedFolderChange();
12171
12172 if (flModifications & IsModified_VRDEServer)
12173 that->onVRDEServerChange(/* aRestart */ TRUE);
12174 if (flModifications & IsModified_USB)
12175 that->onUSBControllerChange();
12176
12177 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12178 if (networkAdapters[slot])
12179 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12180 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12181 if (serialPorts[slot])
12182 that->onSerialPortChange(serialPorts[slot]);
12183 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12184 if (parallelPorts[slot])
12185 that->onParallelPortChange(parallelPorts[slot]);
12186
12187 if (flModifications & IsModified_Storage)
12188 that->onStorageControllerChange();
12189
12190#if 0
12191 if (flModifications & IsModified_BandwidthControl)
12192 that->onBandwidthControlChange();
12193#endif
12194 }
12195}
12196
12197/**
12198 * Commits all the changes to machine settings.
12199 *
12200 * Note that this operation is supposed to never fail.
12201 *
12202 * @note Locks this object and children for writing.
12203 */
12204void Machine::commit()
12205{
12206 AutoCaller autoCaller(this);
12207 AssertComRCReturnVoid(autoCaller.rc());
12208
12209 AutoCaller peerCaller(mPeer);
12210 AssertComRCReturnVoid(peerCaller.rc());
12211
12212 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12213
12214 /*
12215 * use safe commit to ensure Snapshot machines (that share mUserData)
12216 * will still refer to a valid memory location
12217 */
12218 mUserData.commitCopy();
12219
12220 mHWData.commit();
12221
12222 if (mMediaData.isBackedUp())
12223 commitMedia(Global::IsOnline(mData->mMachineState));
12224
12225 mBIOSSettings->commit();
12226 mVRDEServer->i_commit();
12227 mAudioAdapter->commit();
12228 mUSBDeviceFilters->commit();
12229 mBandwidthControl->i_commit();
12230
12231 /* Since mNetworkAdapters is a list which might have been changed (resized)
12232 * without using the Backupable<> template we need to handle the copying
12233 * of the list entries manually, including the creation of peers for the
12234 * new objects. */
12235 bool commitNetworkAdapters = false;
12236 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12237 if (mPeer)
12238 {
12239 /* commit everything, even the ones which will go away */
12240 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12241 mNetworkAdapters[slot]->commit();
12242 /* copy over the new entries, creating a peer and uninit the original */
12243 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12244 for (size_t slot = 0; slot < newSize; slot++)
12245 {
12246 /* look if this adapter has a peer device */
12247 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
12248 if (!peer)
12249 {
12250 /* no peer means the adapter is a newly created one;
12251 * create a peer owning data this data share it with */
12252 peer.createObject();
12253 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12254 }
12255 mPeer->mNetworkAdapters[slot] = peer;
12256 }
12257 /* uninit any no longer needed network adapters */
12258 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12259 mNetworkAdapters[slot]->uninit();
12260 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12261 {
12262 if (mPeer->mNetworkAdapters[slot])
12263 mPeer->mNetworkAdapters[slot]->uninit();
12264 }
12265 /* Keep the original network adapter count until this point, so that
12266 * discarding a chipset type change will not lose settings. */
12267 mNetworkAdapters.resize(newSize);
12268 mPeer->mNetworkAdapters.resize(newSize);
12269 }
12270 else
12271 {
12272 /* we have no peer (our parent is the newly created machine);
12273 * just commit changes to the network adapters */
12274 commitNetworkAdapters = true;
12275 }
12276 if (commitNetworkAdapters)
12277 {
12278 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12279 mNetworkAdapters[slot]->commit();
12280 }
12281
12282 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12283 mSerialPorts[slot]->i_commit();
12284 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12285 mParallelPorts[slot]->commit();
12286
12287 bool commitStorageControllers = false;
12288
12289 if (mStorageControllers.isBackedUp())
12290 {
12291 mStorageControllers.commit();
12292
12293 if (mPeer)
12294 {
12295 /* Commit all changes to new controllers (this will reshare data with
12296 * peers for those who have peers) */
12297 StorageControllerList *newList = new StorageControllerList();
12298 StorageControllerList::const_iterator it = mStorageControllers->begin();
12299 while (it != mStorageControllers->end())
12300 {
12301 (*it)->i_commit();
12302
12303 /* look if this controller has a peer device */
12304 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12305 if (!peer)
12306 {
12307 /* no peer means the device is a newly created one;
12308 * create a peer owning data this device share it with */
12309 peer.createObject();
12310 peer->init(mPeer, *it, true /* aReshare */);
12311 }
12312 else
12313 {
12314 /* remove peer from the old list */
12315 mPeer->mStorageControllers->remove(peer);
12316 }
12317 /* and add it to the new list */
12318 newList->push_back(peer);
12319
12320 ++it;
12321 }
12322
12323 /* uninit old peer's controllers that are left */
12324 it = mPeer->mStorageControllers->begin();
12325 while (it != mPeer->mStorageControllers->end())
12326 {
12327 (*it)->uninit();
12328 ++it;
12329 }
12330
12331 /* attach new list of controllers to our peer */
12332 mPeer->mStorageControllers.attach(newList);
12333 }
12334 else
12335 {
12336 /* we have no peer (our parent is the newly created machine);
12337 * just commit changes to devices */
12338 commitStorageControllers = true;
12339 }
12340 }
12341 else
12342 {
12343 /* the list of controllers itself is not changed,
12344 * just commit changes to controllers themselves */
12345 commitStorageControllers = true;
12346 }
12347
12348 if (commitStorageControllers)
12349 {
12350 StorageControllerList::const_iterator it = mStorageControllers->begin();
12351 while (it != mStorageControllers->end())
12352 {
12353 (*it)->i_commit();
12354 ++it;
12355 }
12356 }
12357
12358 bool commitUSBControllers = false;
12359
12360 if (mUSBControllers.isBackedUp())
12361 {
12362 mUSBControllers.commit();
12363
12364 if (mPeer)
12365 {
12366 /* Commit all changes to new controllers (this will reshare data with
12367 * peers for those who have peers) */
12368 USBControllerList *newList = new USBControllerList();
12369 USBControllerList::const_iterator it = mUSBControllers->begin();
12370 while (it != mUSBControllers->end())
12371 {
12372 (*it)->commit();
12373
12374 /* look if this controller has a peer device */
12375 ComObjPtr<USBController> peer = (*it)->getPeer();
12376 if (!peer)
12377 {
12378 /* no peer means the device is a newly created one;
12379 * create a peer owning data this device share it with */
12380 peer.createObject();
12381 peer->init(mPeer, *it, true /* aReshare */);
12382 }
12383 else
12384 {
12385 /* remove peer from the old list */
12386 mPeer->mUSBControllers->remove(peer);
12387 }
12388 /* and add it to the new list */
12389 newList->push_back(peer);
12390
12391 ++it;
12392 }
12393
12394 /* uninit old peer's controllers that are left */
12395 it = mPeer->mUSBControllers->begin();
12396 while (it != mPeer->mUSBControllers->end())
12397 {
12398 (*it)->uninit();
12399 ++it;
12400 }
12401
12402 /* attach new list of controllers to our peer */
12403 mPeer->mUSBControllers.attach(newList);
12404 }
12405 else
12406 {
12407 /* we have no peer (our parent is the newly created machine);
12408 * just commit changes to devices */
12409 commitUSBControllers = true;
12410 }
12411 }
12412 else
12413 {
12414 /* the list of controllers itself is not changed,
12415 * just commit changes to controllers themselves */
12416 commitUSBControllers = true;
12417 }
12418
12419 if (commitUSBControllers)
12420 {
12421 USBControllerList::const_iterator it = mUSBControllers->begin();
12422 while (it != mUSBControllers->end())
12423 {
12424 (*it)->commit();
12425 ++it;
12426 }
12427 }
12428
12429 if (isSessionMachine())
12430 {
12431 /* attach new data to the primary machine and reshare it */
12432 mPeer->mUserData.attach(mUserData);
12433 mPeer->mHWData.attach(mHWData);
12434 /* mMediaData is reshared by fixupMedia */
12435 // mPeer->mMediaData.attach(mMediaData);
12436 Assert(mPeer->mMediaData.data() == mMediaData.data());
12437 }
12438}
12439
12440/**
12441 * Copies all the hardware data from the given machine.
12442 *
12443 * Currently, only called when the VM is being restored from a snapshot. In
12444 * particular, this implies that the VM is not running during this method's
12445 * call.
12446 *
12447 * @note This method must be called from under this object's lock.
12448 *
12449 * @note This method doesn't call #commit(), so all data remains backed up and
12450 * unsaved.
12451 */
12452void Machine::copyFrom(Machine *aThat)
12453{
12454 AssertReturnVoid(!isSnapshotMachine());
12455 AssertReturnVoid(aThat->isSnapshotMachine());
12456
12457 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12458
12459 mHWData.assignCopy(aThat->mHWData);
12460
12461 // create copies of all shared folders (mHWData after attaching a copy
12462 // contains just references to original objects)
12463 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12464 it != mHWData->mSharedFolders.end();
12465 ++it)
12466 {
12467 ComObjPtr<SharedFolder> folder;
12468 folder.createObject();
12469 HRESULT rc = folder->initCopy(getMachine(), *it);
12470 AssertComRC(rc);
12471 *it = folder;
12472 }
12473
12474 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12475 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12476 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12477 mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters);
12478 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12479
12480 /* create private copies of all controllers */
12481 mStorageControllers.backup();
12482 mStorageControllers->clear();
12483 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12484 it != aThat->mStorageControllers->end();
12485 ++it)
12486 {
12487 ComObjPtr<StorageController> ctrl;
12488 ctrl.createObject();
12489 ctrl->initCopy(this, *it);
12490 mStorageControllers->push_back(ctrl);
12491 }
12492
12493 /* create private copies of all USB controllers */
12494 mUSBControllers.backup();
12495 mUSBControllers->clear();
12496 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12497 it != aThat->mUSBControllers->end();
12498 ++it)
12499 {
12500 ComObjPtr<USBController> ctrl;
12501 ctrl.createObject();
12502 ctrl->initCopy(this, *it);
12503 mUSBControllers->push_back(ctrl);
12504 }
12505
12506 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12507 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12508 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12509 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12510 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12511 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12512 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12513}
12514
12515/**
12516 * Returns whether the given storage controller is hotplug capable.
12517 *
12518 * @returns true if the controller supports hotplugging
12519 * false otherwise.
12520 * @param enmCtrlType The controller type to check for.
12521 */
12522bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12523{
12524 switch (enmCtrlType)
12525 {
12526 case StorageControllerType_IntelAhci:
12527 case StorageControllerType_USB:
12528 return true;
12529 case StorageControllerType_LsiLogic:
12530 case StorageControllerType_LsiLogicSas:
12531 case StorageControllerType_BusLogic:
12532 case StorageControllerType_PIIX3:
12533 case StorageControllerType_PIIX4:
12534 case StorageControllerType_ICH6:
12535 case StorageControllerType_I82078:
12536 default:
12537 return false;
12538 }
12539}
12540
12541#ifdef VBOX_WITH_RESOURCE_USAGE_API
12542
12543void Machine::getDiskList(MediaList &list)
12544{
12545 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12546 it != mMediaData->mAttachments.end();
12547 ++it)
12548 {
12549 MediumAttachment* pAttach = *it;
12550 /* just in case */
12551 AssertStmt(pAttach, continue);
12552
12553 AutoCaller localAutoCallerA(pAttach);
12554 if (FAILED(localAutoCallerA.rc())) continue;
12555
12556 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12557
12558 if (pAttach->getType() == DeviceType_HardDisk)
12559 list.push_back(pAttach->getMedium());
12560 }
12561}
12562
12563void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12564{
12565 AssertReturnVoid(isWriteLockOnCurrentThread());
12566 AssertPtrReturnVoid(aCollector);
12567
12568 pm::CollectorHAL *hal = aCollector->getHAL();
12569 /* Create sub metrics */
12570 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12571 "Percentage of processor time spent in user mode by the VM process.");
12572 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12573 "Percentage of processor time spent in kernel mode by the VM process.");
12574 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12575 "Size of resident portion of VM process in memory.");
12576 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12577 "Actual size of all VM disks combined.");
12578 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12579 "Network receive rate.");
12580 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12581 "Network transmit rate.");
12582 /* Create and register base metrics */
12583 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12584 cpuLoadUser, cpuLoadKernel);
12585 aCollector->registerBaseMetric(cpuLoad);
12586 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12587 ramUsageUsed);
12588 aCollector->registerBaseMetric(ramUsage);
12589 MediaList disks;
12590 getDiskList(disks);
12591 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12592 diskUsageUsed);
12593 aCollector->registerBaseMetric(diskUsage);
12594
12595 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12596 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12597 new pm::AggregateAvg()));
12598 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12599 new pm::AggregateMin()));
12600 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12601 new pm::AggregateMax()));
12602 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12603 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12604 new pm::AggregateAvg()));
12605 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12606 new pm::AggregateMin()));
12607 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12608 new pm::AggregateMax()));
12609
12610 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12611 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12612 new pm::AggregateAvg()));
12613 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12614 new pm::AggregateMin()));
12615 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12616 new pm::AggregateMax()));
12617
12618 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12619 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12620 new pm::AggregateAvg()));
12621 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12622 new pm::AggregateMin()));
12623 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12624 new pm::AggregateMax()));
12625
12626
12627 /* Guest metrics collector */
12628 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12629 aCollector->registerGuest(mCollectorGuest);
12630 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12631 this, __PRETTY_FUNCTION__, mCollectorGuest));
12632
12633 /* Create sub metrics */
12634 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12635 "Percentage of processor time spent in user mode as seen by the guest.");
12636 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12637 "Percentage of processor time spent in kernel mode as seen by the guest.");
12638 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12639 "Percentage of processor time spent idling as seen by the guest.");
12640
12641 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12642 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12643 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12644 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12645 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12646 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12647
12648 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12649
12650 /* Create and register base metrics */
12651 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12652 machineNetRx, machineNetTx);
12653 aCollector->registerBaseMetric(machineNetRate);
12654
12655 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12656 guestLoadUser, guestLoadKernel, guestLoadIdle);
12657 aCollector->registerBaseMetric(guestCpuLoad);
12658
12659 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12660 guestMemTotal, guestMemFree,
12661 guestMemBalloon, guestMemShared,
12662 guestMemCache, guestPagedTotal);
12663 aCollector->registerBaseMetric(guestCpuMem);
12664
12665 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12666 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12667 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12668 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12669
12670 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12671 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12672 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12673 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12674
12675 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12676 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12677 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12678 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12679
12680 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12681 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12682 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12683 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12684
12685 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12686 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12687 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12688 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12689
12690 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12691 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12692 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12693 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12694
12695 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12696 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12697 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12698 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12699
12700 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12701 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12702 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12703 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12704
12705 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12706 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12707 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12708 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12709
12710 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12711 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12712 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12713 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12714
12715 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12716 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12717 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12718 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12719}
12720
12721void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12722{
12723 AssertReturnVoid(isWriteLockOnCurrentThread());
12724
12725 if (aCollector)
12726 {
12727 aCollector->unregisterMetricsFor(aMachine);
12728 aCollector->unregisterBaseMetricsFor(aMachine);
12729 }
12730}
12731
12732#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12733
12734
12735////////////////////////////////////////////////////////////////////////////////
12736
12737DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12738
12739HRESULT SessionMachine::FinalConstruct()
12740{
12741 LogFlowThisFunc(("\n"));
12742
12743 mClientToken = NULL;
12744
12745 return BaseFinalConstruct();
12746}
12747
12748void SessionMachine::FinalRelease()
12749{
12750 LogFlowThisFunc(("\n"));
12751
12752 Assert(!mClientToken);
12753 /* paranoia, should not hang around any more */
12754 if (mClientToken)
12755 {
12756 delete mClientToken;
12757 mClientToken = NULL;
12758 }
12759
12760 uninit(Uninit::Unexpected);
12761
12762 BaseFinalRelease();
12763}
12764
12765/**
12766 * @note Must be called only by Machine::LockMachine() from its own write lock.
12767 */
12768HRESULT SessionMachine::init(Machine *aMachine)
12769{
12770 LogFlowThisFuncEnter();
12771 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12772
12773 AssertReturn(aMachine, E_INVALIDARG);
12774
12775 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12776
12777 /* Enclose the state transition NotReady->InInit->Ready */
12778 AutoInitSpan autoInitSpan(this);
12779 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12780
12781 HRESULT rc = S_OK;
12782
12783 /* create the machine client token */
12784 try
12785 {
12786 mClientToken = new ClientToken(aMachine, this);
12787 if (!mClientToken->isReady())
12788 {
12789 delete mClientToken;
12790 mClientToken = NULL;
12791 rc = E_FAIL;
12792 }
12793 }
12794 catch (std::bad_alloc &)
12795 {
12796 rc = E_OUTOFMEMORY;
12797 }
12798 if (FAILED(rc))
12799 return rc;
12800
12801 /* memorize the peer Machine */
12802 unconst(mPeer) = aMachine;
12803 /* share the parent pointer */
12804 unconst(mParent) = aMachine->mParent;
12805
12806 /* take the pointers to data to share */
12807 mData.share(aMachine->mData);
12808 mSSData.share(aMachine->mSSData);
12809
12810 mUserData.share(aMachine->mUserData);
12811 mHWData.share(aMachine->mHWData);
12812 mMediaData.share(aMachine->mMediaData);
12813
12814 mStorageControllers.allocate();
12815 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12816 it != aMachine->mStorageControllers->end();
12817 ++it)
12818 {
12819 ComObjPtr<StorageController> ctl;
12820 ctl.createObject();
12821 ctl->init(this, *it);
12822 mStorageControllers->push_back(ctl);
12823 }
12824
12825 mUSBControllers.allocate();
12826 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12827 it != aMachine->mUSBControllers->end();
12828 ++it)
12829 {
12830 ComObjPtr<USBController> ctl;
12831 ctl.createObject();
12832 ctl->init(this, *it);
12833 mUSBControllers->push_back(ctl);
12834 }
12835
12836 unconst(mBIOSSettings).createObject();
12837 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12838 /* create another VRDEServer object that will be mutable */
12839 unconst(mVRDEServer).createObject();
12840 mVRDEServer->init(this, aMachine->mVRDEServer);
12841 /* create another audio adapter object that will be mutable */
12842 unconst(mAudioAdapter).createObject();
12843 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12844 /* create a list of serial ports that will be mutable */
12845 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12846 {
12847 unconst(mSerialPorts[slot]).createObject();
12848 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12849 }
12850 /* create a list of parallel ports that will be mutable */
12851 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12852 {
12853 unconst(mParallelPorts[slot]).createObject();
12854 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12855 }
12856
12857 /* create another USB device filters object that will be mutable */
12858 unconst(mUSBDeviceFilters).createObject();
12859 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12860
12861 /* create a list of network adapters that will be mutable */
12862 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12863 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12864 {
12865 unconst(mNetworkAdapters[slot]).createObject();
12866 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12867
12868 NetworkAttachmentType_T type;
12869 HRESULT hrc;
12870 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12871 if ( SUCCEEDED(hrc)
12872 && type == NetworkAttachmentType_NATNetwork)
12873 {
12874 Bstr name;
12875 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12876 if (SUCCEEDED(hrc))
12877 {
12878 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12879 mUserData->s.strName.c_str(), name.raw()));
12880 aMachine->lockHandle()->unlockWrite();
12881 mParent->natNetworkRefInc(name.raw());
12882#ifdef RT_LOCK_STRICT
12883 aMachine->lockHandle()->lockWrite(RT_SRC_POS);
12884#else
12885 aMachine->lockHandle()->lockWrite();
12886#endif
12887 }
12888 }
12889 }
12890
12891 /* create another bandwidth control object that will be mutable */
12892 unconst(mBandwidthControl).createObject();
12893 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12894
12895 /* default is to delete saved state on Saved -> PoweredOff transition */
12896 mRemoveSavedState = true;
12897
12898 /* Confirm a successful initialization when it's the case */
12899 autoInitSpan.setSucceeded();
12900
12901 LogFlowThisFuncLeave();
12902 return rc;
12903}
12904
12905/**
12906 * Uninitializes this session object. If the reason is other than
12907 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12908 * or the client watcher code.
12909 *
12910 * @param aReason uninitialization reason
12911 *
12912 * @note Locks mParent + this object for writing.
12913 */
12914void SessionMachine::uninit(Uninit::Reason aReason)
12915{
12916 LogFlowThisFuncEnter();
12917 LogFlowThisFunc(("reason=%d\n", aReason));
12918
12919 /*
12920 * Strongly reference ourselves to prevent this object deletion after
12921 * mData->mSession.mMachine.setNull() below (which can release the last
12922 * reference and call the destructor). Important: this must be done before
12923 * accessing any members (and before AutoUninitSpan that does it as well).
12924 * This self reference will be released as the very last step on return.
12925 */
12926 ComObjPtr<SessionMachine> selfRef = this;
12927
12928 /* Enclose the state transition Ready->InUninit->NotReady */
12929 AutoUninitSpan autoUninitSpan(this);
12930 if (autoUninitSpan.uninitDone())
12931 {
12932 LogFlowThisFunc(("Already uninitialized\n"));
12933 LogFlowThisFuncLeave();
12934 return;
12935 }
12936
12937 if (autoUninitSpan.initFailed())
12938 {
12939 /* We've been called by init() because it's failed. It's not really
12940 * necessary (nor it's safe) to perform the regular uninit sequence
12941 * below, the following is enough.
12942 */
12943 LogFlowThisFunc(("Initialization failed.\n"));
12944 /* destroy the machine client token */
12945 if (mClientToken)
12946 {
12947 delete mClientToken;
12948 mClientToken = NULL;
12949 }
12950 uninitDataAndChildObjects();
12951 mData.free();
12952 unconst(mParent) = NULL;
12953 unconst(mPeer) = NULL;
12954 LogFlowThisFuncLeave();
12955 return;
12956 }
12957
12958 MachineState_T lastState;
12959 {
12960 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12961 lastState = mData->mMachineState;
12962 }
12963 NOREF(lastState);
12964
12965#ifdef VBOX_WITH_USB
12966 // release all captured USB devices, but do this before requesting the locks below
12967 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12968 {
12969 /* Console::captureUSBDevices() is called in the VM process only after
12970 * setting the machine state to Starting or Restoring.
12971 * Console::detachAllUSBDevices() will be called upon successful
12972 * termination. So, we need to release USB devices only if there was
12973 * an abnormal termination of a running VM.
12974 *
12975 * This is identical to SessionMachine::DetachAllUSBDevices except
12976 * for the aAbnormal argument. */
12977 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
12978 AssertComRC(rc);
12979 NOREF(rc);
12980
12981 USBProxyService *service = mParent->host()->i_usbProxyService();
12982 if (service)
12983 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12984 }
12985#endif /* VBOX_WITH_USB */
12986
12987 // we need to lock this object in uninit() because the lock is shared
12988 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12989 // and others need mParent lock, and USB needs host lock.
12990 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12991
12992#ifdef VBOX_WITH_RESOURCE_USAGE_API
12993 /*
12994 * It is safe to call Machine::unregisterMetrics() here because
12995 * PerformanceCollector::samplerCallback no longer accesses guest methods
12996 * holding the lock.
12997 */
12998 unregisterMetrics(mParent->performanceCollector(), mPeer);
12999 /* The guest must be unregistered after its metrics (@bugref{5949}). */
13000 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
13001 this, __PRETTY_FUNCTION__, mCollectorGuest));
13002 if (mCollectorGuest)
13003 {
13004 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
13005 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
13006 mCollectorGuest = NULL;
13007 }
13008#endif
13009
13010 if (aReason == Uninit::Abnormal)
13011 {
13012 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
13013 Global::IsOnlineOrTransient(lastState)));
13014
13015 /* reset the state to Aborted */
13016 if (mData->mMachineState != MachineState_Aborted)
13017 setMachineState(MachineState_Aborted);
13018 }
13019
13020 // any machine settings modified?
13021 if (mData->flModifications)
13022 {
13023 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
13024 rollback(false /* aNotify */);
13025 }
13026
13027 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
13028 || !mConsoleTaskData.mSnapshot);
13029 if (!mConsoleTaskData.strStateFilePath.isEmpty())
13030 {
13031 LogWarningThisFunc(("canceling failed save state request!\n"));
13032 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
13033 }
13034 else if (!mConsoleTaskData.mSnapshot.isNull())
13035 {
13036 LogWarningThisFunc(("canceling untaken snapshot!\n"));
13037
13038 /* delete all differencing hard disks created (this will also attach
13039 * their parents back by rolling back mMediaData) */
13040 rollbackMedia();
13041
13042 // delete the saved state file (it might have been already created)
13043 // AFTER killing the snapshot so that releaseSavedStateFile() won't
13044 // think it's still in use
13045 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
13046 mConsoleTaskData.mSnapshot->uninit();
13047 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
13048 }
13049
13050 if (!mData->mSession.mType.isEmpty())
13051 {
13052 /* mType is not null when this machine's process has been started by
13053 * Machine::LaunchVMProcess(), therefore it is our child. We
13054 * need to queue the PID to reap the process (and avoid zombies on
13055 * Linux). */
13056 Assert(mData->mSession.mPID != NIL_RTPROCESS);
13057 mParent->addProcessToReap(mData->mSession.mPID);
13058 }
13059
13060 mData->mSession.mPID = NIL_RTPROCESS;
13061
13062 if (aReason == Uninit::Unexpected)
13063 {
13064 /* Uninitialization didn't come from #checkForDeath(), so tell the
13065 * client watcher thread to update the set of machines that have open
13066 * sessions. */
13067 mParent->updateClientWatcher();
13068 }
13069
13070 /* uninitialize all remote controls */
13071 if (mData->mSession.mRemoteControls.size())
13072 {
13073 LogFlowThisFunc(("Closing remote sessions (%d):\n",
13074 mData->mSession.mRemoteControls.size()));
13075
13076 Data::Session::RemoteControlList::iterator it =
13077 mData->mSession.mRemoteControls.begin();
13078 while (it != mData->mSession.mRemoteControls.end())
13079 {
13080 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
13081 HRESULT rc = (*it)->Uninitialize();
13082 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
13083 if (FAILED(rc))
13084 LogWarningThisFunc(("Forgot to close the remote session?\n"));
13085 ++it;
13086 }
13087 mData->mSession.mRemoteControls.clear();
13088 }
13089
13090 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
13091 {
13092 NetworkAttachmentType_T type;
13093 HRESULT hrc;
13094
13095 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13096 if ( SUCCEEDED(hrc)
13097 && type == NetworkAttachmentType_NATNetwork)
13098 {
13099 Bstr name;
13100 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13101 if (SUCCEEDED(hrc))
13102 {
13103 multilock.release();
13104 LogRel(("VM '%s' stops using NAT network '%ls'\n",
13105 mUserData->s.strName.c_str(), name.raw()));
13106 mParent->natNetworkRefDec(name.raw());
13107 multilock.acquire();
13108 }
13109 }
13110 }
13111
13112 /*
13113 * An expected uninitialization can come only from #checkForDeath().
13114 * Otherwise it means that something's gone really wrong (for example,
13115 * the Session implementation has released the VirtualBox reference
13116 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
13117 * etc). However, it's also possible, that the client releases the IPC
13118 * semaphore correctly (i.e. before it releases the VirtualBox reference),
13119 * but the VirtualBox release event comes first to the server process.
13120 * This case is practically possible, so we should not assert on an
13121 * unexpected uninit, just log a warning.
13122 */
13123
13124 if ((aReason == Uninit::Unexpected))
13125 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
13126
13127 if (aReason != Uninit::Normal)
13128 {
13129 mData->mSession.mDirectControl.setNull();
13130 }
13131 else
13132 {
13133 /* this must be null here (see #OnSessionEnd()) */
13134 Assert(mData->mSession.mDirectControl.isNull());
13135 Assert(mData->mSession.mState == SessionState_Unlocking);
13136 Assert(!mData->mSession.mProgress.isNull());
13137 }
13138 if (mData->mSession.mProgress)
13139 {
13140 if (aReason == Uninit::Normal)
13141 mData->mSession.mProgress->notifyComplete(S_OK);
13142 else
13143 mData->mSession.mProgress->notifyComplete(E_FAIL,
13144 COM_IIDOF(ISession),
13145 getComponentName(),
13146 tr("The VM session was aborted"));
13147 mData->mSession.mProgress.setNull();
13148 }
13149
13150 /* remove the association between the peer machine and this session machine */
13151 Assert( (SessionMachine*)mData->mSession.mMachine == this
13152 || aReason == Uninit::Unexpected);
13153
13154 /* reset the rest of session data */
13155 mData->mSession.mMachine.setNull();
13156 mData->mSession.mState = SessionState_Unlocked;
13157 mData->mSession.mType.setNull();
13158
13159 /* destroy the machine client token before leaving the exclusive lock */
13160 if (mClientToken)
13161 {
13162 delete mClientToken;
13163 mClientToken = NULL;
13164 }
13165
13166 /* fire an event */
13167 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13168
13169 uninitDataAndChildObjects();
13170
13171 /* free the essential data structure last */
13172 mData.free();
13173
13174 /* release the exclusive lock before setting the below two to NULL */
13175 multilock.release();
13176
13177 unconst(mParent) = NULL;
13178 unconst(mPeer) = NULL;
13179
13180 LogFlowThisFuncLeave();
13181}
13182
13183// util::Lockable interface
13184////////////////////////////////////////////////////////////////////////////////
13185
13186/**
13187 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13188 * with the primary Machine instance (mPeer).
13189 */
13190RWLockHandle *SessionMachine::lockHandle() const
13191{
13192 AssertReturn(mPeer != NULL, NULL);
13193 return mPeer->lockHandle();
13194}
13195
13196// IInternalMachineControl methods
13197////////////////////////////////////////////////////////////////////////////////
13198
13199/**
13200 * Passes collected guest statistics to performance collector object
13201 */
13202STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13203 ULONG aCpuKernel, ULONG aCpuIdle,
13204 ULONG aMemTotal, ULONG aMemFree,
13205 ULONG aMemBalloon, ULONG aMemShared,
13206 ULONG aMemCache, ULONG aPageTotal,
13207 ULONG aAllocVMM, ULONG aFreeVMM,
13208 ULONG aBalloonedVMM, ULONG aSharedVMM,
13209 ULONG aVmNetRx, ULONG aVmNetTx)
13210{
13211#ifdef VBOX_WITH_RESOURCE_USAGE_API
13212 if (mCollectorGuest)
13213 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13214 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13215 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13216 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13217
13218 return S_OK;
13219#else
13220 NOREF(aValidStats);
13221 NOREF(aCpuUser);
13222 NOREF(aCpuKernel);
13223 NOREF(aCpuIdle);
13224 NOREF(aMemTotal);
13225 NOREF(aMemFree);
13226 NOREF(aMemBalloon);
13227 NOREF(aMemShared);
13228 NOREF(aMemCache);
13229 NOREF(aPageTotal);
13230 NOREF(aAllocVMM);
13231 NOREF(aFreeVMM);
13232 NOREF(aBalloonedVMM);
13233 NOREF(aSharedVMM);
13234 NOREF(aVmNetRx);
13235 NOREF(aVmNetTx);
13236 return E_NOTIMPL;
13237#endif
13238}
13239
13240/**
13241 * @note Locks this object for writing.
13242 */
13243STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13244{
13245 AutoCaller autoCaller(this);
13246 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13247
13248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13249
13250 mRemoveSavedState = aRemove;
13251
13252 return S_OK;
13253}
13254
13255/**
13256 * @note Locks the same as #setMachineState() does.
13257 */
13258STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13259{
13260 return setMachineState(aMachineState);
13261}
13262
13263/**
13264 * @note Locks this object for writing.
13265 */
13266STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13267{
13268 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13269 AutoCaller autoCaller(this);
13270 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13271
13272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13273
13274 if (mData->mSession.mState != SessionState_Locked)
13275 return VBOX_E_INVALID_OBJECT_STATE;
13276
13277 if (!mData->mSession.mProgress.isNull())
13278 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13279
13280 LogFlowThisFunc(("returns S_OK.\n"));
13281 return S_OK;
13282}
13283
13284/**
13285 * @note Locks this object for writing.
13286 */
13287STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13288{
13289 AutoCaller autoCaller(this);
13290 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13291
13292 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13293
13294 if (mData->mSession.mState != SessionState_Locked)
13295 return VBOX_E_INVALID_OBJECT_STATE;
13296
13297 /* Finalize the LaunchVMProcess progress object. */
13298 if (mData->mSession.mProgress)
13299 {
13300 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13301 mData->mSession.mProgress.setNull();
13302 }
13303
13304 if (SUCCEEDED((HRESULT)iResult))
13305 {
13306#ifdef VBOX_WITH_RESOURCE_USAGE_API
13307 /* The VM has been powered up successfully, so it makes sense
13308 * now to offer the performance metrics for a running machine
13309 * object. Doing it earlier wouldn't be safe. */
13310 registerMetrics(mParent->performanceCollector(), mPeer,
13311 mData->mSession.mPID);
13312#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13313 }
13314
13315 return S_OK;
13316}
13317
13318/**
13319 * @note Locks this object for writing.
13320 */
13321STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13322{
13323 LogFlowThisFuncEnter();
13324
13325 CheckComArgOutPointerValid(aProgress);
13326
13327 AutoCaller autoCaller(this);
13328 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13329
13330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13331
13332 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13333 E_FAIL);
13334
13335 /* create a progress object to track operation completion */
13336 ComObjPtr<Progress> pProgress;
13337 pProgress.createObject();
13338 pProgress->init(getVirtualBox(),
13339 static_cast<IMachine *>(this) /* aInitiator */,
13340 Bstr(tr("Stopping the virtual machine")).raw(),
13341 FALSE /* aCancelable */);
13342
13343 /* fill in the console task data */
13344 mConsoleTaskData.mLastState = mData->mMachineState;
13345 mConsoleTaskData.mProgress = pProgress;
13346
13347 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13348 setMachineState(MachineState_Stopping);
13349
13350 pProgress.queryInterfaceTo(aProgress);
13351
13352 return S_OK;
13353}
13354
13355/**
13356 * @note Locks this object for writing.
13357 */
13358STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13359{
13360 LogFlowThisFuncEnter();
13361
13362 AutoCaller autoCaller(this);
13363 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13364
13365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13366
13367 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13368 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13369 && mConsoleTaskData.mLastState != MachineState_Null,
13370 E_FAIL);
13371
13372 /*
13373 * On failure, set the state to the state we had when BeginPoweringDown()
13374 * was called (this is expected by Console::PowerDown() and the associated
13375 * task). On success the VM process already changed the state to
13376 * MachineState_PoweredOff, so no need to do anything.
13377 */
13378 if (FAILED(iResult))
13379 setMachineState(mConsoleTaskData.mLastState);
13380
13381 /* notify the progress object about operation completion */
13382 Assert(mConsoleTaskData.mProgress);
13383 if (SUCCEEDED(iResult))
13384 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13385 else
13386 {
13387 Utf8Str strErrMsg(aErrMsg);
13388 if (strErrMsg.length())
13389 mConsoleTaskData.mProgress->notifyComplete(iResult,
13390 COM_IIDOF(ISession),
13391 getComponentName(),
13392 strErrMsg.c_str());
13393 else
13394 mConsoleTaskData.mProgress->notifyComplete(iResult);
13395 }
13396
13397 /* clear out the temporary saved state data */
13398 mConsoleTaskData.mLastState = MachineState_Null;
13399 mConsoleTaskData.mProgress.setNull();
13400
13401 LogFlowThisFuncLeave();
13402 return S_OK;
13403}
13404
13405
13406/**
13407 * Goes through the USB filters of the given machine to see if the given
13408 * device matches any filter or not.
13409 *
13410 * @note Locks the same as USBController::hasMatchingFilter() does.
13411 */
13412STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13413 BOOL *aMatched,
13414 ULONG *aMaskedIfs)
13415{
13416 LogFlowThisFunc(("\n"));
13417
13418 CheckComArgNotNull(aUSBDevice);
13419 CheckComArgOutPointerValid(aMatched);
13420
13421 AutoCaller autoCaller(this);
13422 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13423
13424#ifdef VBOX_WITH_USB
13425 *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13426#else
13427 NOREF(aUSBDevice);
13428 NOREF(aMaskedIfs);
13429 *aMatched = FALSE;
13430#endif
13431
13432 return S_OK;
13433}
13434
13435/**
13436 * @note Locks the same as Host::captureUSBDevice() does.
13437 */
13438STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13439{
13440 LogFlowThisFunc(("\n"));
13441
13442 AutoCaller autoCaller(this);
13443 AssertComRCReturnRC(autoCaller.rc());
13444
13445#ifdef VBOX_WITH_USB
13446 /* if captureDeviceForVM() fails, it must have set extended error info */
13447 clearError();
13448 MultiResult rc = mParent->host()->i_checkUSBProxyService();
13449 if (FAILED(rc)) return rc;
13450
13451 USBProxyService *service = mParent->host()->i_usbProxyService();
13452 AssertReturn(service, E_FAIL);
13453 return service->captureDeviceForVM(this, Guid(aId).ref());
13454#else
13455 NOREF(aId);
13456 return E_NOTIMPL;
13457#endif
13458}
13459
13460/**
13461 * @note Locks the same as Host::detachUSBDevice() does.
13462 */
13463STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13464{
13465 LogFlowThisFunc(("\n"));
13466
13467 AutoCaller autoCaller(this);
13468 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13469
13470#ifdef VBOX_WITH_USB
13471 USBProxyService *service = mParent->host()->i_usbProxyService();
13472 AssertReturn(service, E_FAIL);
13473 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13474#else
13475 NOREF(aId);
13476 NOREF(aDone);
13477 return E_NOTIMPL;
13478#endif
13479}
13480
13481/**
13482 * Inserts all machine filters to the USB proxy service and then calls
13483 * Host::autoCaptureUSBDevices().
13484 *
13485 * Called by Console from the VM process upon VM startup.
13486 *
13487 * @note Locks what called methods lock.
13488 */
13489STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13490{
13491 LogFlowThisFunc(("\n"));
13492
13493 AutoCaller autoCaller(this);
13494 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13495
13496#ifdef VBOX_WITH_USB
13497 HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);
13498 AssertComRC(rc);
13499 NOREF(rc);
13500
13501 USBProxyService *service = mParent->host()->i_usbProxyService();
13502 AssertReturn(service, E_FAIL);
13503 return service->autoCaptureDevicesForVM(this);
13504#else
13505 return S_OK;
13506#endif
13507}
13508
13509/**
13510 * Removes all machine filters from the USB proxy service and then calls
13511 * Host::detachAllUSBDevices().
13512 *
13513 * Called by Console from the VM process upon normal VM termination or by
13514 * SessionMachine::uninit() upon abnormal VM termination (from under the
13515 * Machine/SessionMachine lock).
13516 *
13517 * @note Locks what called methods lock.
13518 */
13519STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13520{
13521 LogFlowThisFunc(("\n"));
13522
13523 AutoCaller autoCaller(this);
13524 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13525
13526#ifdef VBOX_WITH_USB
13527 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
13528 AssertComRC(rc);
13529 NOREF(rc);
13530
13531 USBProxyService *service = mParent->host()->i_usbProxyService();
13532 AssertReturn(service, E_FAIL);
13533 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13534#else
13535 NOREF(aDone);
13536 return S_OK;
13537#endif
13538}
13539
13540/**
13541 * @note Locks this object for writing.
13542 */
13543STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13544 IProgress **aProgress)
13545{
13546 LogFlowThisFuncEnter();
13547
13548 AssertReturn(aSession, E_INVALIDARG);
13549 AssertReturn(aProgress, E_INVALIDARG);
13550
13551 AutoCaller autoCaller(this);
13552
13553 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13554 /*
13555 * We don't assert below because it might happen that a non-direct session
13556 * informs us it is closed right after we've been uninitialized -- it's ok.
13557 */
13558 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13559
13560 /* get IInternalSessionControl interface */
13561 ComPtr<IInternalSessionControl> control(aSession);
13562
13563 ComAssertRet(!control.isNull(), E_INVALIDARG);
13564
13565 /* Creating a Progress object requires the VirtualBox lock, and
13566 * thus locking it here is required by the lock order rules. */
13567 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13568
13569 if (control == mData->mSession.mDirectControl)
13570 {
13571 ComAssertRet(aProgress, E_POINTER);
13572
13573 /* The direct session is being normally closed by the client process
13574 * ----------------------------------------------------------------- */
13575
13576 /* go to the closing state (essential for all open*Session() calls and
13577 * for #checkForDeath()) */
13578 Assert(mData->mSession.mState == SessionState_Locked);
13579 mData->mSession.mState = SessionState_Unlocking;
13580
13581 /* set direct control to NULL to release the remote instance */
13582 mData->mSession.mDirectControl.setNull();
13583 LogFlowThisFunc(("Direct control is set to NULL\n"));
13584
13585 if (mData->mSession.mProgress)
13586 {
13587 /* finalize the progress, someone might wait if a frontend
13588 * closes the session before powering on the VM. */
13589 mData->mSession.mProgress->notifyComplete(E_FAIL,
13590 COM_IIDOF(ISession),
13591 getComponentName(),
13592 tr("The VM session was closed before any attempt to power it on"));
13593 mData->mSession.mProgress.setNull();
13594 }
13595
13596 /* Create the progress object the client will use to wait until
13597 * #checkForDeath() is called to uninitialize this session object after
13598 * it releases the IPC semaphore.
13599 * Note! Because we're "reusing" mProgress here, this must be a proxy
13600 * object just like for LaunchVMProcess. */
13601 Assert(mData->mSession.mProgress.isNull());
13602 ComObjPtr<ProgressProxy> progress;
13603 progress.createObject();
13604 ComPtr<IUnknown> pPeer(mPeer);
13605 progress->init(mParent, pPeer,
13606 Bstr(tr("Closing session")).raw(),
13607 FALSE /* aCancelable */);
13608 progress.queryInterfaceTo(aProgress);
13609 mData->mSession.mProgress = progress;
13610 }
13611 else
13612 {
13613 /* the remote session is being normally closed */
13614 Data::Session::RemoteControlList::iterator it =
13615 mData->mSession.mRemoteControls.begin();
13616 while (it != mData->mSession.mRemoteControls.end())
13617 {
13618 if (control == *it)
13619 break;
13620 ++it;
13621 }
13622 BOOL found = it != mData->mSession.mRemoteControls.end();
13623 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13624 E_INVALIDARG);
13625 // This MUST be erase(it), not remove(*it) as the latter triggers a
13626 // very nasty use after free due to the place where the value "lives".
13627 mData->mSession.mRemoteControls.erase(it);
13628 }
13629
13630 /* signal the client watcher thread, because the client is going away */
13631 mParent->updateClientWatcher();
13632
13633 LogFlowThisFuncLeave();
13634 return S_OK;
13635}
13636
13637/**
13638 * @note Locks this object for writing.
13639 */
13640STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13641{
13642 LogFlowThisFuncEnter();
13643
13644 CheckComArgOutPointerValid(aProgress);
13645 CheckComArgOutPointerValid(aStateFilePath);
13646
13647 AutoCaller autoCaller(this);
13648 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13649
13650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13651
13652 AssertReturn( mData->mMachineState == MachineState_Paused
13653 && mConsoleTaskData.mLastState == MachineState_Null
13654 && mConsoleTaskData.strStateFilePath.isEmpty(),
13655 E_FAIL);
13656
13657 /* create a progress object to track operation completion */
13658 ComObjPtr<Progress> pProgress;
13659 pProgress.createObject();
13660 pProgress->init(getVirtualBox(),
13661 static_cast<IMachine *>(this) /* aInitiator */,
13662 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13663 FALSE /* aCancelable */);
13664
13665 Utf8Str strStateFilePath;
13666 /* stateFilePath is null when the machine is not running */
13667 if (mData->mMachineState == MachineState_Paused)
13668 composeSavedStateFilename(strStateFilePath);
13669
13670 /* fill in the console task data */
13671 mConsoleTaskData.mLastState = mData->mMachineState;
13672 mConsoleTaskData.strStateFilePath = strStateFilePath;
13673 mConsoleTaskData.mProgress = pProgress;
13674
13675 /* set the state to Saving (this is expected by Console::SaveState()) */
13676 setMachineState(MachineState_Saving);
13677
13678 strStateFilePath.cloneTo(aStateFilePath);
13679 pProgress.queryInterfaceTo(aProgress);
13680
13681 return S_OK;
13682}
13683
13684/**
13685 * @note Locks mParent + this object for writing.
13686 */
13687STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13688{
13689 LogFlowThisFunc(("\n"));
13690
13691 AutoCaller autoCaller(this);
13692 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13693
13694 /* endSavingState() need mParent lock */
13695 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13696
13697 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13698 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13699 && mConsoleTaskData.mLastState != MachineState_Null
13700 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13701 E_FAIL);
13702
13703 /*
13704 * On failure, set the state to the state we had when BeginSavingState()
13705 * was called (this is expected by Console::SaveState() and the associated
13706 * task). On success the VM process already changed the state to
13707 * MachineState_Saved, so no need to do anything.
13708 */
13709 if (FAILED(iResult))
13710 setMachineState(mConsoleTaskData.mLastState);
13711
13712 return endSavingState(iResult, aErrMsg);
13713}
13714
13715/**
13716 * @note Locks this object for writing.
13717 */
13718STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13719{
13720 LogFlowThisFunc(("\n"));
13721
13722 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13723
13724 AutoCaller autoCaller(this);
13725 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13726
13727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13728
13729 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13730 || mData->mMachineState == MachineState_Teleported
13731 || mData->mMachineState == MachineState_Aborted
13732 , E_FAIL); /** @todo setError. */
13733
13734 Utf8Str stateFilePathFull = aSavedStateFile;
13735 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13736 if (RT_FAILURE(vrc))
13737 return setError(VBOX_E_FILE_ERROR,
13738 tr("Invalid saved state file path '%ls' (%Rrc)"),
13739 aSavedStateFile,
13740 vrc);
13741
13742 mSSData->strStateFilePath = stateFilePathFull;
13743
13744 /* The below setMachineState() will detect the state transition and will
13745 * update the settings file */
13746
13747 return setMachineState(MachineState_Saved);
13748}
13749
13750STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13751 ComSafeArrayOut(BSTR, aValues),
13752 ComSafeArrayOut(LONG64, aTimestamps),
13753 ComSafeArrayOut(BSTR, aFlags))
13754{
13755 LogFlowThisFunc(("\n"));
13756
13757#ifdef VBOX_WITH_GUEST_PROPS
13758 using namespace guestProp;
13759
13760 AutoCaller autoCaller(this);
13761 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13762
13763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13764
13765 CheckComArgOutSafeArrayPointerValid(aNames);
13766 CheckComArgOutSafeArrayPointerValid(aValues);
13767 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13768 CheckComArgOutSafeArrayPointerValid(aFlags);
13769
13770 size_t cEntries = mHWData->mGuestProperties.size();
13771 com::SafeArray<BSTR> names(cEntries);
13772 com::SafeArray<BSTR> values(cEntries);
13773 com::SafeArray<LONG64> timestamps(cEntries);
13774 com::SafeArray<BSTR> flags(cEntries);
13775 unsigned i = 0;
13776 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13777 it != mHWData->mGuestProperties.end();
13778 ++it)
13779 {
13780 char szFlags[MAX_FLAGS_LEN + 1];
13781 it->first.cloneTo(&names[i]);
13782 it->second.strValue.cloneTo(&values[i]);
13783 timestamps[i] = it->second.mTimestamp;
13784 /* If it is NULL, keep it NULL. */
13785 if (it->second.mFlags)
13786 {
13787 writeFlags(it->second.mFlags, szFlags);
13788 Bstr(szFlags).cloneTo(&flags[i]);
13789 }
13790 else
13791 flags[i] = NULL;
13792 ++i;
13793 }
13794 names.detachTo(ComSafeArrayOutArg(aNames));
13795 values.detachTo(ComSafeArrayOutArg(aValues));
13796 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13797 flags.detachTo(ComSafeArrayOutArg(aFlags));
13798 return S_OK;
13799#else
13800 ReturnComNotImplemented();
13801#endif
13802}
13803
13804STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13805 IN_BSTR aValue,
13806 LONG64 aTimestamp,
13807 IN_BSTR aFlags)
13808{
13809 LogFlowThisFunc(("\n"));
13810
13811#ifdef VBOX_WITH_GUEST_PROPS
13812 using namespace guestProp;
13813
13814 CheckComArgStrNotEmptyOrNull(aName);
13815 CheckComArgNotNull(aValue);
13816 CheckComArgNotNull(aFlags);
13817
13818 try
13819 {
13820 /*
13821 * Convert input up front.
13822 */
13823 Utf8Str utf8Name(aName);
13824 uint32_t fFlags = NILFLAG;
13825 if (aFlags)
13826 {
13827 Utf8Str utf8Flags(aFlags);
13828 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13829 AssertRCReturn(vrc, E_INVALIDARG);
13830 }
13831
13832 /*
13833 * Now grab the object lock, validate the state and do the update.
13834 */
13835 AutoCaller autoCaller(this);
13836 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13837
13838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13839
13840 switch (mData->mMachineState)
13841 {
13842 case MachineState_Paused:
13843 case MachineState_Running:
13844 case MachineState_Teleporting:
13845 case MachineState_TeleportingPausedVM:
13846 case MachineState_LiveSnapshotting:
13847 case MachineState_DeletingSnapshotOnline:
13848 case MachineState_DeletingSnapshotPaused:
13849 case MachineState_Saving:
13850 case MachineState_Stopping:
13851 break;
13852
13853 default:
13854 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13855 VBOX_E_INVALID_VM_STATE);
13856 }
13857
13858 setModified(IsModified_MachineData);
13859 mHWData.backup();
13860
13861 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13862 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13863 if (it != mHWData->mGuestProperties.end())
13864 {
13865 if (!fDelete)
13866 {
13867 it->second.strValue = aValue;
13868 it->second.mTimestamp = aTimestamp;
13869 it->second.mFlags = fFlags;
13870 }
13871 else
13872 mHWData->mGuestProperties.erase(it);
13873
13874 mData->mGuestPropertiesModified = TRUE;
13875 }
13876 else if (!fDelete)
13877 {
13878 HWData::GuestProperty prop;
13879 prop.strValue = aValue;
13880 prop.mTimestamp = aTimestamp;
13881 prop.mFlags = fFlags;
13882
13883 mHWData->mGuestProperties[utf8Name] = prop;
13884 mData->mGuestPropertiesModified = TRUE;
13885 }
13886
13887 /*
13888 * Send a callback notification if appropriate
13889 */
13890 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13891 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13892 RTSTR_MAX,
13893 utf8Name.c_str(),
13894 RTSTR_MAX, NULL)
13895 )
13896 {
13897 alock.release();
13898
13899 mParent->onGuestPropertyChange(mData->mUuid,
13900 aName,
13901 aValue,
13902 aFlags);
13903 }
13904 }
13905 catch (...)
13906 {
13907 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13908 }
13909 return S_OK;
13910#else
13911 ReturnComNotImplemented();
13912#endif
13913}
13914
13915STDMETHODIMP SessionMachine::LockMedia()
13916{
13917 AutoCaller autoCaller(this);
13918 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13919
13920 AutoMultiWriteLock2 alock(this->lockHandle(),
13921 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13922
13923 AssertReturn( mData->mMachineState == MachineState_Starting
13924 || mData->mMachineState == MachineState_Restoring
13925 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13926
13927 clearError();
13928 alock.release();
13929 return lockMedia();
13930}
13931
13932STDMETHODIMP SessionMachine::UnlockMedia()
13933{
13934 unlockMedia();
13935 return S_OK;
13936}
13937
13938STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13939 IMediumAttachment **aNewAttachment)
13940{
13941 CheckComArgNotNull(aAttachment);
13942 CheckComArgOutPointerValid(aNewAttachment);
13943
13944 AutoCaller autoCaller(this);
13945 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13946
13947 // request the host lock first, since might be calling Host methods for getting host drives;
13948 // next, protect the media tree all the while we're in here, as well as our member variables
13949 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13950 this->lockHandle(),
13951 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13952
13953 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13954
13955 Bstr ctrlName;
13956 LONG lPort;
13957 LONG lDevice;
13958 bool fTempEject;
13959 {
13960 AutoCaller autoAttachCaller(this);
13961 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13962
13963 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13964
13965 /* Need to query the details first, as the IMediumAttachment reference
13966 * might be to the original settings, which we are going to change. */
13967 ctrlName = pAttach->getControllerName();
13968 lPort = pAttach->getPort();
13969 lDevice = pAttach->getDevice();
13970 fTempEject = pAttach->getTempEject();
13971 }
13972
13973 if (!fTempEject)
13974 {
13975 /* Remember previously mounted medium. The medium before taking the
13976 * backup is not necessarily the same thing. */
13977 ComObjPtr<Medium> oldmedium;
13978 oldmedium = pAttach->getMedium();
13979
13980 setModified(IsModified_Storage);
13981 mMediaData.backup();
13982
13983 // The backup operation makes the pAttach reference point to the
13984 // old settings. Re-get the correct reference.
13985 pAttach = findAttachment(mMediaData->mAttachments,
13986 ctrlName.raw(),
13987 lPort,
13988 lDevice);
13989
13990 {
13991 AutoCaller autoAttachCaller(this);
13992 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13993
13994 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13995 if (!oldmedium.isNull())
13996 oldmedium->removeBackReference(mData->mUuid);
13997
13998 pAttach->updateMedium(NULL);
13999 pAttach->updateEjected();
14000 }
14001
14002 setModified(IsModified_Storage);
14003 }
14004 else
14005 {
14006 {
14007 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
14008 pAttach->updateEjected();
14009 }
14010 }
14011
14012 pAttach.queryInterfaceTo(aNewAttachment);
14013
14014 return S_OK;
14015}
14016
14017// public methods only for internal purposes
14018/////////////////////////////////////////////////////////////////////////////
14019
14020#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14021/**
14022 * Called from the client watcher thread to check for expected or unexpected
14023 * death of the client process that has a direct session to this machine.
14024 *
14025 * On Win32 and on OS/2, this method is called only when we've got the
14026 * mutex (i.e. the client has either died or terminated normally) so it always
14027 * returns @c true (the client is terminated, the session machine is
14028 * uninitialized).
14029 *
14030 * On other platforms, the method returns @c true if the client process has
14031 * terminated normally or abnormally and the session machine was uninitialized,
14032 * and @c false if the client process is still alive.
14033 *
14034 * @note Locks this object for writing.
14035 */
14036bool SessionMachine::checkForDeath()
14037{
14038 Uninit::Reason reason;
14039 bool terminated = false;
14040
14041 /* Enclose autoCaller with a block because calling uninit() from under it
14042 * will deadlock. */
14043 {
14044 AutoCaller autoCaller(this);
14045 if (!autoCaller.isOk())
14046 {
14047 /* return true if not ready, to cause the client watcher to exclude
14048 * the corresponding session from watching */
14049 LogFlowThisFunc(("Already uninitialized!\n"));
14050 return true;
14051 }
14052
14053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14054
14055 /* Determine the reason of death: if the session state is Closing here,
14056 * everything is fine. Otherwise it means that the client did not call
14057 * OnSessionEnd() before it released the IPC semaphore. This may happen
14058 * either because the client process has abnormally terminated, or
14059 * because it simply forgot to call ISession::Close() before exiting. We
14060 * threat the latter also as an abnormal termination (see
14061 * Session::uninit() for details). */
14062 reason = mData->mSession.mState == SessionState_Unlocking ?
14063 Uninit::Normal :
14064 Uninit::Abnormal;
14065
14066 if (mClientToken)
14067 terminated = mClientToken->release();
14068 } /* AutoCaller block */
14069
14070 if (terminated)
14071 uninit(reason);
14072
14073 return terminated;
14074}
14075
14076void SessionMachine::getTokenId(Utf8Str &strTokenId)
14077{
14078 LogFlowThisFunc(("\n"));
14079
14080 strTokenId.setNull();
14081
14082 AutoCaller autoCaller(this);
14083 AssertComRCReturnVoid(autoCaller.rc());
14084
14085 Assert(mClientToken);
14086 if (mClientToken)
14087 mClientToken->getId(strTokenId);
14088}
14089#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14090IToken *SessionMachine::getToken()
14091{
14092 LogFlowThisFunc(("\n"));
14093
14094 AutoCaller autoCaller(this);
14095 AssertComRCReturn(autoCaller.rc(), NULL);
14096
14097 Assert(mClientToken);
14098 if (mClientToken)
14099 return mClientToken->getToken();
14100 else
14101 return NULL;
14102}
14103#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14104
14105Machine::ClientToken *SessionMachine::getClientToken()
14106{
14107 LogFlowThisFunc(("\n"));
14108
14109 AutoCaller autoCaller(this);
14110 AssertComRCReturn(autoCaller.rc(), NULL);
14111
14112 return mClientToken;
14113}
14114
14115
14116/**
14117 * @note Locks this object for reading.
14118 */
14119HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14120{
14121 LogFlowThisFunc(("\n"));
14122
14123 AutoCaller autoCaller(this);
14124 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14125
14126 ComPtr<IInternalSessionControl> directControl;
14127 {
14128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14129 directControl = mData->mSession.mDirectControl;
14130 }
14131
14132 /* ignore notifications sent after #OnSessionEnd() is called */
14133 if (!directControl)
14134 return S_OK;
14135
14136 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14137}
14138
14139/**
14140 * @note Locks this object for reading.
14141 */
14142HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14143 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
14144{
14145 LogFlowThisFunc(("\n"));
14146
14147 AutoCaller autoCaller(this);
14148 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14149
14150 ComPtr<IInternalSessionControl> directControl;
14151 {
14152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14153 directControl = mData->mSession.mDirectControl;
14154 }
14155
14156 /* ignore notifications sent after #OnSessionEnd() is called */
14157 if (!directControl)
14158 return S_OK;
14159 /*
14160 * instead acting like callback we ask IVirtualBox deliver corresponding event
14161 */
14162
14163 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14164 return S_OK;
14165}
14166
14167/**
14168 * @note Locks this object for reading.
14169 */
14170HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14171{
14172 LogFlowThisFunc(("\n"));
14173
14174 AutoCaller autoCaller(this);
14175 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14176
14177 ComPtr<IInternalSessionControl> directControl;
14178 {
14179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14180 directControl = mData->mSession.mDirectControl;
14181 }
14182
14183 /* ignore notifications sent after #OnSessionEnd() is called */
14184 if (!directControl)
14185 return S_OK;
14186
14187 return directControl->OnSerialPortChange(serialPort);
14188}
14189
14190/**
14191 * @note Locks this object for reading.
14192 */
14193HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14194{
14195 LogFlowThisFunc(("\n"));
14196
14197 AutoCaller autoCaller(this);
14198 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14199
14200 ComPtr<IInternalSessionControl> directControl;
14201 {
14202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14203 directControl = mData->mSession.mDirectControl;
14204 }
14205
14206 /* ignore notifications sent after #OnSessionEnd() is called */
14207 if (!directControl)
14208 return S_OK;
14209
14210 return directControl->OnParallelPortChange(parallelPort);
14211}
14212
14213/**
14214 * @note Locks this object for reading.
14215 */
14216HRESULT SessionMachine::onStorageControllerChange()
14217{
14218 LogFlowThisFunc(("\n"));
14219
14220 AutoCaller autoCaller(this);
14221 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14222
14223 ComPtr<IInternalSessionControl> directControl;
14224 {
14225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14226 directControl = mData->mSession.mDirectControl;
14227 }
14228
14229 /* ignore notifications sent after #OnSessionEnd() is called */
14230 if (!directControl)
14231 return S_OK;
14232
14233 return directControl->OnStorageControllerChange();
14234}
14235
14236/**
14237 * @note Locks this object for reading.
14238 */
14239HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14240{
14241 LogFlowThisFunc(("\n"));
14242
14243 AutoCaller autoCaller(this);
14244 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14245
14246 ComPtr<IInternalSessionControl> directControl;
14247 {
14248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14249 directControl = mData->mSession.mDirectControl;
14250 }
14251
14252 /* ignore notifications sent after #OnSessionEnd() is called */
14253 if (!directControl)
14254 return S_OK;
14255
14256 return directControl->OnMediumChange(aAttachment, aForce);
14257}
14258
14259/**
14260 * @note Locks this object for reading.
14261 */
14262HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14263{
14264 LogFlowThisFunc(("\n"));
14265
14266 AutoCaller autoCaller(this);
14267 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14268
14269 ComPtr<IInternalSessionControl> directControl;
14270 {
14271 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14272 directControl = mData->mSession.mDirectControl;
14273 }
14274
14275 /* ignore notifications sent after #OnSessionEnd() is called */
14276 if (!directControl)
14277 return S_OK;
14278
14279 return directControl->OnCPUChange(aCPU, aRemove);
14280}
14281
14282HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14283{
14284 LogFlowThisFunc(("\n"));
14285
14286 AutoCaller autoCaller(this);
14287 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14288
14289 ComPtr<IInternalSessionControl> directControl;
14290 {
14291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14292 directControl = mData->mSession.mDirectControl;
14293 }
14294
14295 /* ignore notifications sent after #OnSessionEnd() is called */
14296 if (!directControl)
14297 return S_OK;
14298
14299 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14300}
14301
14302/**
14303 * @note Locks this object for reading.
14304 */
14305HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14306{
14307 LogFlowThisFunc(("\n"));
14308
14309 AutoCaller autoCaller(this);
14310 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14311
14312 ComPtr<IInternalSessionControl> directControl;
14313 {
14314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14315 directControl = mData->mSession.mDirectControl;
14316 }
14317
14318 /* ignore notifications sent after #OnSessionEnd() is called */
14319 if (!directControl)
14320 return S_OK;
14321
14322 return directControl->OnVRDEServerChange(aRestart);
14323}
14324
14325/**
14326 * @note Locks this object for reading.
14327 */
14328HRESULT SessionMachine::onVideoCaptureChange()
14329{
14330 LogFlowThisFunc(("\n"));
14331
14332 AutoCaller autoCaller(this);
14333 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14334
14335 ComPtr<IInternalSessionControl> directControl;
14336 {
14337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14338 directControl = mData->mSession.mDirectControl;
14339 }
14340
14341 /* ignore notifications sent after #OnSessionEnd() is called */
14342 if (!directControl)
14343 return S_OK;
14344
14345 return directControl->OnVideoCaptureChange();
14346}
14347
14348/**
14349 * @note Locks this object for reading.
14350 */
14351HRESULT SessionMachine::onUSBControllerChange()
14352{
14353 LogFlowThisFunc(("\n"));
14354
14355 AutoCaller autoCaller(this);
14356 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14357
14358 ComPtr<IInternalSessionControl> directControl;
14359 {
14360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14361 directControl = mData->mSession.mDirectControl;
14362 }
14363
14364 /* ignore notifications sent after #OnSessionEnd() is called */
14365 if (!directControl)
14366 return S_OK;
14367
14368 return directControl->OnUSBControllerChange();
14369}
14370
14371/**
14372 * @note Locks this object for reading.
14373 */
14374HRESULT SessionMachine::onSharedFolderChange()
14375{
14376 LogFlowThisFunc(("\n"));
14377
14378 AutoCaller autoCaller(this);
14379 AssertComRCReturnRC(autoCaller.rc());
14380
14381 ComPtr<IInternalSessionControl> directControl;
14382 {
14383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14384 directControl = mData->mSession.mDirectControl;
14385 }
14386
14387 /* ignore notifications sent after #OnSessionEnd() is called */
14388 if (!directControl)
14389 return S_OK;
14390
14391 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14392}
14393
14394/**
14395 * @note Locks this object for reading.
14396 */
14397HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14398{
14399 LogFlowThisFunc(("\n"));
14400
14401 AutoCaller autoCaller(this);
14402 AssertComRCReturnRC(autoCaller.rc());
14403
14404 ComPtr<IInternalSessionControl> directControl;
14405 {
14406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14407 directControl = mData->mSession.mDirectControl;
14408 }
14409
14410 /* ignore notifications sent after #OnSessionEnd() is called */
14411 if (!directControl)
14412 return S_OK;
14413
14414 return directControl->OnClipboardModeChange(aClipboardMode);
14415}
14416
14417/**
14418 * @note Locks this object for reading.
14419 */
14420HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14421{
14422 LogFlowThisFunc(("\n"));
14423
14424 AutoCaller autoCaller(this);
14425 AssertComRCReturnRC(autoCaller.rc());
14426
14427 ComPtr<IInternalSessionControl> directControl;
14428 {
14429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14430 directControl = mData->mSession.mDirectControl;
14431 }
14432
14433 /* ignore notifications sent after #OnSessionEnd() is called */
14434 if (!directControl)
14435 return S_OK;
14436
14437 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14438}
14439
14440/**
14441 * @note Locks this object for reading.
14442 */
14443HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14444{
14445 LogFlowThisFunc(("\n"));
14446
14447 AutoCaller autoCaller(this);
14448 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14449
14450 ComPtr<IInternalSessionControl> directControl;
14451 {
14452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14453 directControl = mData->mSession.mDirectControl;
14454 }
14455
14456 /* ignore notifications sent after #OnSessionEnd() is called */
14457 if (!directControl)
14458 return S_OK;
14459
14460 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14461}
14462
14463/**
14464 * @note Locks this object for reading.
14465 */
14466HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14467{
14468 LogFlowThisFunc(("\n"));
14469
14470 AutoCaller autoCaller(this);
14471 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14472
14473 ComPtr<IInternalSessionControl> directControl;
14474 {
14475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14476 directControl = mData->mSession.mDirectControl;
14477 }
14478
14479 /* ignore notifications sent after #OnSessionEnd() is called */
14480 if (!directControl)
14481 return S_OK;
14482
14483 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14484}
14485
14486/**
14487 * Returns @c true if this machine's USB controller reports it has a matching
14488 * filter for the given USB device and @c false otherwise.
14489 *
14490 * @note locks this object for reading.
14491 */
14492bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14493{
14494 AutoCaller autoCaller(this);
14495 /* silently return if not ready -- this method may be called after the
14496 * direct machine session has been called */
14497 if (!autoCaller.isOk())
14498 return false;
14499
14500#ifdef VBOX_WITH_USB
14501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14502
14503 switch (mData->mMachineState)
14504 {
14505 case MachineState_Starting:
14506 case MachineState_Restoring:
14507 case MachineState_TeleportingIn:
14508 case MachineState_Paused:
14509 case MachineState_Running:
14510 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14511 * elsewhere... */
14512 alock.release();
14513 return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs);
14514 default: break;
14515 }
14516#else
14517 NOREF(aDevice);
14518 NOREF(aMaskedIfs);
14519#endif
14520 return false;
14521}
14522
14523/**
14524 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14525 */
14526HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14527 IVirtualBoxErrorInfo *aError,
14528 ULONG aMaskedIfs)
14529{
14530 LogFlowThisFunc(("\n"));
14531
14532 AutoCaller autoCaller(this);
14533
14534 /* This notification may happen after the machine object has been
14535 * uninitialized (the session was closed), so don't assert. */
14536 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14537
14538 ComPtr<IInternalSessionControl> directControl;
14539 {
14540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14541 directControl = mData->mSession.mDirectControl;
14542 }
14543
14544 /* fail on notifications sent after #OnSessionEnd() is called, it is
14545 * expected by the caller */
14546 if (!directControl)
14547 return E_FAIL;
14548
14549 /* No locks should be held at this point. */
14550 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14551 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14552
14553 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14554}
14555
14556/**
14557 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14558 */
14559HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14560 IVirtualBoxErrorInfo *aError)
14561{
14562 LogFlowThisFunc(("\n"));
14563
14564 AutoCaller autoCaller(this);
14565
14566 /* This notification may happen after the machine object has been
14567 * uninitialized (the session was closed), so don't assert. */
14568 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14569
14570 ComPtr<IInternalSessionControl> directControl;
14571 {
14572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14573 directControl = mData->mSession.mDirectControl;
14574 }
14575
14576 /* fail on notifications sent after #OnSessionEnd() is called, it is
14577 * expected by the caller */
14578 if (!directControl)
14579 return E_FAIL;
14580
14581 /* No locks should be held at this point. */
14582 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14583 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14584
14585 return directControl->OnUSBDeviceDetach(aId, aError);
14586}
14587
14588// protected methods
14589/////////////////////////////////////////////////////////////////////////////
14590
14591/**
14592 * Helper method to finalize saving the state.
14593 *
14594 * @note Must be called from under this object's lock.
14595 *
14596 * @param aRc S_OK if the snapshot has been taken successfully
14597 * @param aErrMsg human readable error message for failure
14598 *
14599 * @note Locks mParent + this objects for writing.
14600 */
14601HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14602{
14603 LogFlowThisFuncEnter();
14604
14605 AutoCaller autoCaller(this);
14606 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14607
14608 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14609
14610 HRESULT rc = S_OK;
14611
14612 if (SUCCEEDED(aRc))
14613 {
14614 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14615
14616 /* save all VM settings */
14617 rc = saveSettings(NULL);
14618 // no need to check whether VirtualBox.xml needs saving also since
14619 // we can't have a name change pending at this point
14620 }
14621 else
14622 {
14623 // delete the saved state file (it might have been already created);
14624 // we need not check whether this is shared with a snapshot here because
14625 // we certainly created this saved state file here anew
14626 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14627 }
14628
14629 /* notify the progress object about operation completion */
14630 Assert(mConsoleTaskData.mProgress);
14631 if (SUCCEEDED(aRc))
14632 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14633 else
14634 {
14635 if (aErrMsg.length())
14636 mConsoleTaskData.mProgress->notifyComplete(aRc,
14637 COM_IIDOF(ISession),
14638 getComponentName(),
14639 aErrMsg.c_str());
14640 else
14641 mConsoleTaskData.mProgress->notifyComplete(aRc);
14642 }
14643
14644 /* clear out the temporary saved state data */
14645 mConsoleTaskData.mLastState = MachineState_Null;
14646 mConsoleTaskData.strStateFilePath.setNull();
14647 mConsoleTaskData.mProgress.setNull();
14648
14649 LogFlowThisFuncLeave();
14650 return rc;
14651}
14652
14653/**
14654 * Deletes the given file if it is no longer in use by either the current machine state
14655 * (if the machine is "saved") or any of the machine's snapshots.
14656 *
14657 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14658 * but is different for each SnapshotMachine. When calling this, the order of calling this
14659 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14660 * is therefore critical. I know, it's all rather messy.
14661 *
14662 * @param strStateFile
14663 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14664 */
14665void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14666 Snapshot *pSnapshotToIgnore)
14667{
14668 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14669 if ( (strStateFile.isNotEmpty())
14670 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14671 )
14672 // ... and it must also not be shared with other snapshots
14673 if ( !mData->mFirstSnapshot
14674 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14675 // this checks the SnapshotMachine's state file paths
14676 )
14677 RTFileDelete(strStateFile.c_str());
14678}
14679
14680/**
14681 * Locks the attached media.
14682 *
14683 * All attached hard disks are locked for writing and DVD/floppy are locked for
14684 * reading. Parents of attached hard disks (if any) are locked for reading.
14685 *
14686 * This method also performs accessibility check of all media it locks: if some
14687 * media is inaccessible, the method will return a failure and a bunch of
14688 * extended error info objects per each inaccessible medium.
14689 *
14690 * Note that this method is atomic: if it returns a success, all media are
14691 * locked as described above; on failure no media is locked at all (all
14692 * succeeded individual locks will be undone).
14693 *
14694 * The caller is responsible for doing the necessary state sanity checks.
14695 *
14696 * The locks made by this method must be undone by calling #unlockMedia() when
14697 * no more needed.
14698 */
14699HRESULT SessionMachine::lockMedia()
14700{
14701 AutoCaller autoCaller(this);
14702 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14703
14704 AutoMultiWriteLock2 alock(this->lockHandle(),
14705 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14706
14707 /* bail out if trying to lock things with already set up locking */
14708 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14709
14710 MultiResult mrc(S_OK);
14711
14712 /* Collect locking information for all medium objects attached to the VM. */
14713 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14714 it != mMediaData->mAttachments.end();
14715 ++it)
14716 {
14717 MediumAttachment* pAtt = *it;
14718 DeviceType_T devType = pAtt->getType();
14719 Medium *pMedium = pAtt->getMedium();
14720
14721 MediumLockList *pMediumLockList(new MediumLockList());
14722 // There can be attachments without a medium (floppy/dvd), and thus
14723 // it's impossible to create a medium lock list. It still makes sense
14724 // to have the empty medium lock list in the map in case a medium is
14725 // attached later.
14726 if (pMedium != NULL)
14727 {
14728 MediumType_T mediumType = pMedium->getType();
14729 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14730 || mediumType == MediumType_Shareable;
14731 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14732
14733 alock.release();
14734 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14735 !fIsReadOnlyLock /* fMediumLockWrite */,
14736 NULL,
14737 *pMediumLockList);
14738 alock.acquire();
14739 if (FAILED(mrc))
14740 {
14741 delete pMediumLockList;
14742 mData->mSession.mLockedMedia.Clear();
14743 break;
14744 }
14745 }
14746
14747 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14748 if (FAILED(rc))
14749 {
14750 mData->mSession.mLockedMedia.Clear();
14751 mrc = setError(rc,
14752 tr("Collecting locking information for all attached media failed"));
14753 break;
14754 }
14755 }
14756
14757 if (SUCCEEDED(mrc))
14758 {
14759 /* Now lock all media. If this fails, nothing is locked. */
14760 alock.release();
14761 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14762 alock.acquire();
14763 if (FAILED(rc))
14764 {
14765 mrc = setError(rc,
14766 tr("Locking of attached media failed"));
14767 }
14768 }
14769
14770 return mrc;
14771}
14772
14773/**
14774 * Undoes the locks made by by #lockMedia().
14775 */
14776void SessionMachine::unlockMedia()
14777{
14778 AutoCaller autoCaller(this);
14779 AssertComRCReturnVoid(autoCaller.rc());
14780
14781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14782
14783 /* we may be holding important error info on the current thread;
14784 * preserve it */
14785 ErrorInfoKeeper eik;
14786
14787 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14788 AssertComRC(rc);
14789}
14790
14791/**
14792 * Helper to change the machine state (reimplementation).
14793 *
14794 * @note Locks this object for writing.
14795 * @note This method must not call saveSettings or SaveSettings, otherwise
14796 * it can cause crashes in random places due to unexpectedly committing
14797 * the current settings. The caller is responsible for that. The call
14798 * to saveStateSettings is fine, because this method does not commit.
14799 */
14800HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14801{
14802 LogFlowThisFuncEnter();
14803 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14804
14805 AutoCaller autoCaller(this);
14806 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14807
14808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14809
14810 MachineState_T oldMachineState = mData->mMachineState;
14811
14812 AssertMsgReturn(oldMachineState != aMachineState,
14813 ("oldMachineState=%s, aMachineState=%s\n",
14814 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14815 E_FAIL);
14816
14817 HRESULT rc = S_OK;
14818
14819 int stsFlags = 0;
14820 bool deleteSavedState = false;
14821
14822 /* detect some state transitions */
14823
14824 if ( ( oldMachineState == MachineState_Saved
14825 && aMachineState == MachineState_Restoring)
14826 || ( ( oldMachineState == MachineState_PoweredOff
14827 || oldMachineState == MachineState_Teleported
14828 || oldMachineState == MachineState_Aborted
14829 )
14830 && ( aMachineState == MachineState_TeleportingIn
14831 || aMachineState == MachineState_Starting
14832 )
14833 )
14834 )
14835 {
14836 /* The EMT thread is about to start */
14837
14838 /* Nothing to do here for now... */
14839
14840 /// @todo NEWMEDIA don't let mDVDDrive and other children
14841 /// change anything when in the Starting/Restoring state
14842 }
14843 else if ( ( oldMachineState == MachineState_Running
14844 || oldMachineState == MachineState_Paused
14845 || oldMachineState == MachineState_Teleporting
14846 || oldMachineState == MachineState_LiveSnapshotting
14847 || oldMachineState == MachineState_Stuck
14848 || oldMachineState == MachineState_Starting
14849 || oldMachineState == MachineState_Stopping
14850 || oldMachineState == MachineState_Saving
14851 || oldMachineState == MachineState_Restoring
14852 || oldMachineState == MachineState_TeleportingPausedVM
14853 || oldMachineState == MachineState_TeleportingIn
14854 )
14855 && ( aMachineState == MachineState_PoweredOff
14856 || aMachineState == MachineState_Saved
14857 || aMachineState == MachineState_Teleported
14858 || aMachineState == MachineState_Aborted
14859 )
14860 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14861 * snapshot */
14862 && ( mConsoleTaskData.mSnapshot.isNull()
14863 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14864 )
14865 )
14866 {
14867 /* The EMT thread has just stopped, unlock attached media. Note that as
14868 * opposed to locking that is done from Console, we do unlocking here
14869 * because the VM process may have aborted before having a chance to
14870 * properly unlock all media it locked. */
14871
14872 unlockMedia();
14873 }
14874
14875 if (oldMachineState == MachineState_Restoring)
14876 {
14877 if (aMachineState != MachineState_Saved)
14878 {
14879 /*
14880 * delete the saved state file once the machine has finished
14881 * restoring from it (note that Console sets the state from
14882 * Restoring to Saved if the VM couldn't restore successfully,
14883 * to give the user an ability to fix an error and retry --
14884 * we keep the saved state file in this case)
14885 */
14886 deleteSavedState = true;
14887 }
14888 }
14889 else if ( oldMachineState == MachineState_Saved
14890 && ( aMachineState == MachineState_PoweredOff
14891 || aMachineState == MachineState_Aborted
14892 || aMachineState == MachineState_Teleported
14893 )
14894 )
14895 {
14896 /*
14897 * delete the saved state after Console::ForgetSavedState() is called
14898 * or if the VM process (owning a direct VM session) crashed while the
14899 * VM was Saved
14900 */
14901
14902 /// @todo (dmik)
14903 // Not sure that deleting the saved state file just because of the
14904 // client death before it attempted to restore the VM is a good
14905 // thing. But when it crashes we need to go to the Aborted state
14906 // which cannot have the saved state file associated... The only
14907 // way to fix this is to make the Aborted condition not a VM state
14908 // but a bool flag: i.e., when a crash occurs, set it to true and
14909 // change the state to PoweredOff or Saved depending on the
14910 // saved state presence.
14911
14912 deleteSavedState = true;
14913 mData->mCurrentStateModified = TRUE;
14914 stsFlags |= SaveSTS_CurStateModified;
14915 }
14916
14917 if ( aMachineState == MachineState_Starting
14918 || aMachineState == MachineState_Restoring
14919 || aMachineState == MachineState_TeleportingIn
14920 )
14921 {
14922 /* set the current state modified flag to indicate that the current
14923 * state is no more identical to the state in the
14924 * current snapshot */
14925 if (!mData->mCurrentSnapshot.isNull())
14926 {
14927 mData->mCurrentStateModified = TRUE;
14928 stsFlags |= SaveSTS_CurStateModified;
14929 }
14930 }
14931
14932 if (deleteSavedState)
14933 {
14934 if (mRemoveSavedState)
14935 {
14936 Assert(!mSSData->strStateFilePath.isEmpty());
14937
14938 // it is safe to delete the saved state file if ...
14939 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14940 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14941 // ... none of the snapshots share the saved state file
14942 )
14943 RTFileDelete(mSSData->strStateFilePath.c_str());
14944 }
14945
14946 mSSData->strStateFilePath.setNull();
14947 stsFlags |= SaveSTS_StateFilePath;
14948 }
14949
14950 /* redirect to the underlying peer machine */
14951 mPeer->setMachineState(aMachineState);
14952
14953 if ( aMachineState == MachineState_PoweredOff
14954 || aMachineState == MachineState_Teleported
14955 || aMachineState == MachineState_Aborted
14956 || aMachineState == MachineState_Saved)
14957 {
14958 /* the machine has stopped execution
14959 * (or the saved state file was adopted) */
14960 stsFlags |= SaveSTS_StateTimeStamp;
14961 }
14962
14963 if ( ( oldMachineState == MachineState_PoweredOff
14964 || oldMachineState == MachineState_Aborted
14965 || oldMachineState == MachineState_Teleported
14966 )
14967 && aMachineState == MachineState_Saved)
14968 {
14969 /* the saved state file was adopted */
14970 Assert(!mSSData->strStateFilePath.isEmpty());
14971 stsFlags |= SaveSTS_StateFilePath;
14972 }
14973
14974#ifdef VBOX_WITH_GUEST_PROPS
14975 if ( aMachineState == MachineState_PoweredOff
14976 || aMachineState == MachineState_Aborted
14977 || aMachineState == MachineState_Teleported)
14978 {
14979 /* Make sure any transient guest properties get removed from the
14980 * property store on shutdown. */
14981
14982 HWData::GuestPropertyMap::const_iterator it;
14983 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14984 if (!fNeedsSaving)
14985 for (it = mHWData->mGuestProperties.begin();
14986 it != mHWData->mGuestProperties.end(); ++it)
14987 if ( (it->second.mFlags & guestProp::TRANSIENT)
14988 || (it->second.mFlags & guestProp::TRANSRESET))
14989 {
14990 fNeedsSaving = true;
14991 break;
14992 }
14993 if (fNeedsSaving)
14994 {
14995 mData->mCurrentStateModified = TRUE;
14996 stsFlags |= SaveSTS_CurStateModified;
14997 }
14998 }
14999#endif
15000
15001 rc = saveStateSettings(stsFlags);
15002
15003 if ( ( oldMachineState != MachineState_PoweredOff
15004 && oldMachineState != MachineState_Aborted
15005 && oldMachineState != MachineState_Teleported
15006 )
15007 && ( aMachineState == MachineState_PoweredOff
15008 || aMachineState == MachineState_Aborted
15009 || aMachineState == MachineState_Teleported
15010 )
15011 )
15012 {
15013 /* we've been shut down for any reason */
15014 /* no special action so far */
15015 }
15016
15017 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15018 LogFlowThisFuncLeave();
15019 return rc;
15020}
15021
15022/**
15023 * Sends the current machine state value to the VM process.
15024 *
15025 * @note Locks this object for reading, then calls a client process.
15026 */
15027HRESULT SessionMachine::updateMachineStateOnClient()
15028{
15029 AutoCaller autoCaller(this);
15030 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15031
15032 ComPtr<IInternalSessionControl> directControl;
15033 {
15034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15035 AssertReturn(!!mData, E_FAIL);
15036 directControl = mData->mSession.mDirectControl;
15037
15038 /* directControl may be already set to NULL here in #OnSessionEnd()
15039 * called too early by the direct session process while there is still
15040 * some operation (like deleting the snapshot) in progress. The client
15041 * process in this case is waiting inside Session::close() for the
15042 * "end session" process object to complete, while #uninit() called by
15043 * #checkForDeath() on the Watcher thread is waiting for the pending
15044 * operation to complete. For now, we accept this inconsistent behavior
15045 * and simply do nothing here. */
15046
15047 if (mData->mSession.mState == SessionState_Unlocking)
15048 return S_OK;
15049
15050 AssertReturn(!directControl.isNull(), E_FAIL);
15051 }
15052
15053 return directControl->UpdateMachineState(mData->mMachineState);
15054}
Note: See TracBrowser for help on using the repository browser.

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