VirtualBox

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

Last change on this file since 48297 was 48297, checked in by vboxsync, 12 years ago

Main/Medium: redesign API level medium locking, needed conversions from MediaList to MediumLockLists in several places, forced cleanups elsewhere, too
Main/Token: introduced token objects for controlling the unlocking, will be used as a general concept in the future
Main/Snapshot: snapshot deletion needed significant cleanups as it was still using many shortcuts, directly calling the API to lock media instead of using lock lists. Now much better, and the online snapshot deletion is also a lot cleaner as it no longer passes unnecessary parameters around which are already known in the machine/snapshot code
Main/MediumLock: small improvements, now has a mode which skips locking already locked media, needed by the Snapshot code where we have overlapping lock lists and have to update the original one instead
Main/Console+Session+Machine: follow-up changes for the online snapshot merging parameter passing simplification, plus an unrelated lock order violation fix in Machine which happens only for inaccessible machines
Main/testcase: update correspondingly
doc: update SDK reference

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 499.9 KB
Line 
1/* $Id: MachineImpl.cpp 48297 2013-09-05 09:57:44Z 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 mHPETEnabled = false;
191
192 /* default boot order: floppy - DVD - HDD */
193 mBootOrder[0] = DeviceType_Floppy;
194 mBootOrder[1] = DeviceType_DVD;
195 mBootOrder[2] = DeviceType_HardDisk;
196 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
197 mBootOrder[i] = DeviceType_Null;
198
199 mClipboardMode = ClipboardMode_Disabled;
200 mDragAndDropMode = DragAndDropMode_Disabled;
201 mGuestPropertyNotificationPatterns = "";
202
203 mFirmwareType = FirmwareType_BIOS;
204 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
205 mPointingHIDType = PointingHIDType_PS2Mouse;
206 mChipsetType = ChipsetType_PIIX3;
207 mEmulatedUSBWebcamEnabled = FALSE;
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->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]->applyDefaults(aOsType);
351
352 /* Let the OS type select 64-bit ness. */
353 mHWData->mLongMode = aOsType->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.isValid())
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(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1633{
1634#ifdef VBOX_WITH_USB_VIDEO
1635 CheckComArgOutPointerValid(aEnabled);
1636
1637 AutoCaller autoCaller(this);
1638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1639
1640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1641
1642 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1643
1644 return S_OK;
1645#else
1646 NOREF(aEnabled);
1647 return E_NOTIMPL;
1648#endif
1649}
1650
1651STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1652{
1653#ifdef VBOX_WITH_USB_VIDEO
1654 AutoCaller autoCaller(this);
1655 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1657
1658 HRESULT rc = checkStateDependency(MutableStateDep);
1659 if (FAILED(rc)) return rc;
1660
1661 setModified(IsModified_MachineData);
1662 mHWData.backup();
1663 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1664
1665 return S_OK;
1666#else
1667 NOREF(aEnabled);
1668 return E_NOTIMPL;
1669#endif
1670}
1671
1672STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1673{
1674 CheckComArgOutPointerValid(aEnabled);
1675
1676 AutoCaller autoCaller(this);
1677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 *aEnabled = mHWData->mHPETEnabled;
1681
1682 return S_OK;
1683}
1684
1685STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1686{
1687 HRESULT rc = S_OK;
1688
1689 AutoCaller autoCaller(this);
1690 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1692
1693 rc = checkStateDependency(MutableStateDep);
1694 if (FAILED(rc)) return rc;
1695
1696 setModified(IsModified_MachineData);
1697 mHWData.backup();
1698
1699 mHWData->mHPETEnabled = aEnabled;
1700
1701 return rc;
1702}
1703
1704STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1705{
1706 AutoCaller autoCaller(this);
1707 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1708
1709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1710
1711 *fEnabled = mHWData->mVideoCaptureEnabled;
1712 return S_OK;
1713}
1714
1715STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1716{
1717 HRESULT rc = S_OK;
1718
1719 AutoCaller autoCaller(this);
1720 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1722
1723 setModified(IsModified_MachineData);
1724 mHWData.backup();
1725 mHWData->mVideoCaptureEnabled = fEnabled;
1726
1727 alock.release();
1728 rc = onVideoCaptureChange();
1729 alock.acquire();
1730 if (FAILED(rc))
1731 {
1732 /*
1733 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1734 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1735 * determine if it should start or stop capturing. Therefore we need to manually
1736 * undo change.
1737 */
1738 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1739 return rc;
1740 }
1741
1742 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1743 if (Global::IsOnline(mData->mMachineState))
1744 saveSettings(NULL);
1745
1746 return rc;
1747}
1748
1749STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1750{
1751 CheckComArgOutSafeArrayPointerValid(aScreens);
1752
1753 AutoCaller autoCaller(this);
1754 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1755
1756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1757
1758 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1759 for (unsigned i = 0; i < screens.size(); i++)
1760 screens[i] = mHWData->maVideoCaptureScreens[i];
1761 screens.detachTo(ComSafeArrayOutArg(aScreens));
1762 return S_OK;
1763}
1764
1765STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1766{
1767 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1768 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1769 bool fChanged = false;
1770
1771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1772
1773 for (unsigned i = 0; i < screens.size(); i++)
1774 {
1775 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1776 {
1777 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1778 fChanged = true;
1779 }
1780 }
1781 if (fChanged)
1782 {
1783 alock.release();
1784 HRESULT rc = onVideoCaptureChange();
1785 alock.acquire();
1786 if (FAILED(rc)) return rc;
1787 setModified(IsModified_MachineData);
1788
1789 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1790 if (Global::IsOnline(mData->mMachineState))
1791 saveSettings(NULL);
1792 }
1793
1794 return S_OK;
1795}
1796
1797STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1798{
1799 AutoCaller autoCaller(this);
1800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1801
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803 if (mHWData->mVideoCaptureFile.isEmpty())
1804 {
1805 Utf8Str defaultFile;
1806 getDefaultVideoCaptureFile(defaultFile);
1807 defaultFile.cloneTo(apFile);
1808 }
1809 else
1810 mHWData->mVideoCaptureFile.cloneTo(apFile);
1811 return S_OK;
1812}
1813
1814STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1815{
1816 Utf8Str strFile(aFile);
1817 AutoCaller autoCaller(this);
1818 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1819
1820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1821
1822 if ( Global::IsOnline(mData->mMachineState)
1823 && mHWData->mVideoCaptureEnabled)
1824 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1825
1826 if (!RTPathStartsWithRoot(strFile.c_str()))
1827 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1828
1829 if (!strFile.isEmpty())
1830 {
1831 Utf8Str defaultFile;
1832 getDefaultVideoCaptureFile(defaultFile);
1833 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1834 strFile.setNull();
1835 }
1836
1837 setModified(IsModified_MachineData);
1838 mHWData.backup();
1839 mHWData->mVideoCaptureFile = strFile;
1840
1841 return S_OK;
1842}
1843
1844STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1845{
1846 AutoCaller autoCaller(this);
1847 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1848
1849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1850 *aHorzRes = mHWData->mVideoCaptureWidth;
1851 return S_OK;
1852}
1853
1854STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1855{
1856 AutoCaller autoCaller(this);
1857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1858
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 if ( Global::IsOnline(mData->mMachineState)
1862 && mHWData->mVideoCaptureEnabled)
1863 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1864
1865 setModified(IsModified_MachineData);
1866 mHWData.backup();
1867 mHWData->mVideoCaptureWidth = aHorzRes;
1868
1869 return S_OK;
1870}
1871
1872STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1873{
1874 AutoCaller autoCaller(this);
1875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1876
1877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1878 *aVertRes = mHWData->mVideoCaptureHeight;
1879 return S_OK;
1880}
1881
1882STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1883{
1884 AutoCaller autoCaller(this);
1885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1886
1887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1888
1889 if ( Global::IsOnline(mData->mMachineState)
1890 && mHWData->mVideoCaptureEnabled)
1891 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1892
1893 setModified(IsModified_MachineData);
1894 mHWData.backup();
1895 mHWData->mVideoCaptureHeight = aVertRes;
1896
1897 return S_OK;
1898}
1899
1900STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1901{
1902 AutoCaller autoCaller(this);
1903 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1904
1905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1906 *aRate = mHWData->mVideoCaptureRate;
1907 return S_OK;
1908}
1909
1910STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1911{
1912 AutoCaller autoCaller(this);
1913 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1914
1915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1916
1917 if ( Global::IsOnline(mData->mMachineState)
1918 && mHWData->mVideoCaptureEnabled)
1919 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1920
1921 setModified(IsModified_MachineData);
1922 mHWData.backup();
1923 mHWData->mVideoCaptureRate = aRate;
1924
1925 return S_OK;
1926}
1927
1928STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1929{
1930 AutoCaller autoCaller(this);
1931 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1932
1933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1934 *aFPS = mHWData->mVideoCaptureFPS;
1935 return S_OK;
1936}
1937
1938STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1939{
1940 AutoCaller autoCaller(this);
1941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1942
1943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1944
1945 if ( Global::IsOnline(mData->mMachineState)
1946 && mHWData->mVideoCaptureEnabled)
1947 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1948
1949 setModified(IsModified_MachineData);
1950 mHWData.backup();
1951 mHWData->mVideoCaptureFPS = aFPS;
1952
1953 return S_OK;
1954}
1955
1956STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1957{
1958 CheckComArgOutPointerValid(aGraphicsControllerType);
1959
1960 AutoCaller autoCaller(this);
1961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1962
1963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1964
1965 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1966
1967 return S_OK;
1968}
1969
1970STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1971{
1972 switch (aGraphicsControllerType)
1973 {
1974 case GraphicsControllerType_Null:
1975 case GraphicsControllerType_VBoxVGA:
1976 break;
1977 default:
1978 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1979 }
1980
1981 AutoCaller autoCaller(this);
1982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1983
1984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1985
1986 HRESULT rc = checkStateDependency(MutableStateDep);
1987 if (FAILED(rc)) return rc;
1988
1989 setModified(IsModified_MachineData);
1990 mHWData.backup();
1991 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1992
1993 return S_OK;
1994}
1995
1996STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1997{
1998 CheckComArgOutPointerValid(memorySize);
1999
2000 AutoCaller autoCaller(this);
2001 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2002
2003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2004
2005 *memorySize = mHWData->mVRAMSize;
2006
2007 return S_OK;
2008}
2009
2010STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
2011{
2012 /* check VRAM limits */
2013 if (memorySize < SchemaDefs::MinGuestVRAM ||
2014 memorySize > SchemaDefs::MaxGuestVRAM)
2015 return setError(E_INVALIDARG,
2016 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2017 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2018
2019 AutoCaller autoCaller(this);
2020 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2021
2022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2023
2024 HRESULT rc = checkStateDependency(MutableStateDep);
2025 if (FAILED(rc)) return rc;
2026
2027 setModified(IsModified_MachineData);
2028 mHWData.backup();
2029 mHWData->mVRAMSize = memorySize;
2030
2031 return S_OK;
2032}
2033
2034/** @todo this method should not be public */
2035STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2036{
2037 CheckComArgOutPointerValid(memoryBalloonSize);
2038
2039 AutoCaller autoCaller(this);
2040 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2041
2042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2043
2044 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2045
2046 return S_OK;
2047}
2048
2049/**
2050 * Set the memory balloon size.
2051 *
2052 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2053 * we have to make sure that we never call IGuest from here.
2054 */
2055STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2056{
2057 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2058#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2059 /* check limits */
2060 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2061 return setError(E_INVALIDARG,
2062 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2063 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2064
2065 AutoCaller autoCaller(this);
2066 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2067
2068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2069
2070 setModified(IsModified_MachineData);
2071 mHWData.backup();
2072 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2073
2074 return S_OK;
2075#else
2076 NOREF(memoryBalloonSize);
2077 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2078#endif
2079}
2080
2081STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2082{
2083 CheckComArgOutPointerValid(aEnabled);
2084
2085 AutoCaller autoCaller(this);
2086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2087
2088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2089
2090 *aEnabled = mHWData->mPageFusionEnabled;
2091 return S_OK;
2092}
2093
2094STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2095{
2096#ifdef VBOX_WITH_PAGE_SHARING
2097 AutoCaller autoCaller(this);
2098 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2099
2100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2101
2102 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2103 setModified(IsModified_MachineData);
2104 mHWData.backup();
2105 mHWData->mPageFusionEnabled = aEnabled;
2106 return S_OK;
2107#else
2108 NOREF(aEnabled);
2109 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2110#endif
2111}
2112
2113STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2114{
2115 CheckComArgOutPointerValid(aEnabled);
2116
2117 AutoCaller autoCaller(this);
2118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2119
2120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2121
2122 *aEnabled = mHWData->mAccelerate3DEnabled;
2123
2124 return S_OK;
2125}
2126
2127STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2128{
2129 AutoCaller autoCaller(this);
2130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2131
2132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2133
2134 HRESULT rc = checkStateDependency(MutableStateDep);
2135 if (FAILED(rc)) return rc;
2136
2137 /** @todo check validity! */
2138
2139 setModified(IsModified_MachineData);
2140 mHWData.backup();
2141 mHWData->mAccelerate3DEnabled = enable;
2142
2143 return S_OK;
2144}
2145
2146
2147STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2148{
2149 CheckComArgOutPointerValid(aEnabled);
2150
2151 AutoCaller autoCaller(this);
2152 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2153
2154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2155
2156 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2157
2158 return S_OK;
2159}
2160
2161STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2162{
2163 AutoCaller autoCaller(this);
2164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2165
2166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2167
2168 HRESULT rc = checkStateDependency(MutableStateDep);
2169 if (FAILED(rc)) return rc;
2170
2171 /** @todo check validity! */
2172
2173 setModified(IsModified_MachineData);
2174 mHWData.backup();
2175 mHWData->mAccelerate2DVideoEnabled = enable;
2176
2177 return S_OK;
2178}
2179
2180STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2181{
2182 CheckComArgOutPointerValid(monitorCount);
2183
2184 AutoCaller autoCaller(this);
2185 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2186
2187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2188
2189 *monitorCount = mHWData->mMonitorCount;
2190
2191 return S_OK;
2192}
2193
2194STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2195{
2196 /* make sure monitor count is a sensible number */
2197 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2198 return setError(E_INVALIDARG,
2199 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2200 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2201
2202 AutoCaller autoCaller(this);
2203 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2204
2205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2206
2207 HRESULT rc = checkStateDependency(MutableStateDep);
2208 if (FAILED(rc)) return rc;
2209
2210 setModified(IsModified_MachineData);
2211 mHWData.backup();
2212 mHWData->mMonitorCount = monitorCount;
2213
2214 return S_OK;
2215}
2216
2217STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2218{
2219 CheckComArgOutPointerValid(biosSettings);
2220
2221 AutoCaller autoCaller(this);
2222 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2223
2224 /* mBIOSSettings is constant during life time, no need to lock */
2225 mBIOSSettings.queryInterfaceTo(biosSettings);
2226
2227 return S_OK;
2228}
2229
2230STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2231{
2232 CheckComArgOutPointerValid(aVal);
2233
2234 AutoCaller autoCaller(this);
2235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2236
2237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2238
2239 switch (property)
2240 {
2241 case CPUPropertyType_PAE:
2242 *aVal = mHWData->mPAEEnabled;
2243 break;
2244
2245 case CPUPropertyType_Synthetic:
2246 *aVal = mHWData->mSyntheticCpu;
2247 break;
2248
2249 case CPUPropertyType_LongMode:
2250 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2251 *aVal = TRUE;
2252 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2253 *aVal = FALSE;
2254#if HC_ARCH_BITS == 64
2255 else
2256 *aVal = TRUE;
2257#else
2258 else
2259 {
2260 *aVal = FALSE;
2261
2262 ComPtr<IGuestOSType> ptrGuestOSType;
2263 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2264 if (SUCCEEDED(hrc2))
2265 {
2266 BOOL fIs64Bit = FALSE;
2267 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2268 if (SUCCEEDED(hrc2) && fIs64Bit)
2269 {
2270 ComObjPtr<Host> ptrHost = mParent->host();
2271 alock.release();
2272
2273 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2274 if (FAILED(hrc2))
2275 *aVal = FALSE;
2276 }
2277 }
2278 }
2279#endif
2280 break;
2281
2282 default:
2283 return E_INVALIDARG;
2284 }
2285 return S_OK;
2286}
2287
2288STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2289{
2290 AutoCaller autoCaller(this);
2291 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2292
2293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2294
2295 HRESULT rc = checkStateDependency(MutableStateDep);
2296 if (FAILED(rc)) return rc;
2297
2298 switch (property)
2299 {
2300 case CPUPropertyType_PAE:
2301 setModified(IsModified_MachineData);
2302 mHWData.backup();
2303 mHWData->mPAEEnabled = !!aVal;
2304 break;
2305
2306 case CPUPropertyType_Synthetic:
2307 setModified(IsModified_MachineData);
2308 mHWData.backup();
2309 mHWData->mSyntheticCpu = !!aVal;
2310 break;
2311
2312 case CPUPropertyType_LongMode:
2313 setModified(IsModified_MachineData);
2314 mHWData.backup();
2315 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2316 break;
2317
2318 default:
2319 return E_INVALIDARG;
2320 }
2321 return S_OK;
2322}
2323
2324STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2325{
2326 CheckComArgOutPointerValid(aValEax);
2327 CheckComArgOutPointerValid(aValEbx);
2328 CheckComArgOutPointerValid(aValEcx);
2329 CheckComArgOutPointerValid(aValEdx);
2330
2331 AutoCaller autoCaller(this);
2332 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2333
2334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2335
2336 switch(aId)
2337 {
2338 case 0x0:
2339 case 0x1:
2340 case 0x2:
2341 case 0x3:
2342 case 0x4:
2343 case 0x5:
2344 case 0x6:
2345 case 0x7:
2346 case 0x8:
2347 case 0x9:
2348 case 0xA:
2349 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2350 return E_INVALIDARG;
2351
2352 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2353 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2354 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2355 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2356 break;
2357
2358 case 0x80000000:
2359 case 0x80000001:
2360 case 0x80000002:
2361 case 0x80000003:
2362 case 0x80000004:
2363 case 0x80000005:
2364 case 0x80000006:
2365 case 0x80000007:
2366 case 0x80000008:
2367 case 0x80000009:
2368 case 0x8000000A:
2369 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2370 return E_INVALIDARG;
2371
2372 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2373 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2374 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2375 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2376 break;
2377
2378 default:
2379 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2380 }
2381 return S_OK;
2382}
2383
2384STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2385{
2386 AutoCaller autoCaller(this);
2387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2388
2389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2390
2391 HRESULT rc = checkStateDependency(MutableStateDep);
2392 if (FAILED(rc)) return rc;
2393
2394 switch(aId)
2395 {
2396 case 0x0:
2397 case 0x1:
2398 case 0x2:
2399 case 0x3:
2400 case 0x4:
2401 case 0x5:
2402 case 0x6:
2403 case 0x7:
2404 case 0x8:
2405 case 0x9:
2406 case 0xA:
2407 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2408 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2409 setModified(IsModified_MachineData);
2410 mHWData.backup();
2411 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2412 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2413 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2414 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2415 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2416 break;
2417
2418 case 0x80000000:
2419 case 0x80000001:
2420 case 0x80000002:
2421 case 0x80000003:
2422 case 0x80000004:
2423 case 0x80000005:
2424 case 0x80000006:
2425 case 0x80000007:
2426 case 0x80000008:
2427 case 0x80000009:
2428 case 0x8000000A:
2429 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2430 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2431 setModified(IsModified_MachineData);
2432 mHWData.backup();
2433 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2434 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2435 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2436 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2437 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2438 break;
2439
2440 default:
2441 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2442 }
2443 return S_OK;
2444}
2445
2446STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2447{
2448 AutoCaller autoCaller(this);
2449 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2450
2451 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2452
2453 HRESULT rc = checkStateDependency(MutableStateDep);
2454 if (FAILED(rc)) return rc;
2455
2456 switch(aId)
2457 {
2458 case 0x0:
2459 case 0x1:
2460 case 0x2:
2461 case 0x3:
2462 case 0x4:
2463 case 0x5:
2464 case 0x6:
2465 case 0x7:
2466 case 0x8:
2467 case 0x9:
2468 case 0xA:
2469 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2470 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2471 setModified(IsModified_MachineData);
2472 mHWData.backup();
2473 /* Invalidate leaf. */
2474 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2475 break;
2476
2477 case 0x80000000:
2478 case 0x80000001:
2479 case 0x80000002:
2480 case 0x80000003:
2481 case 0x80000004:
2482 case 0x80000005:
2483 case 0x80000006:
2484 case 0x80000007:
2485 case 0x80000008:
2486 case 0x80000009:
2487 case 0x8000000A:
2488 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2489 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2490 setModified(IsModified_MachineData);
2491 mHWData.backup();
2492 /* Invalidate leaf. */
2493 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2494 break;
2495
2496 default:
2497 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2498 }
2499 return S_OK;
2500}
2501
2502STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2503{
2504 AutoCaller autoCaller(this);
2505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2506
2507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2508
2509 HRESULT rc = checkStateDependency(MutableStateDep);
2510 if (FAILED(rc)) return rc;
2511
2512 setModified(IsModified_MachineData);
2513 mHWData.backup();
2514
2515 /* Invalidate all standard leafs. */
2516 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2517 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2518
2519 /* Invalidate all extended leafs. */
2520 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2521 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2522
2523 return S_OK;
2524}
2525
2526STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2527{
2528 CheckComArgOutPointerValid(aVal);
2529
2530 AutoCaller autoCaller(this);
2531 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2532
2533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2534
2535 switch(property)
2536 {
2537 case HWVirtExPropertyType_Enabled:
2538 *aVal = mHWData->mHWVirtExEnabled;
2539 break;
2540
2541 case HWVirtExPropertyType_VPID:
2542 *aVal = mHWData->mHWVirtExVPIDEnabled;
2543 break;
2544
2545 case HWVirtExPropertyType_NestedPaging:
2546 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2547 break;
2548
2549 case HWVirtExPropertyType_UnrestrictedExecution:
2550 *aVal = mHWData->mHWVirtExUXEnabled;
2551 break;
2552
2553 case HWVirtExPropertyType_LargePages:
2554 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2555#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2556 *aVal = FALSE;
2557#endif
2558 break;
2559
2560 case HWVirtExPropertyType_Force:
2561 *aVal = mHWData->mHWVirtExForceEnabled;
2562 break;
2563
2564 default:
2565 return E_INVALIDARG;
2566 }
2567 return S_OK;
2568}
2569
2570STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2571{
2572 AutoCaller autoCaller(this);
2573 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2574
2575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 HRESULT rc = checkStateDependency(MutableStateDep);
2578 if (FAILED(rc)) return rc;
2579
2580 switch(property)
2581 {
2582 case HWVirtExPropertyType_Enabled:
2583 setModified(IsModified_MachineData);
2584 mHWData.backup();
2585 mHWData->mHWVirtExEnabled = !!aVal;
2586 break;
2587
2588 case HWVirtExPropertyType_VPID:
2589 setModified(IsModified_MachineData);
2590 mHWData.backup();
2591 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2592 break;
2593
2594 case HWVirtExPropertyType_NestedPaging:
2595 setModified(IsModified_MachineData);
2596 mHWData.backup();
2597 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2598 break;
2599
2600 case HWVirtExPropertyType_UnrestrictedExecution:
2601 setModified(IsModified_MachineData);
2602 mHWData.backup();
2603 mHWData->mHWVirtExUXEnabled = !!aVal;
2604 break;
2605
2606 case HWVirtExPropertyType_LargePages:
2607 setModified(IsModified_MachineData);
2608 mHWData.backup();
2609 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2610 break;
2611
2612 case HWVirtExPropertyType_Force:
2613 setModified(IsModified_MachineData);
2614 mHWData.backup();
2615 mHWData->mHWVirtExForceEnabled = !!aVal;
2616 break;
2617
2618 default:
2619 return E_INVALIDARG;
2620 }
2621
2622 return S_OK;
2623}
2624
2625STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2626{
2627 CheckComArgOutPointerValid(aSnapshotFolder);
2628
2629 AutoCaller autoCaller(this);
2630 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2631
2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 Utf8Str strFullSnapshotFolder;
2635 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2636 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2637
2638 return S_OK;
2639}
2640
2641STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2642{
2643 /* @todo (r=dmik):
2644 * 1. Allow to change the name of the snapshot folder containing snapshots
2645 * 2. Rename the folder on disk instead of just changing the property
2646 * value (to be smart and not to leave garbage). Note that it cannot be
2647 * done here because the change may be rolled back. Thus, the right
2648 * place is #saveSettings().
2649 */
2650
2651 AutoCaller autoCaller(this);
2652 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2653
2654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2655
2656 HRESULT rc = checkStateDependency(MutableStateDep);
2657 if (FAILED(rc)) return rc;
2658
2659 if (!mData->mCurrentSnapshot.isNull())
2660 return setError(E_FAIL,
2661 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2662
2663 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2664
2665 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2666 if (strSnapshotFolder.isEmpty())
2667 strSnapshotFolder = "Snapshots";
2668 int vrc = calculateFullPath(strSnapshotFolder,
2669 strSnapshotFolder);
2670 if (RT_FAILURE(vrc))
2671 return setError(E_FAIL,
2672 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2673 aSnapshotFolder, vrc);
2674
2675 setModified(IsModified_MachineData);
2676 mUserData.backup();
2677
2678 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2679
2680 return S_OK;
2681}
2682
2683STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2684{
2685 CheckComArgOutSafeArrayPointerValid(aAttachments);
2686
2687 AutoCaller autoCaller(this);
2688 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2689
2690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2691
2692 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2693 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2694
2695 return S_OK;
2696}
2697
2698STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2699{
2700 CheckComArgOutPointerValid(vrdeServer);
2701
2702 AutoCaller autoCaller(this);
2703 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2704
2705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2706
2707 Assert(!!mVRDEServer);
2708 mVRDEServer.queryInterfaceTo(vrdeServer);
2709
2710 return S_OK;
2711}
2712
2713STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2714{
2715 CheckComArgOutPointerValid(audioAdapter);
2716
2717 AutoCaller autoCaller(this);
2718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2719
2720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2721
2722 mAudioAdapter.queryInterfaceTo(audioAdapter);
2723 return S_OK;
2724}
2725
2726STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers))
2727{
2728#ifdef VBOX_WITH_VUSB
2729 CheckComArgOutPointerValid(aUSBControllers);
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()->checkUSBProxyService();
2739 if (FAILED(rc)) return rc;
2740# endif
2741
2742 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2743
2744 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data());
2745 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers));
2746 return S_OK;
2747#else
2748 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2749 * extended error info to indicate that USB is simply not available
2750 * (w/o treating it as a failure), for example, as in OSE */
2751 NOREF(aUSBControllers);
2752 ReturnComNotImplemented();
2753#endif /* VBOX_WITH_VUSB */
2754}
2755
2756STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2757{
2758#ifdef VBOX_WITH_VUSB
2759 CheckComArgOutPointerValid(aUSBDeviceFilters);
2760
2761 AutoCaller autoCaller(this);
2762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2763
2764 clearError();
2765 MultiResult rc(S_OK);
2766
2767# ifdef VBOX_WITH_USB
2768 rc = mParent->host()->checkUSBProxyService();
2769 if (FAILED(rc)) return rc;
2770# endif
2771
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2775#else
2776 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2777 * extended error info to indicate that USB is simply not available
2778 * (w/o treating it as a failure), for example, as in OSE */
2779 NOREF(aUSBDeviceFilters);
2780 ReturnComNotImplemented();
2781#endif /* VBOX_WITH_VUSB */
2782}
2783
2784STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2785{
2786 CheckComArgOutPointerValid(aFilePath);
2787
2788 AutoLimitedCaller autoCaller(this);
2789 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2790
2791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2792
2793 mData->m_strConfigFileFull.cloneTo(aFilePath);
2794 return S_OK;
2795}
2796
2797STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2798{
2799 CheckComArgOutPointerValid(aModified);
2800
2801 AutoCaller autoCaller(this);
2802 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2803
2804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 HRESULT rc = checkStateDependency(MutableStateDep);
2807 if (FAILED(rc)) return rc;
2808
2809 if (!mData->pMachineConfigFile->fileExists())
2810 // this is a new machine, and no config file exists yet:
2811 *aModified = TRUE;
2812 else
2813 *aModified = (mData->flModifications != 0);
2814
2815 return S_OK;
2816}
2817
2818STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2819{
2820 CheckComArgOutPointerValid(aSessionState);
2821
2822 AutoCaller autoCaller(this);
2823 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2824
2825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2826
2827 *aSessionState = mData->mSession.mState;
2828
2829 return S_OK;
2830}
2831
2832STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2833{
2834 CheckComArgOutPointerValid(aSessionType);
2835
2836 AutoCaller autoCaller(this);
2837 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2838
2839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2840
2841 mData->mSession.mType.cloneTo(aSessionType);
2842
2843 return S_OK;
2844}
2845
2846STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2847{
2848 CheckComArgOutPointerValid(aSessionPID);
2849
2850 AutoCaller autoCaller(this);
2851 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2852
2853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2854
2855 *aSessionPID = mData->mSession.mPID;
2856
2857 return S_OK;
2858}
2859
2860STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2861{
2862 CheckComArgOutPointerValid(machineState);
2863
2864 AutoCaller autoCaller(this);
2865 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2866
2867 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2868
2869 *machineState = mData->mMachineState;
2870
2871 return S_OK;
2872}
2873
2874STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2875{
2876 CheckComArgOutPointerValid(aLastStateChange);
2877
2878 AutoCaller autoCaller(this);
2879 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2880
2881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2882
2883 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2884
2885 return S_OK;
2886}
2887
2888STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2889{
2890 CheckComArgOutPointerValid(aStateFilePath);
2891
2892 AutoCaller autoCaller(this);
2893 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2894
2895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2896
2897 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2898
2899 return S_OK;
2900}
2901
2902STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2903{
2904 CheckComArgOutPointerValid(aLogFolder);
2905
2906 AutoCaller autoCaller(this);
2907 AssertComRCReturnRC(autoCaller.rc());
2908
2909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2910
2911 Utf8Str logFolder;
2912 getLogFolder(logFolder);
2913 logFolder.cloneTo(aLogFolder);
2914
2915 return S_OK;
2916}
2917
2918STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2919{
2920 CheckComArgOutPointerValid(aCurrentSnapshot);
2921
2922 AutoCaller autoCaller(this);
2923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2924
2925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2926
2927 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2928
2929 return S_OK;
2930}
2931
2932STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2933{
2934 CheckComArgOutPointerValid(aSnapshotCount);
2935
2936 AutoCaller autoCaller(this);
2937 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2938
2939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2940
2941 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2942 ? 0
2943 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2944
2945 return S_OK;
2946}
2947
2948STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2949{
2950 CheckComArgOutPointerValid(aCurrentStateModified);
2951
2952 AutoCaller autoCaller(this);
2953 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2954
2955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2956
2957 /* Note: for machines with no snapshots, we always return FALSE
2958 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2959 * reasons :) */
2960
2961 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2962 ? FALSE
2963 : mData->mCurrentStateModified;
2964
2965 return S_OK;
2966}
2967
2968STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2969{
2970 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2971
2972 AutoCaller autoCaller(this);
2973 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2974
2975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2976
2977 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2978 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2979
2980 return S_OK;
2981}
2982
2983STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2984{
2985 CheckComArgOutPointerValid(aClipboardMode);
2986
2987 AutoCaller autoCaller(this);
2988 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2989
2990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2991
2992 *aClipboardMode = mHWData->mClipboardMode;
2993
2994 return S_OK;
2995}
2996
2997STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2998{
2999 HRESULT rc = S_OK;
3000
3001 AutoCaller autoCaller(this);
3002 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3003
3004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3005
3006 alock.release();
3007 rc = onClipboardModeChange(aClipboardMode);
3008 alock.acquire();
3009 if (FAILED(rc)) return rc;
3010
3011 setModified(IsModified_MachineData);
3012 mHWData.backup();
3013 mHWData->mClipboardMode = aClipboardMode;
3014
3015 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3016 if (Global::IsOnline(mData->mMachineState))
3017 saveSettings(NULL);
3018
3019 return S_OK;
3020}
3021
3022STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
3023{
3024 CheckComArgOutPointerValid(aDragAndDropMode);
3025
3026 AutoCaller autoCaller(this);
3027 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3028
3029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3030
3031 *aDragAndDropMode = mHWData->mDragAndDropMode;
3032
3033 return S_OK;
3034}
3035
3036STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3037{
3038 HRESULT rc = S_OK;
3039
3040 AutoCaller autoCaller(this);
3041 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3042
3043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3044
3045 alock.release();
3046 rc = onDragAndDropModeChange(aDragAndDropMode);
3047 alock.acquire();
3048 if (FAILED(rc)) return rc;
3049
3050 setModified(IsModified_MachineData);
3051 mHWData.backup();
3052 mHWData->mDragAndDropMode = aDragAndDropMode;
3053
3054 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3055 if (Global::IsOnline(mData->mMachineState))
3056 saveSettings(NULL);
3057
3058 return S_OK;
3059}
3060
3061STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3062{
3063 CheckComArgOutPointerValid(aPatterns);
3064
3065 AutoCaller autoCaller(this);
3066 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3067
3068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3069
3070 try
3071 {
3072 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3073 }
3074 catch (...)
3075 {
3076 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3077 }
3078
3079 return S_OK;
3080}
3081
3082STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3083{
3084 AutoCaller autoCaller(this);
3085 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3086
3087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3088
3089 HRESULT rc = checkStateDependency(MutableStateDep);
3090 if (FAILED(rc)) return rc;
3091
3092 setModified(IsModified_MachineData);
3093 mHWData.backup();
3094 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3095 return rc;
3096}
3097
3098STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3099{
3100 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3101
3102 AutoCaller autoCaller(this);
3103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3104
3105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3106
3107 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3108 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3109
3110 return S_OK;
3111}
3112
3113STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3114{
3115 CheckComArgOutPointerValid(aEnabled);
3116
3117 AutoCaller autoCaller(this);
3118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3119
3120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3121
3122 *aEnabled = mUserData->s.fTeleporterEnabled;
3123
3124 return S_OK;
3125}
3126
3127STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3128{
3129 AutoCaller autoCaller(this);
3130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3131
3132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3133
3134 /* Only allow it to be set to true when PoweredOff or Aborted.
3135 (Clearing it is always permitted.) */
3136 if ( aEnabled
3137 && mData->mRegistered
3138 && ( !isSessionMachine()
3139 || ( mData->mMachineState != MachineState_PoweredOff
3140 && mData->mMachineState != MachineState_Teleported
3141 && mData->mMachineState != MachineState_Aborted
3142 )
3143 )
3144 )
3145 return setError(VBOX_E_INVALID_VM_STATE,
3146 tr("The machine is not powered off (state is %s)"),
3147 Global::stringifyMachineState(mData->mMachineState));
3148
3149 setModified(IsModified_MachineData);
3150 mUserData.backup();
3151 mUserData->s.fTeleporterEnabled = !!aEnabled;
3152
3153 return S_OK;
3154}
3155
3156STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3157{
3158 CheckComArgOutPointerValid(aPort);
3159
3160 AutoCaller autoCaller(this);
3161 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3162
3163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3164
3165 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3166
3167 return S_OK;
3168}
3169
3170STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3171{
3172 if (aPort >= _64K)
3173 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3174
3175 AutoCaller autoCaller(this);
3176 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3177
3178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3179
3180 HRESULT rc = checkStateDependency(MutableStateDep);
3181 if (FAILED(rc)) return rc;
3182
3183 setModified(IsModified_MachineData);
3184 mUserData.backup();
3185 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3186
3187 return S_OK;
3188}
3189
3190STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3191{
3192 CheckComArgOutPointerValid(aAddress);
3193
3194 AutoCaller autoCaller(this);
3195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3196
3197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3198
3199 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3200
3201 return S_OK;
3202}
3203
3204STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3205{
3206 AutoCaller autoCaller(this);
3207 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3208
3209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3210
3211 HRESULT rc = checkStateDependency(MutableStateDep);
3212 if (FAILED(rc)) return rc;
3213
3214 setModified(IsModified_MachineData);
3215 mUserData.backup();
3216 mUserData->s.strTeleporterAddress = aAddress;
3217
3218 return S_OK;
3219}
3220
3221STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3222{
3223 CheckComArgOutPointerValid(aPassword);
3224
3225 AutoCaller autoCaller(this);
3226 HRESULT hrc = autoCaller.rc();
3227 if (SUCCEEDED(hrc))
3228 {
3229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3230 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3231 }
3232
3233 return hrc;
3234}
3235
3236STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3237{
3238 /*
3239 * Hash the password first.
3240 */
3241 Utf8Str strPassword(aPassword);
3242 if (!strPassword.isEmpty())
3243 {
3244 if (VBoxIsPasswordHashed(&strPassword))
3245 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3246 VBoxHashPassword(&strPassword);
3247 }
3248
3249 /*
3250 * Do the update.
3251 */
3252 AutoCaller autoCaller(this);
3253 HRESULT hrc = autoCaller.rc();
3254 if (SUCCEEDED(hrc))
3255 {
3256 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3257 hrc = checkStateDependency(MutableStateDep);
3258 if (SUCCEEDED(hrc))
3259 {
3260 setModified(IsModified_MachineData);
3261 mUserData.backup();
3262 mUserData->s.strTeleporterPassword = strPassword;
3263 }
3264 }
3265
3266 return hrc;
3267}
3268
3269STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3270{
3271 CheckComArgOutPointerValid(aState);
3272
3273 AutoCaller autoCaller(this);
3274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3275
3276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3277
3278 *aState = mUserData->s.enmFaultToleranceState;
3279 return S_OK;
3280}
3281
3282STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
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.enmFaultToleranceState = aState;
3296 return S_OK;
3297}
3298
3299STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3300{
3301 CheckComArgOutPointerValid(aAddress);
3302
3303 AutoCaller autoCaller(this);
3304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3305
3306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3307
3308 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3309 return S_OK;
3310}
3311
3312STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
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.strFaultToleranceAddress = aAddress;
3326 return S_OK;
3327}
3328
3329STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3330{
3331 CheckComArgOutPointerValid(aPort);
3332
3333 AutoCaller autoCaller(this);
3334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3335
3336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3337
3338 *aPort = mUserData->s.uFaultTolerancePort;
3339 return S_OK;
3340}
3341
3342STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3343{
3344 AutoCaller autoCaller(this);
3345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3346
3347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3348
3349 /* @todo deal with running state change. */
3350 HRESULT rc = checkStateDependency(MutableStateDep);
3351 if (FAILED(rc)) return rc;
3352
3353 setModified(IsModified_MachineData);
3354 mUserData.backup();
3355 mUserData->s.uFaultTolerancePort = aPort;
3356 return S_OK;
3357}
3358
3359STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3360{
3361 CheckComArgOutPointerValid(aPassword);
3362
3363 AutoCaller autoCaller(this);
3364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3365
3366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3367
3368 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3369
3370 return S_OK;
3371}
3372
3373STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3374{
3375 AutoCaller autoCaller(this);
3376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3377
3378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3379
3380 /* @todo deal with running state change. */
3381 HRESULT rc = checkStateDependency(MutableStateDep);
3382 if (FAILED(rc)) return rc;
3383
3384 setModified(IsModified_MachineData);
3385 mUserData.backup();
3386 mUserData->s.strFaultTolerancePassword = aPassword;
3387
3388 return S_OK;
3389}
3390
3391STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3392{
3393 CheckComArgOutPointerValid(aInterval);
3394
3395 AutoCaller autoCaller(this);
3396 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3397
3398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3399
3400 *aInterval = mUserData->s.uFaultToleranceInterval;
3401 return S_OK;
3402}
3403
3404STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3405{
3406 AutoCaller autoCaller(this);
3407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3408
3409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3410
3411 /* @todo deal with running state change. */
3412 HRESULT rc = checkStateDependency(MutableStateDep);
3413 if (FAILED(rc)) return rc;
3414
3415 setModified(IsModified_MachineData);
3416 mUserData.backup();
3417 mUserData->s.uFaultToleranceInterval = aInterval;
3418 return S_OK;
3419}
3420
3421STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3422{
3423 CheckComArgOutPointerValid(aEnabled);
3424
3425 AutoCaller autoCaller(this);
3426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3427
3428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3429
3430 *aEnabled = mUserData->s.fRTCUseUTC;
3431
3432 return S_OK;
3433}
3434
3435STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3436{
3437 AutoCaller autoCaller(this);
3438 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3439
3440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3441
3442 /* Only allow it to be set to true when PoweredOff or Aborted.
3443 (Clearing it is always permitted.) */
3444 if ( aEnabled
3445 && mData->mRegistered
3446 && ( !isSessionMachine()
3447 || ( mData->mMachineState != MachineState_PoweredOff
3448 && mData->mMachineState != MachineState_Teleported
3449 && mData->mMachineState != MachineState_Aborted
3450 )
3451 )
3452 )
3453 return setError(VBOX_E_INVALID_VM_STATE,
3454 tr("The machine is not powered off (state is %s)"),
3455 Global::stringifyMachineState(mData->mMachineState));
3456
3457 setModified(IsModified_MachineData);
3458 mUserData.backup();
3459 mUserData->s.fRTCUseUTC = !!aEnabled;
3460
3461 return S_OK;
3462}
3463
3464STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3465{
3466 CheckComArgOutPointerValid(aEnabled);
3467
3468 AutoCaller autoCaller(this);
3469 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3470
3471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3472
3473 *aEnabled = mHWData->mIOCacheEnabled;
3474
3475 return S_OK;
3476}
3477
3478STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3479{
3480 AutoCaller autoCaller(this);
3481 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3482
3483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3484
3485 HRESULT rc = checkStateDependency(MutableStateDep);
3486 if (FAILED(rc)) return rc;
3487
3488 setModified(IsModified_MachineData);
3489 mHWData.backup();
3490 mHWData->mIOCacheEnabled = aEnabled;
3491
3492 return S_OK;
3493}
3494
3495STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3496{
3497 CheckComArgOutPointerValid(aIOCacheSize);
3498
3499 AutoCaller autoCaller(this);
3500 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3501
3502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3503
3504 *aIOCacheSize = mHWData->mIOCacheSize;
3505
3506 return S_OK;
3507}
3508
3509STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3510{
3511 AutoCaller autoCaller(this);
3512 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3513
3514 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3515
3516 HRESULT rc = checkStateDependency(MutableStateDep);
3517 if (FAILED(rc)) return rc;
3518
3519 setModified(IsModified_MachineData);
3520 mHWData.backup();
3521 mHWData->mIOCacheSize = aIOCacheSize;
3522
3523 return S_OK;
3524}
3525
3526
3527/**
3528 * @note Locks objects!
3529 */
3530STDMETHODIMP Machine::LockMachine(ISession *aSession,
3531 LockType_T lockType)
3532{
3533 CheckComArgNotNull(aSession);
3534
3535 AutoCaller autoCaller(this);
3536 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3537
3538 /* check the session state */
3539 SessionState_T state;
3540 HRESULT rc = aSession->COMGETTER(State)(&state);
3541 if (FAILED(rc)) return rc;
3542
3543 if (state != SessionState_Unlocked)
3544 return setError(VBOX_E_INVALID_OBJECT_STATE,
3545 tr("The given session is busy"));
3546
3547 // get the client's IInternalSessionControl interface
3548 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3549 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3550 E_INVALIDARG);
3551
3552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3553
3554 if (!mData->mRegistered)
3555 return setError(E_UNEXPECTED,
3556 tr("The machine '%s' is not registered"),
3557 mUserData->s.strName.c_str());
3558
3559 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3560
3561 SessionState_T oldState = mData->mSession.mState;
3562 /* Hack: in case the session is closing and there is a progress object
3563 * which allows waiting for the session to be closed, take the opportunity
3564 * and do a limited wait (max. 1 second). This helps a lot when the system
3565 * is busy and thus session closing can take a little while. */
3566 if ( mData->mSession.mState == SessionState_Unlocking
3567 && mData->mSession.mProgress)
3568 {
3569 alock.release();
3570 mData->mSession.mProgress->WaitForCompletion(1000);
3571 alock.acquire();
3572 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3573 }
3574
3575 // try again now
3576 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3577 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3578 )
3579 {
3580 // OK, share the session... we are now dealing with three processes:
3581 // 1) VBoxSVC (where this code runs);
3582 // 2) process C: the caller's client process (who wants a shared session);
3583 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3584
3585 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3586 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3587 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3588 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3589 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3590
3591 /*
3592 * Release the lock before calling the client process. It's safe here
3593 * since the only thing to do after we get the lock again is to add
3594 * the remote control to the list (which doesn't directly influence
3595 * anything).
3596 */
3597 alock.release();
3598
3599 // get the console of the session holding the write lock (this is a remote call)
3600 ComPtr<IConsole> pConsoleW;
3601 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3602 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3603 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3604 if (FAILED(rc))
3605 // the failure may occur w/o any error info (from RPC), so provide one
3606 return setError(VBOX_E_VM_ERROR,
3607 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3608
3609 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3610
3611 // share the session machine and W's console with the caller's session
3612 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3613 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3614 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3615
3616 if (FAILED(rc))
3617 // the failure may occur w/o any error info (from RPC), so provide one
3618 return setError(VBOX_E_VM_ERROR,
3619 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3620 alock.acquire();
3621
3622 // need to revalidate the state after acquiring the lock again
3623 if (mData->mSession.mState != SessionState_Locked)
3624 {
3625 pSessionControl->Uninitialize();
3626 return setError(VBOX_E_INVALID_SESSION_STATE,
3627 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3628 mUserData->s.strName.c_str());
3629 }
3630
3631 // add the caller's session to the list
3632 mData->mSession.mRemoteControls.push_back(pSessionControl);
3633 }
3634 else if ( mData->mSession.mState == SessionState_Locked
3635 || mData->mSession.mState == SessionState_Unlocking
3636 )
3637 {
3638 // sharing not permitted, or machine still unlocking:
3639 return setError(VBOX_E_INVALID_OBJECT_STATE,
3640 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3641 mUserData->s.strName.c_str());
3642 }
3643 else
3644 {
3645 // machine is not locked: then write-lock the machine (create the session machine)
3646
3647 // must not be busy
3648 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3649
3650 // get the caller's session PID
3651 RTPROCESS pid = NIL_RTPROCESS;
3652 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3653 pSessionControl->GetPID((ULONG*)&pid);
3654 Assert(pid != NIL_RTPROCESS);
3655
3656 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3657
3658 if (fLaunchingVMProcess)
3659 {
3660 // this machine is awaiting for a spawning session to be opened:
3661 // then the calling process must be the one that got started by
3662 // LaunchVMProcess()
3663
3664 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3665 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3666
3667 if (mData->mSession.mPID != pid)
3668 return setError(E_ACCESSDENIED,
3669 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3670 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3671 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3672 }
3673
3674 // create the mutable SessionMachine from the current machine
3675 ComObjPtr<SessionMachine> sessionMachine;
3676 sessionMachine.createObject();
3677 rc = sessionMachine->init(this);
3678 AssertComRC(rc);
3679
3680 /* NOTE: doing return from this function after this point but
3681 * before the end is forbidden since it may call SessionMachine::uninit()
3682 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3683 * lock while still holding the Machine lock in alock so that a deadlock
3684 * is possible due to the wrong lock order. */
3685
3686 if (SUCCEEDED(rc))
3687 {
3688 /*
3689 * Set the session state to Spawning to protect against subsequent
3690 * attempts to open a session and to unregister the machine after
3691 * we release the lock.
3692 */
3693 SessionState_T origState = mData->mSession.mState;
3694 mData->mSession.mState = SessionState_Spawning;
3695
3696 /* Get the client token ID to be passed to the client process */
3697 Utf8Str strTokenId;
3698 sessionMachine->getTokenId(strTokenId);
3699 Assert(!strTokenId.isEmpty());
3700
3701 /*
3702 * Release the lock before calling the client process -- it will call
3703 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3704 * because the state is Spawning, so that LaunchVMProcess() and
3705 * LockMachine() calls will fail. This method, called before we
3706 * acquire the lock again, will fail because of the wrong PID.
3707 *
3708 * Note that mData->mSession.mRemoteControls accessed outside
3709 * the lock may not be modified when state is Spawning, so it's safe.
3710 */
3711 alock.release();
3712
3713 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3714 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw());
3715 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3716
3717 /* The failure may occur w/o any error info (from RPC), so provide one */
3718 if (FAILED(rc))
3719 setError(VBOX_E_VM_ERROR,
3720 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3721
3722 if ( SUCCEEDED(rc)
3723 && fLaunchingVMProcess
3724 )
3725 {
3726 /* complete the remote session initialization */
3727
3728 /* get the console from the direct session */
3729 ComPtr<IConsole> console;
3730 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3731 ComAssertComRC(rc);
3732
3733 if (SUCCEEDED(rc) && !console)
3734 {
3735 ComAssert(!!console);
3736 rc = E_FAIL;
3737 }
3738
3739 /* assign machine & console to the remote session */
3740 if (SUCCEEDED(rc))
3741 {
3742 /*
3743 * after LaunchVMProcess(), the first and the only
3744 * entry in remoteControls is that remote session
3745 */
3746 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3747 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3748 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3749
3750 /* The failure may occur w/o any error info (from RPC), so provide one */
3751 if (FAILED(rc))
3752 setError(VBOX_E_VM_ERROR,
3753 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3754 }
3755
3756 if (FAILED(rc))
3757 pSessionControl->Uninitialize();
3758 }
3759
3760 /* acquire the lock again */
3761 alock.acquire();
3762
3763 /* Restore the session state */
3764 mData->mSession.mState = origState;
3765 }
3766
3767 // finalize spawning anyway (this is why we don't return on errors above)
3768 if (fLaunchingVMProcess)
3769 {
3770 /* Note that the progress object is finalized later */
3771 /** @todo Consider checking mData->mSession.mProgress for cancellation
3772 * around here. */
3773
3774 /* We don't reset mSession.mPID here because it is necessary for
3775 * SessionMachine::uninit() to reap the child process later. */
3776
3777 if (FAILED(rc))
3778 {
3779 /* Close the remote session, remove the remote control from the list
3780 * and reset session state to Closed (@note keep the code in sync
3781 * with the relevant part in checkForSpawnFailure()). */
3782
3783 Assert(mData->mSession.mRemoteControls.size() == 1);
3784 if (mData->mSession.mRemoteControls.size() == 1)
3785 {
3786 ErrorInfoKeeper eik;
3787 mData->mSession.mRemoteControls.front()->Uninitialize();
3788 }
3789
3790 mData->mSession.mRemoteControls.clear();
3791 mData->mSession.mState = SessionState_Unlocked;
3792 }
3793 }
3794 else
3795 {
3796 /* memorize PID of the directly opened session */
3797 if (SUCCEEDED(rc))
3798 mData->mSession.mPID = pid;
3799 }
3800
3801 if (SUCCEEDED(rc))
3802 {
3803 /* memorize the direct session control and cache IUnknown for it */
3804 mData->mSession.mDirectControl = pSessionControl;
3805 mData->mSession.mState = SessionState_Locked;
3806 /* associate the SessionMachine with this Machine */
3807 mData->mSession.mMachine = sessionMachine;
3808
3809 /* request an IUnknown pointer early from the remote party for later
3810 * identity checks (it will be internally cached within mDirectControl
3811 * at least on XPCOM) */
3812 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3813 NOREF(unk);
3814 }
3815
3816 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3817 * would break the lock order */
3818 alock.release();
3819
3820 /* uninitialize the created session machine on failure */
3821 if (FAILED(rc))
3822 sessionMachine->uninit();
3823
3824 }
3825
3826 if (SUCCEEDED(rc))
3827 {
3828 /*
3829 * tell the client watcher thread to update the set of
3830 * machines that have open sessions
3831 */
3832 mParent->updateClientWatcher();
3833
3834 if (oldState != SessionState_Locked)
3835 /* fire an event */
3836 mParent->onSessionStateChange(getId(), SessionState_Locked);
3837 }
3838
3839 return rc;
3840}
3841
3842/**
3843 * @note Locks objects!
3844 */
3845STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3846 IN_BSTR aFrontend,
3847 IN_BSTR aEnvironment,
3848 IProgress **aProgress)
3849{
3850 CheckComArgStr(aFrontend);
3851 Utf8Str strFrontend(aFrontend);
3852 Utf8Str strEnvironment(aEnvironment);
3853 /* "emergencystop" doesn't need the session, so skip the checks/interface
3854 * retrieval. This code doesn't quite fit in here, but introducing a
3855 * special API method would be even more effort, and would require explicit
3856 * support by every API client. It's better to hide the feature a bit. */
3857 if (strFrontend != "emergencystop")
3858 CheckComArgNotNull(aSession);
3859 CheckComArgOutPointerValid(aProgress);
3860
3861 AutoCaller autoCaller(this);
3862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3863
3864 HRESULT rc = S_OK;
3865 if (strFrontend.isEmpty())
3866 {
3867 Bstr bstrFrontend;
3868 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3869 if (FAILED(rc))
3870 return rc;
3871 strFrontend = bstrFrontend;
3872 if (strFrontend.isEmpty())
3873 {
3874 ComPtr<ISystemProperties> systemProperties;
3875 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3876 if (FAILED(rc))
3877 return rc;
3878 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3879 if (FAILED(rc))
3880 return rc;
3881 strFrontend = bstrFrontend;
3882 }
3883 /* paranoia - emergencystop is not a valid default */
3884 if (strFrontend == "emergencystop")
3885 strFrontend = Utf8Str::Empty;
3886 }
3887 /* default frontend: Qt GUI */
3888 if (strFrontend.isEmpty())
3889 strFrontend = "GUI/Qt";
3890
3891 if (strFrontend != "emergencystop")
3892 {
3893 /* check the session state */
3894 SessionState_T state;
3895 rc = aSession->COMGETTER(State)(&state);
3896 if (FAILED(rc))
3897 return rc;
3898
3899 if (state != SessionState_Unlocked)
3900 return setError(VBOX_E_INVALID_OBJECT_STATE,
3901 tr("The given session is busy"));
3902
3903 /* get the IInternalSessionControl interface */
3904 ComPtr<IInternalSessionControl> control(aSession);
3905 ComAssertMsgRet(!control.isNull(),
3906 ("No IInternalSessionControl interface"),
3907 E_INVALIDARG);
3908
3909 /* get the teleporter enable state for the progress object init. */
3910 BOOL fTeleporterEnabled;
3911 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3912 if (FAILED(rc))
3913 return rc;
3914
3915 /* create a progress object */
3916 ComObjPtr<ProgressProxy> progress;
3917 progress.createObject();
3918 rc = progress->init(mParent,
3919 static_cast<IMachine*>(this),
3920 Bstr(tr("Starting VM")).raw(),
3921 TRUE /* aCancelable */,
3922 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3923 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3924 2 /* uFirstOperationWeight */,
3925 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3926
3927 if (SUCCEEDED(rc))
3928 {
3929 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3930 if (SUCCEEDED(rc))
3931 {
3932 progress.queryInterfaceTo(aProgress);
3933
3934 /* signal the client watcher thread */
3935 mParent->updateClientWatcher();
3936
3937 /* fire an event */
3938 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3939 }
3940 }
3941 }
3942 else
3943 {
3944 /* no progress object - either instant success or failure */
3945 *aProgress = NULL;
3946
3947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3948
3949 if (mData->mSession.mState != SessionState_Locked)
3950 return setError(VBOX_E_INVALID_OBJECT_STATE,
3951 tr("The machine '%s' is not locked by a session"),
3952 mUserData->s.strName.c_str());
3953
3954 /* must have a VM process associated - do not kill normal API clients
3955 * with an open session */
3956 if (!Global::IsOnline(mData->mMachineState))
3957 return setError(VBOX_E_INVALID_OBJECT_STATE,
3958 tr("The machine '%s' does not have a VM process"),
3959 mUserData->s.strName.c_str());
3960
3961 /* forcibly terminate the VM process */
3962 if (mData->mSession.mPID != NIL_RTPROCESS)
3963 RTProcTerminate(mData->mSession.mPID);
3964
3965 /* signal the client watcher thread, as most likely the client has
3966 * been terminated */
3967 mParent->updateClientWatcher();
3968 }
3969
3970 return rc;
3971}
3972
3973STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3974{
3975 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3976 return setError(E_INVALIDARG,
3977 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3978 aPosition, SchemaDefs::MaxBootPosition);
3979
3980 if (aDevice == DeviceType_USB)
3981 return setError(E_NOTIMPL,
3982 tr("Booting from USB device is currently not supported"));
3983
3984 AutoCaller autoCaller(this);
3985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3986
3987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3988
3989 HRESULT rc = checkStateDependency(MutableStateDep);
3990 if (FAILED(rc)) return rc;
3991
3992 setModified(IsModified_MachineData);
3993 mHWData.backup();
3994 mHWData->mBootOrder[aPosition - 1] = aDevice;
3995
3996 return S_OK;
3997}
3998
3999STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
4000{
4001 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
4002 return setError(E_INVALIDARG,
4003 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
4004 aPosition, SchemaDefs::MaxBootPosition);
4005
4006 AutoCaller autoCaller(this);
4007 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4008
4009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4010
4011 *aDevice = mHWData->mBootOrder[aPosition - 1];
4012
4013 return S_OK;
4014}
4015
4016STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4017 LONG aControllerPort,
4018 LONG aDevice,
4019 DeviceType_T aType,
4020 IMedium *aMedium)
4021{
4022 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4023 aControllerName, aControllerPort, aDevice, aType, aMedium));
4024
4025 CheckComArgStrNotEmptyOrNull(aControllerName);
4026
4027 AutoCaller autoCaller(this);
4028 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4029
4030 // request the host lock first, since might be calling Host methods for getting host drives;
4031 // next, protect the media tree all the while we're in here, as well as our member variables
4032 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4033 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4034
4035 HRESULT rc = checkStateDependency(MutableStateDep);
4036 if (FAILED(rc)) return rc;
4037
4038 /// @todo NEWMEDIA implicit machine registration
4039 if (!mData->mRegistered)
4040 return setError(VBOX_E_INVALID_OBJECT_STATE,
4041 tr("Cannot attach storage devices to an unregistered machine"));
4042
4043 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4044
4045 /* Check for an existing controller. */
4046 ComObjPtr<StorageController> ctl;
4047 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4048 if (FAILED(rc)) return rc;
4049
4050 StorageControllerType_T ctrlType;
4051 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4052 if (FAILED(rc))
4053 return setError(E_FAIL,
4054 tr("Could not get type of controller '%ls'"),
4055 aControllerName);
4056
4057 bool fSilent = false;
4058 Utf8Str strReconfig;
4059
4060 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4061 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4062 if ( mData->mMachineState == MachineState_Paused
4063 && strReconfig == "1")
4064 fSilent = true;
4065
4066 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4067 bool fHotplug = false;
4068 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4069 fHotplug = true;
4070
4071 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4072 return setError(VBOX_E_INVALID_VM_STATE,
4073 tr("Controller '%ls' does not support hotplugging"),
4074 aControllerName);
4075
4076 // check that the port and device are not out of range
4077 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4078 if (FAILED(rc)) return rc;
4079
4080 /* check if the device slot is already busy */
4081 MediumAttachment *pAttachTemp;
4082 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4083 aControllerName,
4084 aControllerPort,
4085 aDevice)))
4086 {
4087 Medium *pMedium = pAttachTemp->getMedium();
4088 if (pMedium)
4089 {
4090 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4091 return setError(VBOX_E_OBJECT_IN_USE,
4092 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4093 pMedium->getLocationFull().c_str(),
4094 aControllerPort,
4095 aDevice,
4096 aControllerName);
4097 }
4098 else
4099 return setError(VBOX_E_OBJECT_IN_USE,
4100 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4101 aControllerPort, aDevice, aControllerName);
4102 }
4103
4104 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4105 if (aMedium && medium.isNull())
4106 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4107
4108 AutoCaller mediumCaller(medium);
4109 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4110
4111 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4112
4113 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4114 && !medium.isNull()
4115 )
4116 return setError(VBOX_E_OBJECT_IN_USE,
4117 tr("Medium '%s' is already attached to this virtual machine"),
4118 medium->getLocationFull().c_str());
4119
4120 if (!medium.isNull())
4121 {
4122 MediumType_T mtype = medium->getType();
4123 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4124 // For DVDs it's not written to the config file, so needs no global config
4125 // version bump. For floppies it's a new attribute "type", which is ignored
4126 // by older VirtualBox version, so needs no global config version bump either.
4127 // For hard disks this type is not accepted.
4128 if (mtype == MediumType_MultiAttach)
4129 {
4130 // This type is new with VirtualBox 4.0 and therefore requires settings
4131 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4132 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4133 // two reasons: The medium type is a property of the media registry tree, which
4134 // can reside in the global config file (for pre-4.0 media); we would therefore
4135 // possibly need to bump the global config version. We don't want to do that though
4136 // because that might make downgrading to pre-4.0 impossible.
4137 // As a result, we can only use these two new types if the medium is NOT in the
4138 // global registry:
4139 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4140 if ( medium->isInRegistry(uuidGlobalRegistry)
4141 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4142 )
4143 return setError(VBOX_E_INVALID_OBJECT_STATE,
4144 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4145 "to machines that were created with VirtualBox 4.0 or later"),
4146 medium->getLocationFull().c_str());
4147 }
4148 }
4149
4150 bool fIndirect = false;
4151 if (!medium.isNull())
4152 fIndirect = medium->isReadOnly();
4153 bool associate = true;
4154
4155 do
4156 {
4157 if ( aType == DeviceType_HardDisk
4158 && mMediaData.isBackedUp())
4159 {
4160 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4161
4162 /* check if the medium was attached to the VM before we started
4163 * changing attachments in which case the attachment just needs to
4164 * be restored */
4165 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4166 {
4167 AssertReturn(!fIndirect, E_FAIL);
4168
4169 /* see if it's the same bus/channel/device */
4170 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4171 {
4172 /* the simplest case: restore the whole attachment
4173 * and return, nothing else to do */
4174 mMediaData->mAttachments.push_back(pAttachTemp);
4175
4176 /* Reattach the medium to the VM. */
4177 if (fHotplug || fSilent)
4178 {
4179 mediumLock.release();
4180 treeLock.release();
4181 alock.release();
4182
4183 MediumLockList *pMediumLockList(new MediumLockList());
4184
4185 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4186 true /* fMediumLockWrite */,
4187 NULL,
4188 *pMediumLockList);
4189 alock.acquire();
4190 if (FAILED(rc))
4191 delete pMediumLockList;
4192 else
4193 {
4194 mData->mSession.mLockedMedia.Unlock();
4195 alock.release();
4196 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4197 mData->mSession.mLockedMedia.Lock();
4198 alock.acquire();
4199 }
4200 alock.release();
4201
4202 if (SUCCEEDED(rc))
4203 {
4204 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4205 /* Remove lock list in case of error. */
4206 if (FAILED(rc))
4207 {
4208 mData->mSession.mLockedMedia.Unlock();
4209 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4210 mData->mSession.mLockedMedia.Lock();
4211 }
4212 }
4213 }
4214
4215 return S_OK;
4216 }
4217
4218 /* bus/channel/device differ; we need a new attachment object,
4219 * but don't try to associate it again */
4220 associate = false;
4221 break;
4222 }
4223 }
4224
4225 /* go further only if the attachment is to be indirect */
4226 if (!fIndirect)
4227 break;
4228
4229 /* perform the so called smart attachment logic for indirect
4230 * attachments. Note that smart attachment is only applicable to base
4231 * hard disks. */
4232
4233 if (medium->getParent().isNull())
4234 {
4235 /* first, investigate the backup copy of the current hard disk
4236 * attachments to make it possible to re-attach existing diffs to
4237 * another device slot w/o losing their contents */
4238 if (mMediaData.isBackedUp())
4239 {
4240 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4241
4242 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4243 uint32_t foundLevel = 0;
4244
4245 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4246 it != oldAtts.end();
4247 ++it)
4248 {
4249 uint32_t level = 0;
4250 MediumAttachment *pAttach = *it;
4251 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4252 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4253 if (pMedium.isNull())
4254 continue;
4255
4256 if (pMedium->getBase(&level) == medium)
4257 {
4258 /* skip the hard disk if its currently attached (we
4259 * cannot attach the same hard disk twice) */
4260 if (findAttachment(mMediaData->mAttachments,
4261 pMedium))
4262 continue;
4263
4264 /* matched device, channel and bus (i.e. attached to the
4265 * same place) will win and immediately stop the search;
4266 * otherwise the attachment that has the youngest
4267 * descendant of medium will be used
4268 */
4269 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4270 {
4271 /* the simplest case: restore the whole attachment
4272 * and return, nothing else to do */
4273 mMediaData->mAttachments.push_back(*it);
4274
4275 /* Reattach the medium to the VM. */
4276 if (fHotplug || fSilent)
4277 {
4278 mediumLock.release();
4279 treeLock.release();
4280 alock.release();
4281
4282 MediumLockList *pMediumLockList(new MediumLockList());
4283
4284 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4285 true /* fMediumLockWrite */,
4286 NULL,
4287 *pMediumLockList);
4288 alock.acquire();
4289 if (FAILED(rc))
4290 delete pMediumLockList;
4291 else
4292 {
4293 mData->mSession.mLockedMedia.Unlock();
4294 alock.release();
4295 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4296 mData->mSession.mLockedMedia.Lock();
4297 alock.acquire();
4298 }
4299 alock.release();
4300
4301 if (SUCCEEDED(rc))
4302 {
4303 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4304 /* Remove lock list in case of error. */
4305 if (FAILED(rc))
4306 {
4307 mData->mSession.mLockedMedia.Unlock();
4308 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4309 mData->mSession.mLockedMedia.Lock();
4310 }
4311 }
4312 }
4313
4314 return S_OK;
4315 }
4316 else if ( foundIt == oldAtts.end()
4317 || level > foundLevel /* prefer younger */
4318 )
4319 {
4320 foundIt = it;
4321 foundLevel = level;
4322 }
4323 }
4324 }
4325
4326 if (foundIt != oldAtts.end())
4327 {
4328 /* use the previously attached hard disk */
4329 medium = (*foundIt)->getMedium();
4330 mediumCaller.attach(medium);
4331 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4332 mediumLock.attach(medium);
4333 /* not implicit, doesn't require association with this VM */
4334 fIndirect = false;
4335 associate = false;
4336 /* go right to the MediumAttachment creation */
4337 break;
4338 }
4339 }
4340
4341 /* must give up the medium lock and medium tree lock as below we
4342 * go over snapshots, which needs a lock with higher lock order. */
4343 mediumLock.release();
4344 treeLock.release();
4345
4346 /* then, search through snapshots for the best diff in the given
4347 * hard disk's chain to base the new diff on */
4348
4349 ComObjPtr<Medium> base;
4350 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4351 while (snap)
4352 {
4353 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4354
4355 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4356
4357 MediumAttachment *pAttachFound = NULL;
4358 uint32_t foundLevel = 0;
4359
4360 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4361 it != snapAtts.end();
4362 ++it)
4363 {
4364 MediumAttachment *pAttach = *it;
4365 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4366 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4367 if (pMedium.isNull())
4368 continue;
4369
4370 uint32_t level = 0;
4371 if (pMedium->getBase(&level) == medium)
4372 {
4373 /* matched device, channel and bus (i.e. attached to the
4374 * same place) will win and immediately stop the search;
4375 * otherwise the attachment that has the youngest
4376 * descendant of medium will be used
4377 */
4378 if ( pAttach->getDevice() == aDevice
4379 && pAttach->getPort() == aControllerPort
4380 && pAttach->getControllerName() == aControllerName
4381 )
4382 {
4383 pAttachFound = pAttach;
4384 break;
4385 }
4386 else if ( !pAttachFound
4387 || level > foundLevel /* prefer younger */
4388 )
4389 {
4390 pAttachFound = pAttach;
4391 foundLevel = level;
4392 }
4393 }
4394 }
4395
4396 if (pAttachFound)
4397 {
4398 base = pAttachFound->getMedium();
4399 break;
4400 }
4401
4402 snap = snap->getParent();
4403 }
4404
4405 /* re-lock medium tree and the medium, as we need it below */
4406 treeLock.acquire();
4407 mediumLock.acquire();
4408
4409 /* found a suitable diff, use it as a base */
4410 if (!base.isNull())
4411 {
4412 medium = base;
4413 mediumCaller.attach(medium);
4414 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4415 mediumLock.attach(medium);
4416 }
4417 }
4418
4419 Utf8Str strFullSnapshotFolder;
4420 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4421
4422 ComObjPtr<Medium> diff;
4423 diff.createObject();
4424 // store this diff in the same registry as the parent
4425 Guid uuidRegistryParent;
4426 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4427 {
4428 // parent image has no registry: this can happen if we're attaching a new immutable
4429 // image that has not yet been attached (medium then points to the base and we're
4430 // creating the diff image for the immutable, and the parent is not yet registered);
4431 // put the parent in the machine registry then
4432 mediumLock.release();
4433 treeLock.release();
4434 alock.release();
4435 addMediumToRegistry(medium);
4436 alock.acquire();
4437 treeLock.acquire();
4438 mediumLock.acquire();
4439 medium->getFirstRegistryMachineId(uuidRegistryParent);
4440 }
4441 rc = diff->init(mParent,
4442 medium->getPreferredDiffFormat(),
4443 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4444 uuidRegistryParent);
4445 if (FAILED(rc)) return rc;
4446
4447 /* Apply the normal locking logic to the entire chain. */
4448 MediumLockList *pMediumLockList(new MediumLockList());
4449 mediumLock.release();
4450 treeLock.release();
4451 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4452 true /* fMediumLockWrite */,
4453 medium,
4454 *pMediumLockList);
4455 treeLock.acquire();
4456 mediumLock.acquire();
4457 if (SUCCEEDED(rc))
4458 {
4459 mediumLock.release();
4460 treeLock.release();
4461 rc = pMediumLockList->Lock();
4462 treeLock.acquire();
4463 mediumLock.acquire();
4464 if (FAILED(rc))
4465 setError(rc,
4466 tr("Could not lock medium when creating diff '%s'"),
4467 diff->getLocationFull().c_str());
4468 else
4469 {
4470 /* will release the lock before the potentially lengthy
4471 * operation, so protect with the special state */
4472 MachineState_T oldState = mData->mMachineState;
4473 setMachineState(MachineState_SettingUp);
4474
4475 mediumLock.release();
4476 treeLock.release();
4477 alock.release();
4478
4479 rc = medium->createDiffStorage(diff,
4480 MediumVariant_Standard,
4481 pMediumLockList,
4482 NULL /* aProgress */,
4483 true /* aWait */);
4484
4485 alock.acquire();
4486 treeLock.acquire();
4487 mediumLock.acquire();
4488
4489 setMachineState(oldState);
4490 }
4491 }
4492
4493 /* Unlock the media and free the associated memory. */
4494 delete pMediumLockList;
4495
4496 if (FAILED(rc)) return rc;
4497
4498 /* use the created diff for the actual attachment */
4499 medium = diff;
4500 mediumCaller.attach(medium);
4501 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4502 mediumLock.attach(medium);
4503 }
4504 while (0);
4505
4506 ComObjPtr<MediumAttachment> attachment;
4507 attachment.createObject();
4508 rc = attachment->init(this,
4509 medium,
4510 aControllerName,
4511 aControllerPort,
4512 aDevice,
4513 aType,
4514 fIndirect,
4515 false /* fPassthrough */,
4516 false /* fTempEject */,
4517 false /* fNonRotational */,
4518 false /* fDiscard */,
4519 Utf8Str::Empty);
4520 if (FAILED(rc)) return rc;
4521
4522 if (associate && !medium.isNull())
4523 {
4524 // as the last step, associate the medium to the VM
4525 rc = medium->addBackReference(mData->mUuid);
4526 // here we can fail because of Deleting, or being in process of creating a Diff
4527 if (FAILED(rc)) return rc;
4528
4529 mediumLock.release();
4530 treeLock.release();
4531 alock.release();
4532 addMediumToRegistry(medium);
4533 alock.acquire();
4534 treeLock.acquire();
4535 mediumLock.acquire();
4536 }
4537
4538 /* success: finally remember the attachment */
4539 setModified(IsModified_Storage);
4540 mMediaData.backup();
4541 mMediaData->mAttachments.push_back(attachment);
4542
4543 mediumLock.release();
4544 treeLock.release();
4545 alock.release();
4546
4547 if (fHotplug || fSilent)
4548 {
4549 if (!medium.isNull())
4550 {
4551 MediumLockList *pMediumLockList(new MediumLockList());
4552
4553 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4554 true /* fMediumLockWrite */,
4555 NULL,
4556 *pMediumLockList);
4557 alock.acquire();
4558 if (FAILED(rc))
4559 delete pMediumLockList;
4560 else
4561 {
4562 mData->mSession.mLockedMedia.Unlock();
4563 alock.release();
4564 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4565 mData->mSession.mLockedMedia.Lock();
4566 alock.acquire();
4567 }
4568 alock.release();
4569 }
4570
4571 if (SUCCEEDED(rc))
4572 {
4573 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4574 /* Remove lock list in case of error. */
4575 if (FAILED(rc))
4576 {
4577 mData->mSession.mLockedMedia.Unlock();
4578 mData->mSession.mLockedMedia.Remove(attachment);
4579 mData->mSession.mLockedMedia.Lock();
4580 }
4581 }
4582 }
4583
4584 mParent->saveModifiedRegistries();
4585
4586 return rc;
4587}
4588
4589STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4590 LONG aDevice)
4591{
4592 CheckComArgStrNotEmptyOrNull(aControllerName);
4593
4594 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4595 aControllerName, aControllerPort, aDevice));
4596
4597 AutoCaller autoCaller(this);
4598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4599
4600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4601
4602 HRESULT rc = checkStateDependency(MutableStateDep);
4603 if (FAILED(rc)) return rc;
4604
4605 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4606
4607 /* Check for an existing controller. */
4608 ComObjPtr<StorageController> ctl;
4609 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4610 if (FAILED(rc)) return rc;
4611
4612 StorageControllerType_T ctrlType;
4613 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4614 if (FAILED(rc))
4615 return setError(E_FAIL,
4616 tr("Could not get type of controller '%ls'"),
4617 aControllerName);
4618
4619 bool fSilent = false;
4620 Utf8Str strReconfig;
4621
4622 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4623 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4624 if ( mData->mMachineState == MachineState_Paused
4625 && strReconfig == "1")
4626 fSilent = true;
4627
4628 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4629 bool fHotplug = false;
4630 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4631 fHotplug = true;
4632
4633 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4634 return setError(VBOX_E_INVALID_VM_STATE,
4635 tr("Controller '%ls' does not support hotplugging"),
4636 aControllerName);
4637
4638 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4639 aControllerName,
4640 aControllerPort,
4641 aDevice);
4642 if (!pAttach)
4643 return setError(VBOX_E_OBJECT_NOT_FOUND,
4644 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4645 aDevice, aControllerPort, aControllerName);
4646
4647 /*
4648 * The VM has to detach the device before we delete any implicit diffs.
4649 * If this fails we can roll back without loosing data.
4650 */
4651 if (fHotplug || fSilent)
4652 {
4653 alock.release();
4654 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4655 alock.acquire();
4656 }
4657 if (FAILED(rc)) return rc;
4658
4659 /* If we are here everything went well and we can delete the implicit now. */
4660 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4661
4662 alock.release();
4663
4664 mParent->saveModifiedRegistries();
4665
4666 return rc;
4667}
4668
4669STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4670 LONG aDevice, BOOL aPassthrough)
4671{
4672 CheckComArgStrNotEmptyOrNull(aControllerName);
4673
4674 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4675 aControllerName, aControllerPort, aDevice, aPassthrough));
4676
4677 AutoCaller autoCaller(this);
4678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4679
4680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4681
4682 HRESULT rc = checkStateDependency(MutableStateDep);
4683 if (FAILED(rc)) return rc;
4684
4685 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4686
4687 if (Global::IsOnlineOrTransient(mData->mMachineState))
4688 return setError(VBOX_E_INVALID_VM_STATE,
4689 tr("Invalid machine state: %s"),
4690 Global::stringifyMachineState(mData->mMachineState));
4691
4692 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4693 aControllerName,
4694 aControllerPort,
4695 aDevice);
4696 if (!pAttach)
4697 return setError(VBOX_E_OBJECT_NOT_FOUND,
4698 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4699 aDevice, aControllerPort, aControllerName);
4700
4701
4702 setModified(IsModified_Storage);
4703 mMediaData.backup();
4704
4705 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4706
4707 if (pAttach->getType() != DeviceType_DVD)
4708 return setError(E_INVALIDARG,
4709 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4710 aDevice, aControllerPort, aControllerName);
4711 pAttach->updatePassthrough(!!aPassthrough);
4712
4713 return S_OK;
4714}
4715
4716STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4717 LONG aDevice, BOOL aTemporaryEject)
4718{
4719 CheckComArgStrNotEmptyOrNull(aControllerName);
4720
4721 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4722 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4723
4724 AutoCaller autoCaller(this);
4725 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4726
4727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4728
4729 HRESULT rc = checkStateDependency(MutableStateDep);
4730 if (FAILED(rc)) return rc;
4731
4732 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4733 aControllerName,
4734 aControllerPort,
4735 aDevice);
4736 if (!pAttach)
4737 return setError(VBOX_E_OBJECT_NOT_FOUND,
4738 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4739 aDevice, aControllerPort, aControllerName);
4740
4741
4742 setModified(IsModified_Storage);
4743 mMediaData.backup();
4744
4745 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4746
4747 if (pAttach->getType() != DeviceType_DVD)
4748 return setError(E_INVALIDARG,
4749 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4750 aDevice, aControllerPort, aControllerName);
4751 pAttach->updateTempEject(!!aTemporaryEject);
4752
4753 return S_OK;
4754}
4755
4756STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4757 LONG aDevice, BOOL aNonRotational)
4758{
4759 CheckComArgStrNotEmptyOrNull(aControllerName);
4760
4761 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4762 aControllerName, aControllerPort, aDevice, aNonRotational));
4763
4764 AutoCaller autoCaller(this);
4765 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4766
4767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4768
4769 HRESULT rc = checkStateDependency(MutableStateDep);
4770 if (FAILED(rc)) return rc;
4771
4772 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4773
4774 if (Global::IsOnlineOrTransient(mData->mMachineState))
4775 return setError(VBOX_E_INVALID_VM_STATE,
4776 tr("Invalid machine state: %s"),
4777 Global::stringifyMachineState(mData->mMachineState));
4778
4779 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4780 aControllerName,
4781 aControllerPort,
4782 aDevice);
4783 if (!pAttach)
4784 return setError(VBOX_E_OBJECT_NOT_FOUND,
4785 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4786 aDevice, aControllerPort, aControllerName);
4787
4788
4789 setModified(IsModified_Storage);
4790 mMediaData.backup();
4791
4792 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4793
4794 if (pAttach->getType() != DeviceType_HardDisk)
4795 return setError(E_INVALIDARG,
4796 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"),
4797 aDevice, aControllerPort, aControllerName);
4798 pAttach->updateNonRotational(!!aNonRotational);
4799
4800 return S_OK;
4801}
4802
4803STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4804 LONG aDevice, BOOL aDiscard)
4805{
4806 CheckComArgStrNotEmptyOrNull(aControllerName);
4807
4808 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4809 aControllerName, aControllerPort, aDevice, aDiscard));
4810
4811 AutoCaller autoCaller(this);
4812 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4813
4814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4815
4816 HRESULT rc = checkStateDependency(MutableStateDep);
4817 if (FAILED(rc)) return rc;
4818
4819 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4820
4821 if (Global::IsOnlineOrTransient(mData->mMachineState))
4822 return setError(VBOX_E_INVALID_VM_STATE,
4823 tr("Invalid machine state: %s"),
4824 Global::stringifyMachineState(mData->mMachineState));
4825
4826 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4827 aControllerName,
4828 aControllerPort,
4829 aDevice);
4830 if (!pAttach)
4831 return setError(VBOX_E_OBJECT_NOT_FOUND,
4832 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4833 aDevice, aControllerPort, aControllerName);
4834
4835
4836 setModified(IsModified_Storage);
4837 mMediaData.backup();
4838
4839 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4840
4841 if (pAttach->getType() != DeviceType_HardDisk)
4842 return setError(E_INVALIDARG,
4843 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"),
4844 aDevice, aControllerPort, aControllerName);
4845 pAttach->updateDiscard(!!aDiscard);
4846
4847 return S_OK;
4848}
4849
4850STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4851 LONG aDevice)
4852{
4853 int rc = S_OK;
4854 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4855 aControllerName, aControllerPort, aDevice));
4856
4857 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4858
4859 return rc;
4860}
4861
4862STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4863 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4864{
4865 CheckComArgStrNotEmptyOrNull(aControllerName);
4866
4867 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4868 aControllerName, aControllerPort, aDevice));
4869
4870 AutoCaller autoCaller(this);
4871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4872
4873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4874
4875 HRESULT rc = checkStateDependency(MutableStateDep);
4876 if (FAILED(rc)) return rc;
4877
4878 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4879
4880 if (Global::IsOnlineOrTransient(mData->mMachineState))
4881 return setError(VBOX_E_INVALID_VM_STATE,
4882 tr("Invalid machine state: %s"),
4883 Global::stringifyMachineState(mData->mMachineState));
4884
4885 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4886 aControllerName,
4887 aControllerPort,
4888 aDevice);
4889 if (!pAttach)
4890 return setError(VBOX_E_OBJECT_NOT_FOUND,
4891 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4892 aDevice, aControllerPort, aControllerName);
4893
4894
4895 setModified(IsModified_Storage);
4896 mMediaData.backup();
4897
4898 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4899 if (aBandwidthGroup && group.isNull())
4900 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4901
4902 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4903
4904 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4905 if (strBandwidthGroupOld.isNotEmpty())
4906 {
4907 /* Get the bandwidth group object and release it - this must not fail. */
4908 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4909 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4910 Assert(SUCCEEDED(rc));
4911
4912 pBandwidthGroupOld->release();
4913 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4914 }
4915
4916 if (!group.isNull())
4917 {
4918 group->reference();
4919 pAttach->updateBandwidthGroup(group->getName());
4920 }
4921
4922 return S_OK;
4923}
4924
4925STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4926 LONG aControllerPort,
4927 LONG aDevice,
4928 DeviceType_T aType)
4929{
4930 HRESULT rc = S_OK;
4931
4932 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4933 aControllerName, aControllerPort, aDevice, aType));
4934
4935 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4936
4937 return rc;
4938}
4939
4940
4941
4942STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4943 LONG aControllerPort,
4944 LONG aDevice,
4945 BOOL aForce)
4946{
4947 int rc = S_OK;
4948 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4949 aControllerName, aControllerPort, aForce));
4950
4951 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4952
4953 return rc;
4954}
4955
4956STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4957 LONG aControllerPort,
4958 LONG aDevice,
4959 IMedium *aMedium,
4960 BOOL aForce)
4961{
4962 int rc = S_OK;
4963 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4964 aControllerName, aControllerPort, aDevice, aForce));
4965
4966 CheckComArgStrNotEmptyOrNull(aControllerName);
4967
4968 AutoCaller autoCaller(this);
4969 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4970
4971 // request the host lock first, since might be calling Host methods for getting host drives;
4972 // next, protect the media tree all the while we're in here, as well as our member variables
4973 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4974 this->lockHandle(),
4975 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4976
4977 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4978 aControllerName,
4979 aControllerPort,
4980 aDevice);
4981 if (pAttach.isNull())
4982 return setError(VBOX_E_OBJECT_NOT_FOUND,
4983 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4984 aDevice, aControllerPort, aControllerName);
4985
4986 /* Remember previously mounted medium. The medium before taking the
4987 * backup is not necessarily the same thing. */
4988 ComObjPtr<Medium> oldmedium;
4989 oldmedium = pAttach->getMedium();
4990
4991 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4992 if (aMedium && pMedium.isNull())
4993 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4994
4995 AutoCaller mediumCaller(pMedium);
4996 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4997
4998 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4999 if (pMedium)
5000 {
5001 DeviceType_T mediumType = pAttach->getType();
5002 switch (mediumType)
5003 {
5004 case DeviceType_DVD:
5005 case DeviceType_Floppy:
5006 break;
5007
5008 default:
5009 return setError(VBOX_E_INVALID_OBJECT_STATE,
5010 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5011 aControllerPort,
5012 aDevice,
5013 aControllerName);
5014 }
5015 }
5016
5017 setModified(IsModified_Storage);
5018 mMediaData.backup();
5019
5020 {
5021 // The backup operation makes the pAttach reference point to the
5022 // old settings. Re-get the correct reference.
5023 pAttach = findAttachment(mMediaData->mAttachments,
5024 aControllerName,
5025 aControllerPort,
5026 aDevice);
5027 if (!oldmedium.isNull())
5028 oldmedium->removeBackReference(mData->mUuid);
5029 if (!pMedium.isNull())
5030 {
5031 pMedium->addBackReference(mData->mUuid);
5032
5033 mediumLock.release();
5034 multiLock.release();
5035 addMediumToRegistry(pMedium);
5036 multiLock.acquire();
5037 mediumLock.acquire();
5038 }
5039
5040 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5041 pAttach->updateMedium(pMedium);
5042 }
5043
5044 setModified(IsModified_Storage);
5045
5046 mediumLock.release();
5047 multiLock.release();
5048 rc = onMediumChange(pAttach, aForce);
5049 multiLock.acquire();
5050 mediumLock.acquire();
5051
5052 /* On error roll back this change only. */
5053 if (FAILED(rc))
5054 {
5055 if (!pMedium.isNull())
5056 pMedium->removeBackReference(mData->mUuid);
5057 pAttach = findAttachment(mMediaData->mAttachments,
5058 aControllerName,
5059 aControllerPort,
5060 aDevice);
5061 /* If the attachment is gone in the meantime, bail out. */
5062 if (pAttach.isNull())
5063 return rc;
5064 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5065 if (!oldmedium.isNull())
5066 oldmedium->addBackReference(mData->mUuid);
5067 pAttach->updateMedium(oldmedium);
5068 }
5069
5070 mediumLock.release();
5071 multiLock.release();
5072
5073 mParent->saveModifiedRegistries();
5074
5075 return rc;
5076}
5077
5078STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5079 LONG aControllerPort,
5080 LONG aDevice,
5081 IMedium **aMedium)
5082{
5083 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5084 aControllerName, aControllerPort, aDevice));
5085
5086 CheckComArgStrNotEmptyOrNull(aControllerName);
5087 CheckComArgOutPointerValid(aMedium);
5088
5089 AutoCaller autoCaller(this);
5090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5091
5092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5093
5094 *aMedium = NULL;
5095
5096 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5097 aControllerName,
5098 aControllerPort,
5099 aDevice);
5100 if (pAttach.isNull())
5101 return setError(VBOX_E_OBJECT_NOT_FOUND,
5102 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5103 aDevice, aControllerPort, aControllerName);
5104
5105 pAttach->getMedium().queryInterfaceTo(aMedium);
5106
5107 return S_OK;
5108}
5109
5110STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5111{
5112 CheckComArgOutPointerValid(port);
5113 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5114
5115 AutoCaller autoCaller(this);
5116 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5117
5118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5119
5120 mSerialPorts[slot].queryInterfaceTo(port);
5121
5122 return S_OK;
5123}
5124
5125STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5126{
5127 CheckComArgOutPointerValid(port);
5128 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5129
5130 AutoCaller autoCaller(this);
5131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5132
5133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5134
5135 mParallelPorts[slot].queryInterfaceTo(port);
5136
5137 return S_OK;
5138}
5139
5140STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5141{
5142 CheckComArgOutPointerValid(adapter);
5143 /* Do not assert if slot is out of range, just return the advertised
5144 status. testdriver/vbox.py triggers this in logVmInfo. */
5145 if (slot >= mNetworkAdapters.size())
5146 return setError(E_INVALIDARG,
5147 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5148 slot, mNetworkAdapters.size());
5149
5150 AutoCaller autoCaller(this);
5151 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5152
5153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5154
5155 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5156
5157 return S_OK;
5158}
5159
5160STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5161{
5162 CheckComArgOutSafeArrayPointerValid(aKeys);
5163
5164 AutoCaller autoCaller(this);
5165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5166
5167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5168
5169 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5170 int i = 0;
5171 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5172 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5173 ++it, ++i)
5174 {
5175 const Utf8Str &strKey = it->first;
5176 strKey.cloneTo(&saKeys[i]);
5177 }
5178 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5179
5180 return S_OK;
5181 }
5182
5183 /**
5184 * @note Locks this object for reading.
5185 */
5186STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5187 BSTR *aValue)
5188{
5189 CheckComArgStrNotEmptyOrNull(aKey);
5190 CheckComArgOutPointerValid(aValue);
5191
5192 AutoCaller autoCaller(this);
5193 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5194
5195 /* start with nothing found */
5196 Bstr bstrResult("");
5197
5198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5199
5200 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5201 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5202 // found:
5203 bstrResult = it->second; // source is a Utf8Str
5204
5205 /* return the result to caller (may be empty) */
5206 bstrResult.cloneTo(aValue);
5207
5208 return S_OK;
5209}
5210
5211 /**
5212 * @note Locks mParent for writing + this object for writing.
5213 */
5214STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5215{
5216 CheckComArgStrNotEmptyOrNull(aKey);
5217
5218 AutoCaller autoCaller(this);
5219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5220
5221 Utf8Str strKey(aKey);
5222 Utf8Str strValue(aValue);
5223 Utf8Str strOldValue; // empty
5224
5225 // locking note: we only hold the read lock briefly to look up the old value,
5226 // then release it and call the onExtraCanChange callbacks. There is a small
5227 // chance of a race insofar as the callback might be called twice if two callers
5228 // change the same key at the same time, but that's a much better solution
5229 // than the deadlock we had here before. The actual changing of the extradata
5230 // is then performed under the write lock and race-free.
5231
5232 // look up the old value first; if nothing has changed then we need not do anything
5233 {
5234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5235 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5236 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5237 strOldValue = it->second;
5238 }
5239
5240 bool fChanged;
5241 if ((fChanged = (strOldValue != strValue)))
5242 {
5243 // ask for permission from all listeners outside the locks;
5244 // onExtraDataCanChange() only briefly requests the VirtualBox
5245 // lock to copy the list of callbacks to invoke
5246 Bstr error;
5247 Bstr bstrValue(aValue);
5248
5249 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5250 {
5251 const char *sep = error.isEmpty() ? "" : ": ";
5252 CBSTR err = error.raw();
5253 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5254 sep, err));
5255 return setError(E_ACCESSDENIED,
5256 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5257 aKey,
5258 bstrValue.raw(),
5259 sep,
5260 err);
5261 }
5262
5263 // data is changing and change not vetoed: then write it out under the lock
5264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5265
5266 if (isSnapshotMachine())
5267 {
5268 HRESULT rc = checkStateDependency(MutableStateDep);
5269 if (FAILED(rc)) return rc;
5270 }
5271
5272 if (strValue.isEmpty())
5273 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5274 else
5275 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5276 // creates a new key if needed
5277
5278 bool fNeedsGlobalSaveSettings = false;
5279 saveSettings(&fNeedsGlobalSaveSettings);
5280
5281 if (fNeedsGlobalSaveSettings)
5282 {
5283 // save the global settings; for that we should hold only the VirtualBox lock
5284 alock.release();
5285 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5286 mParent->saveSettings();
5287 }
5288 }
5289
5290 // fire notification outside the lock
5291 if (fChanged)
5292 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5293
5294 return S_OK;
5295}
5296
5297STDMETHODIMP Machine::SaveSettings()
5298{
5299 AutoCaller autoCaller(this);
5300 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5301
5302 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5303
5304 /* when there was auto-conversion, we want to save the file even if
5305 * the VM is saved */
5306 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5307 if (FAILED(rc)) return rc;
5308
5309 /* the settings file path may never be null */
5310 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5311
5312 /* save all VM data excluding snapshots */
5313 bool fNeedsGlobalSaveSettings = false;
5314 rc = saveSettings(&fNeedsGlobalSaveSettings);
5315 mlock.release();
5316
5317 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5318 {
5319 // save the global settings; for that we should hold only the VirtualBox lock
5320 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5321 rc = mParent->saveSettings();
5322 }
5323
5324 return rc;
5325}
5326
5327STDMETHODIMP Machine::DiscardSettings()
5328{
5329 AutoCaller autoCaller(this);
5330 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5331
5332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5333
5334 HRESULT rc = checkStateDependency(MutableStateDep);
5335 if (FAILED(rc)) return rc;
5336
5337 /*
5338 * during this rollback, the session will be notified if data has
5339 * been actually changed
5340 */
5341 rollback(true /* aNotify */);
5342
5343 return S_OK;
5344}
5345
5346/** @note Locks objects! */
5347STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5348 ComSafeArrayOut(IMedium*, aMedia))
5349{
5350 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5351 AutoLimitedCaller autoCaller(this);
5352 AssertComRCReturnRC(autoCaller.rc());
5353
5354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5355
5356 Guid id(getId());
5357
5358 if (mData->mSession.mState != SessionState_Unlocked)
5359 return setError(VBOX_E_INVALID_OBJECT_STATE,
5360 tr("Cannot unregister the machine '%s' while it is locked"),
5361 mUserData->s.strName.c_str());
5362
5363 // wait for state dependents to drop to zero
5364 ensureNoStateDependencies();
5365
5366 if (!mData->mAccessible)
5367 {
5368 // inaccessible maschines can only be unregistered; uninitialize ourselves
5369 // here because currently there may be no unregistered that are inaccessible
5370 // (this state combination is not supported). Note releasing the caller and
5371 // leaving the lock before calling uninit()
5372 alock.release();
5373 autoCaller.release();
5374
5375 uninit();
5376
5377 mParent->unregisterMachine(this, id);
5378 // calls VirtualBox::saveSettings()
5379
5380 return S_OK;
5381 }
5382
5383 HRESULT rc = S_OK;
5384
5385 // discard saved state
5386 if (mData->mMachineState == MachineState_Saved)
5387 {
5388 // add the saved state file to the list of files the caller should delete
5389 Assert(!mSSData->strStateFilePath.isEmpty());
5390 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5391
5392 mSSData->strStateFilePath.setNull();
5393
5394 // unconditionally set the machine state to powered off, we now
5395 // know no session has locked the machine
5396 mData->mMachineState = MachineState_PoweredOff;
5397 }
5398
5399 size_t cSnapshots = 0;
5400 if (mData->mFirstSnapshot)
5401 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5402 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5403 // fail now before we start detaching media
5404 return setError(VBOX_E_INVALID_OBJECT_STATE,
5405 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5406 mUserData->s.strName.c_str(), cSnapshots);
5407
5408 // This list collects the medium objects from all medium attachments
5409 // which we will detach from the machine and its snapshots, in a specific
5410 // order which allows for closing all media without getting "media in use"
5411 // errors, simply by going through the list from the front to the back:
5412 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5413 // and must be closed before the parent media from the snapshots, or closing the parents
5414 // will fail because they still have children);
5415 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5416 // the root ("first") snapshot of the machine.
5417 MediaList llMedia;
5418
5419 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5420 && mMediaData->mAttachments.size()
5421 )
5422 {
5423 // we have media attachments: detach them all and add the Medium objects to our list
5424 if (cleanupMode != CleanupMode_UnregisterOnly)
5425 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5426 else
5427 return setError(VBOX_E_INVALID_OBJECT_STATE,
5428 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5429 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5430 }
5431
5432 if (cSnapshots)
5433 {
5434 // autoCleanup must be true here, or we would have failed above
5435
5436 // add the media from the medium attachments of the snapshots to llMedia
5437 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5438 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5439 // into the children first
5440
5441 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5442 MachineState_T oldState = mData->mMachineState;
5443 mData->mMachineState = MachineState_DeletingSnapshot;
5444
5445 // make a copy of the first snapshot so the refcount does not drop to 0
5446 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5447 // because of the AutoCaller voodoo)
5448 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5449
5450 // GO!
5451 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5452
5453 mData->mMachineState = oldState;
5454 }
5455
5456 if (FAILED(rc))
5457 {
5458 rollbackMedia();
5459 return rc;
5460 }
5461
5462 // commit all the media changes made above
5463 commitMedia();
5464
5465 mData->mRegistered = false;
5466
5467 // machine lock no longer needed
5468 alock.release();
5469
5470 // return media to caller
5471 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5472 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5473
5474 mParent->unregisterMachine(this, id);
5475 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5476
5477 return S_OK;
5478}
5479
5480struct Machine::DeleteTask
5481{
5482 ComObjPtr<Machine> pMachine;
5483 RTCList<ComPtr<IMedium> > llMediums;
5484 StringsList llFilesToDelete;
5485 ComObjPtr<Progress> pProgress;
5486};
5487
5488STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5489{
5490 LogFlowFuncEnter();
5491
5492 AutoCaller autoCaller(this);
5493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5494
5495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5496
5497 HRESULT rc = checkStateDependency(MutableStateDep);
5498 if (FAILED(rc)) return rc;
5499
5500 if (mData->mRegistered)
5501 return setError(VBOX_E_INVALID_VM_STATE,
5502 tr("Cannot delete settings of a registered machine"));
5503
5504 DeleteTask *pTask = new DeleteTask;
5505 pTask->pMachine = this;
5506 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5507
5508 // collect files to delete
5509 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5510
5511 for (size_t i = 0; i < sfaMedia.size(); ++i)
5512 {
5513 IMedium *pIMedium(sfaMedia[i]);
5514 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5515 if (pMedium.isNull())
5516 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5517 SafeArray<BSTR> ids;
5518 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5519 if (FAILED(rc)) return rc;
5520 /* At this point the medium should not have any back references
5521 * anymore. If it has it is attached to another VM and *must* not
5522 * deleted. */
5523 if (ids.size() < 1)
5524 pTask->llMediums.append(pMedium);
5525 }
5526 if (mData->pMachineConfigFile->fileExists())
5527 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5528
5529 pTask->pProgress.createObject();
5530 pTask->pProgress->init(getVirtualBox(),
5531 static_cast<IMachine*>(this) /* aInitiator */,
5532 Bstr(tr("Deleting files")).raw(),
5533 true /* fCancellable */,
5534 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5535 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5536
5537 int vrc = RTThreadCreate(NULL,
5538 Machine::deleteThread,
5539 (void*)pTask,
5540 0,
5541 RTTHREADTYPE_MAIN_WORKER,
5542 0,
5543 "MachineDelete");
5544
5545 pTask->pProgress.queryInterfaceTo(aProgress);
5546
5547 if (RT_FAILURE(vrc))
5548 {
5549 delete pTask;
5550 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5551 }
5552
5553 LogFlowFuncLeave();
5554
5555 return S_OK;
5556}
5557
5558/**
5559 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5560 * calls Machine::deleteTaskWorker() on the actual machine object.
5561 * @param Thread
5562 * @param pvUser
5563 * @return
5564 */
5565/*static*/
5566DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5567{
5568 LogFlowFuncEnter();
5569
5570 DeleteTask *pTask = (DeleteTask*)pvUser;
5571 Assert(pTask);
5572 Assert(pTask->pMachine);
5573 Assert(pTask->pProgress);
5574
5575 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5576 pTask->pProgress->notifyComplete(rc);
5577
5578 delete pTask;
5579
5580 LogFlowFuncLeave();
5581
5582 NOREF(Thread);
5583
5584 return VINF_SUCCESS;
5585}
5586
5587/**
5588 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5589 * @param task
5590 * @return
5591 */
5592HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5593{
5594 AutoCaller autoCaller(this);
5595 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5596
5597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5598
5599 HRESULT rc = S_OK;
5600
5601 try
5602 {
5603 ULONG uLogHistoryCount = 3;
5604 ComPtr<ISystemProperties> systemProperties;
5605 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5606 if (FAILED(rc)) throw rc;
5607
5608 if (!systemProperties.isNull())
5609 {
5610 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5611 if (FAILED(rc)) throw rc;
5612 }
5613
5614 MachineState_T oldState = mData->mMachineState;
5615 setMachineState(MachineState_SettingUp);
5616 alock.release();
5617 for (size_t i = 0; i < task.llMediums.size(); ++i)
5618 {
5619 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5620 {
5621 AutoCaller mac(pMedium);
5622 if (FAILED(mac.rc())) throw mac.rc();
5623 Utf8Str strLocation = pMedium->getLocationFull();
5624 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5625 if (FAILED(rc)) throw rc;
5626 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5627 }
5628 ComPtr<IProgress> pProgress2;
5629 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5630 if (FAILED(rc)) throw rc;
5631 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5632 if (FAILED(rc)) throw rc;
5633 /* Check the result of the asynchrony process. */
5634 LONG iRc;
5635 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5636 if (FAILED(rc)) throw rc;
5637 /* If the thread of the progress object has an error, then
5638 * retrieve the error info from there, or it'll be lost. */
5639 if (FAILED(iRc))
5640 throw setError(ProgressErrorInfo(pProgress2));
5641 }
5642 setMachineState(oldState);
5643 alock.acquire();
5644
5645 // delete the files pushed on the task list by Machine::Delete()
5646 // (this includes saved states of the machine and snapshots and
5647 // medium storage files from the IMedium list passed in, and the
5648 // machine XML file)
5649 StringsList::const_iterator it = task.llFilesToDelete.begin();
5650 while (it != task.llFilesToDelete.end())
5651 {
5652 const Utf8Str &strFile = *it;
5653 LogFunc(("Deleting file %s\n", strFile.c_str()));
5654 int vrc = RTFileDelete(strFile.c_str());
5655 if (RT_FAILURE(vrc))
5656 throw setError(VBOX_E_IPRT_ERROR,
5657 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5658
5659 ++it;
5660 if (it == task.llFilesToDelete.end())
5661 {
5662 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5663 if (FAILED(rc)) throw rc;
5664 break;
5665 }
5666
5667 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5668 if (FAILED(rc)) throw rc;
5669 }
5670
5671 /* delete the settings only when the file actually exists */
5672 if (mData->pMachineConfigFile->fileExists())
5673 {
5674 /* Delete any backup or uncommitted XML files. Ignore failures.
5675 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5676 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5677 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5678 RTFileDelete(otherXml.c_str());
5679 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5680 RTFileDelete(otherXml.c_str());
5681
5682 /* delete the Logs folder, nothing important should be left
5683 * there (we don't check for errors because the user might have
5684 * some private files there that we don't want to delete) */
5685 Utf8Str logFolder;
5686 getLogFolder(logFolder);
5687 Assert(logFolder.length());
5688 if (RTDirExists(logFolder.c_str()))
5689 {
5690 /* Delete all VBox.log[.N] files from the Logs folder
5691 * (this must be in sync with the rotation logic in
5692 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5693 * files that may have been created by the GUI. */
5694 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5695 logFolder.c_str(), RTPATH_DELIMITER);
5696 RTFileDelete(log.c_str());
5697 log = Utf8StrFmt("%s%cVBox.png",
5698 logFolder.c_str(), RTPATH_DELIMITER);
5699 RTFileDelete(log.c_str());
5700 for (int i = uLogHistoryCount; i > 0; i--)
5701 {
5702 log = Utf8StrFmt("%s%cVBox.log.%d",
5703 logFolder.c_str(), RTPATH_DELIMITER, i);
5704 RTFileDelete(log.c_str());
5705 log = Utf8StrFmt("%s%cVBox.png.%d",
5706 logFolder.c_str(), RTPATH_DELIMITER, i);
5707 RTFileDelete(log.c_str());
5708 }
5709
5710 RTDirRemove(logFolder.c_str());
5711 }
5712
5713 /* delete the Snapshots folder, nothing important should be left
5714 * there (we don't check for errors because the user might have
5715 * some private files there that we don't want to delete) */
5716 Utf8Str strFullSnapshotFolder;
5717 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5718 Assert(!strFullSnapshotFolder.isEmpty());
5719 if (RTDirExists(strFullSnapshotFolder.c_str()))
5720 RTDirRemove(strFullSnapshotFolder.c_str());
5721
5722 // delete the directory that contains the settings file, but only
5723 // if it matches the VM name
5724 Utf8Str settingsDir;
5725 if (isInOwnDir(&settingsDir))
5726 RTDirRemove(settingsDir.c_str());
5727 }
5728
5729 alock.release();
5730
5731 mParent->saveModifiedRegistries();
5732 }
5733 catch (HRESULT aRC) { rc = aRC; }
5734
5735 return rc;
5736}
5737
5738STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5739{
5740 CheckComArgOutPointerValid(aSnapshot);
5741
5742 AutoCaller autoCaller(this);
5743 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5744
5745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5746
5747 ComObjPtr<Snapshot> pSnapshot;
5748 HRESULT rc;
5749
5750 if (!aNameOrId || !*aNameOrId)
5751 // null case (caller wants root snapshot): findSnapshotById() handles this
5752 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5753 else
5754 {
5755 Guid uuid(aNameOrId);
5756 if (uuid.isValid())
5757 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5758 else
5759 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5760 }
5761 pSnapshot.queryInterfaceTo(aSnapshot);
5762
5763 return rc;
5764}
5765
5766STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5767{
5768 CheckComArgStrNotEmptyOrNull(aName);
5769 CheckComArgStrNotEmptyOrNull(aHostPath);
5770
5771 AutoCaller autoCaller(this);
5772 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5773
5774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5775
5776 HRESULT rc = checkStateDependency(MutableStateDep);
5777 if (FAILED(rc)) return rc;
5778
5779 Utf8Str strName(aName);
5780
5781 ComObjPtr<SharedFolder> sharedFolder;
5782 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5783 if (SUCCEEDED(rc))
5784 return setError(VBOX_E_OBJECT_IN_USE,
5785 tr("Shared folder named '%s' already exists"),
5786 strName.c_str());
5787
5788 sharedFolder.createObject();
5789 rc = sharedFolder->init(getMachine(),
5790 strName,
5791 aHostPath,
5792 !!aWritable,
5793 !!aAutoMount,
5794 true /* fFailOnError */);
5795 if (FAILED(rc)) return rc;
5796
5797 setModified(IsModified_SharedFolders);
5798 mHWData.backup();
5799 mHWData->mSharedFolders.push_back(sharedFolder);
5800
5801 /* inform the direct session if any */
5802 alock.release();
5803 onSharedFolderChange();
5804
5805 return S_OK;
5806}
5807
5808STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5809{
5810 CheckComArgStrNotEmptyOrNull(aName);
5811
5812 AutoCaller autoCaller(this);
5813 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5814
5815 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5816
5817 HRESULT rc = checkStateDependency(MutableStateDep);
5818 if (FAILED(rc)) return rc;
5819
5820 ComObjPtr<SharedFolder> sharedFolder;
5821 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5822 if (FAILED(rc)) return rc;
5823
5824 setModified(IsModified_SharedFolders);
5825 mHWData.backup();
5826 mHWData->mSharedFolders.remove(sharedFolder);
5827
5828 /* inform the direct session if any */
5829 alock.release();
5830 onSharedFolderChange();
5831
5832 return S_OK;
5833}
5834
5835STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5836{
5837 CheckComArgOutPointerValid(aCanShow);
5838
5839 /* start with No */
5840 *aCanShow = FALSE;
5841
5842 AutoCaller autoCaller(this);
5843 AssertComRCReturnRC(autoCaller.rc());
5844
5845 ComPtr<IInternalSessionControl> directControl;
5846 {
5847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5848
5849 if (mData->mSession.mState != SessionState_Locked)
5850 return setError(VBOX_E_INVALID_VM_STATE,
5851 tr("Machine is not locked for session (session state: %s)"),
5852 Global::stringifySessionState(mData->mSession.mState));
5853
5854 directControl = mData->mSession.mDirectControl;
5855 }
5856
5857 /* ignore calls made after #OnSessionEnd() is called */
5858 if (!directControl)
5859 return S_OK;
5860
5861 LONG64 dummy;
5862 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5863}
5864
5865STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5866{
5867 CheckComArgOutPointerValid(aWinId);
5868
5869 AutoCaller autoCaller(this);
5870 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5871
5872 ComPtr<IInternalSessionControl> directControl;
5873 {
5874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5875
5876 if (mData->mSession.mState != SessionState_Locked)
5877 return setError(E_FAIL,
5878 tr("Machine is not locked for session (session state: %s)"),
5879 Global::stringifySessionState(mData->mSession.mState));
5880
5881 directControl = mData->mSession.mDirectControl;
5882 }
5883
5884 /* ignore calls made after #OnSessionEnd() is called */
5885 if (!directControl)
5886 return S_OK;
5887
5888 BOOL dummy;
5889 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5890}
5891
5892#ifdef VBOX_WITH_GUEST_PROPS
5893/**
5894 * Look up a guest property in VBoxSVC's internal structures.
5895 */
5896HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5897 BSTR *aValue,
5898 LONG64 *aTimestamp,
5899 BSTR *aFlags) const
5900{
5901 using namespace guestProp;
5902
5903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5904 Utf8Str strName(aName);
5905 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5906
5907 if (it != mHWData->mGuestProperties.end())
5908 {
5909 char szFlags[MAX_FLAGS_LEN + 1];
5910 it->second.strValue.cloneTo(aValue);
5911 *aTimestamp = it->second.mTimestamp;
5912 writeFlags(it->second.mFlags, szFlags);
5913 Bstr(szFlags).cloneTo(aFlags);
5914 }
5915
5916 return S_OK;
5917}
5918
5919/**
5920 * Query the VM that a guest property belongs to for the property.
5921 * @returns E_ACCESSDENIED if the VM process is not available or not
5922 * currently handling queries and the lookup should then be done in
5923 * VBoxSVC.
5924 */
5925HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5926 BSTR *aValue,
5927 LONG64 *aTimestamp,
5928 BSTR *aFlags) const
5929{
5930 HRESULT rc;
5931 ComPtr<IInternalSessionControl> directControl;
5932 directControl = mData->mSession.mDirectControl;
5933
5934 /* fail if we were called after #OnSessionEnd() is called. This is a
5935 * silly race condition. */
5936
5937 if (!directControl)
5938 rc = E_ACCESSDENIED;
5939 else
5940 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5941 false /* isSetter */,
5942 aValue, aTimestamp, aFlags);
5943 return rc;
5944}
5945#endif // VBOX_WITH_GUEST_PROPS
5946
5947STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5948 BSTR *aValue,
5949 LONG64 *aTimestamp,
5950 BSTR *aFlags)
5951{
5952#ifndef VBOX_WITH_GUEST_PROPS
5953 ReturnComNotImplemented();
5954#else // VBOX_WITH_GUEST_PROPS
5955 CheckComArgStrNotEmptyOrNull(aName);
5956 CheckComArgOutPointerValid(aValue);
5957 CheckComArgOutPointerValid(aTimestamp);
5958 CheckComArgOutPointerValid(aFlags);
5959
5960 AutoCaller autoCaller(this);
5961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5962
5963 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5964 if (rc == E_ACCESSDENIED)
5965 /* The VM is not running or the service is not (yet) accessible */
5966 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5967 return rc;
5968#endif // VBOX_WITH_GUEST_PROPS
5969}
5970
5971STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5972{
5973 LONG64 dummyTimestamp;
5974 Bstr dummyFlags;
5975 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5976}
5977
5978STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5979{
5980 Bstr dummyValue;
5981 Bstr dummyFlags;
5982 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5983}
5984
5985#ifdef VBOX_WITH_GUEST_PROPS
5986/**
5987 * Set a guest property in VBoxSVC's internal structures.
5988 */
5989HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5990 IN_BSTR aFlags)
5991{
5992 using namespace guestProp;
5993
5994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5995 HRESULT rc = S_OK;
5996
5997 rc = checkStateDependency(MutableStateDep);
5998 if (FAILED(rc)) return rc;
5999
6000 try
6001 {
6002 Utf8Str utf8Name(aName);
6003 Utf8Str utf8Flags(aFlags);
6004 uint32_t fFlags = NILFLAG;
6005 if ( aFlags != NULL
6006 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6007 return setError(E_INVALIDARG,
6008 tr("Invalid guest property flag values: '%ls'"),
6009 aFlags);
6010
6011 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6012 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6013 if (it == mHWData->mGuestProperties.end())
6014 {
6015 if (!fDelete)
6016 {
6017 setModified(IsModified_MachineData);
6018 mHWData.backupEx();
6019
6020 RTTIMESPEC time;
6021 HWData::GuestProperty prop;
6022 prop.strValue = aValue;
6023 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6024 prop.mFlags = fFlags;
6025 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6026 }
6027 }
6028 else
6029 {
6030 if (it->second.mFlags & (RDONLYHOST))
6031 {
6032 rc = setError(E_ACCESSDENIED,
6033 tr("The property '%ls' cannot be changed by the host"),
6034 aName);
6035 }
6036 else
6037 {
6038 setModified(IsModified_MachineData);
6039 mHWData.backupEx();
6040
6041 /* The backupEx() operation invalidates our iterator,
6042 * so get a new one. */
6043 it = mHWData->mGuestProperties.find(utf8Name);
6044 Assert(it != mHWData->mGuestProperties.end());
6045
6046 if (!fDelete)
6047 {
6048 RTTIMESPEC time;
6049 it->second.strValue = aValue;
6050 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6051 it->second.mFlags = fFlags;
6052 }
6053 else
6054 mHWData->mGuestProperties.erase(it);
6055 }
6056 }
6057
6058 if ( SUCCEEDED(rc)
6059 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6060 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6061 RTSTR_MAX,
6062 utf8Name.c_str(),
6063 RTSTR_MAX,
6064 NULL)
6065 )
6066 )
6067 {
6068 alock.release();
6069
6070 mParent->onGuestPropertyChange(mData->mUuid, aName,
6071 aValue ? aValue : Bstr("").raw(),
6072 aFlags ? aFlags : Bstr("").raw());
6073 }
6074 }
6075 catch (std::bad_alloc &)
6076 {
6077 rc = E_OUTOFMEMORY;
6078 }
6079
6080 return rc;
6081}
6082
6083/**
6084 * Set a property on the VM that that property belongs to.
6085 * @returns E_ACCESSDENIED if the VM process is not available or not
6086 * currently handling queries and the setting should then be done in
6087 * VBoxSVC.
6088 */
6089HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6090 IN_BSTR aFlags)
6091{
6092 HRESULT rc;
6093
6094 try
6095 {
6096 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6097
6098 BSTR dummy = NULL; /* will not be changed (setter) */
6099 LONG64 dummy64;
6100 if (!directControl)
6101 rc = E_ACCESSDENIED;
6102 else
6103 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6104 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6105 true /* isSetter */,
6106 &dummy, &dummy64, &dummy);
6107 }
6108 catch (std::bad_alloc &)
6109 {
6110 rc = E_OUTOFMEMORY;
6111 }
6112
6113 return rc;
6114}
6115#endif // VBOX_WITH_GUEST_PROPS
6116
6117STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6118 IN_BSTR aFlags)
6119{
6120#ifndef VBOX_WITH_GUEST_PROPS
6121 ReturnComNotImplemented();
6122#else // VBOX_WITH_GUEST_PROPS
6123 CheckComArgStrNotEmptyOrNull(aName);
6124 CheckComArgMaybeNull(aFlags);
6125 CheckComArgMaybeNull(aValue);
6126
6127 AutoCaller autoCaller(this);
6128 if (FAILED(autoCaller.rc()))
6129 return autoCaller.rc();
6130
6131 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6132 if (rc == E_ACCESSDENIED)
6133 /* The VM is not running or the service is not (yet) accessible */
6134 rc = setGuestPropertyToService(aName, aValue, aFlags);
6135 return rc;
6136#endif // VBOX_WITH_GUEST_PROPS
6137}
6138
6139STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6140{
6141 return SetGuestProperty(aName, aValue, NULL);
6142}
6143
6144STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6145{
6146 return SetGuestProperty(aName, NULL, NULL);
6147}
6148
6149#ifdef VBOX_WITH_GUEST_PROPS
6150/**
6151 * Enumerate the guest properties in VBoxSVC's internal structures.
6152 */
6153HRESULT Machine::enumerateGuestPropertiesInService
6154 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6155 ComSafeArrayOut(BSTR, aValues),
6156 ComSafeArrayOut(LONG64, aTimestamps),
6157 ComSafeArrayOut(BSTR, aFlags))
6158{
6159 using namespace guestProp;
6160
6161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6162 Utf8Str strPatterns(aPatterns);
6163
6164 HWData::GuestPropertyMap propMap;
6165
6166 /*
6167 * Look for matching patterns and build up a list.
6168 */
6169 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6170 while (it != mHWData->mGuestProperties.end())
6171 {
6172 if ( strPatterns.isEmpty()
6173 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6174 RTSTR_MAX,
6175 it->first.c_str(),
6176 RTSTR_MAX,
6177 NULL)
6178 )
6179 {
6180 propMap.insert(*it);
6181 }
6182
6183 it++;
6184 }
6185
6186 alock.release();
6187
6188 /*
6189 * And build up the arrays for returning the property information.
6190 */
6191 size_t cEntries = propMap.size();
6192 SafeArray<BSTR> names(cEntries);
6193 SafeArray<BSTR> values(cEntries);
6194 SafeArray<LONG64> timestamps(cEntries);
6195 SafeArray<BSTR> flags(cEntries);
6196 size_t iProp = 0;
6197
6198 it = propMap.begin();
6199 while (it != propMap.end())
6200 {
6201 char szFlags[MAX_FLAGS_LEN + 1];
6202 it->first.cloneTo(&names[iProp]);
6203 it->second.strValue.cloneTo(&values[iProp]);
6204 timestamps[iProp] = it->second.mTimestamp;
6205 writeFlags(it->second.mFlags, szFlags);
6206 Bstr(szFlags).cloneTo(&flags[iProp++]);
6207 it++;
6208 }
6209 names.detachTo(ComSafeArrayOutArg(aNames));
6210 values.detachTo(ComSafeArrayOutArg(aValues));
6211 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6212 flags.detachTo(ComSafeArrayOutArg(aFlags));
6213 return S_OK;
6214}
6215
6216/**
6217 * Enumerate the properties managed by a VM.
6218 * @returns E_ACCESSDENIED if the VM process is not available or not
6219 * currently handling queries and the setting should then be done in
6220 * VBoxSVC.
6221 */
6222HRESULT Machine::enumerateGuestPropertiesOnVM
6223 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6224 ComSafeArrayOut(BSTR, aValues),
6225 ComSafeArrayOut(LONG64, aTimestamps),
6226 ComSafeArrayOut(BSTR, aFlags))
6227{
6228 HRESULT rc;
6229 ComPtr<IInternalSessionControl> directControl;
6230 directControl = mData->mSession.mDirectControl;
6231
6232 if (!directControl)
6233 rc = E_ACCESSDENIED;
6234 else
6235 rc = directControl->EnumerateGuestProperties
6236 (aPatterns, ComSafeArrayOutArg(aNames),
6237 ComSafeArrayOutArg(aValues),
6238 ComSafeArrayOutArg(aTimestamps),
6239 ComSafeArrayOutArg(aFlags));
6240 return rc;
6241}
6242#endif // VBOX_WITH_GUEST_PROPS
6243
6244STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6245 ComSafeArrayOut(BSTR, aNames),
6246 ComSafeArrayOut(BSTR, aValues),
6247 ComSafeArrayOut(LONG64, aTimestamps),
6248 ComSafeArrayOut(BSTR, aFlags))
6249{
6250#ifndef VBOX_WITH_GUEST_PROPS
6251 ReturnComNotImplemented();
6252#else // VBOX_WITH_GUEST_PROPS
6253 CheckComArgMaybeNull(aPatterns);
6254 CheckComArgOutSafeArrayPointerValid(aNames);
6255 CheckComArgOutSafeArrayPointerValid(aValues);
6256 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6257 CheckComArgOutSafeArrayPointerValid(aFlags);
6258
6259 AutoCaller autoCaller(this);
6260 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6261
6262 HRESULT rc = enumerateGuestPropertiesOnVM
6263 (aPatterns, ComSafeArrayOutArg(aNames),
6264 ComSafeArrayOutArg(aValues),
6265 ComSafeArrayOutArg(aTimestamps),
6266 ComSafeArrayOutArg(aFlags));
6267 if (rc == E_ACCESSDENIED)
6268 /* The VM is not running or the service is not (yet) accessible */
6269 rc = enumerateGuestPropertiesInService
6270 (aPatterns, ComSafeArrayOutArg(aNames),
6271 ComSafeArrayOutArg(aValues),
6272 ComSafeArrayOutArg(aTimestamps),
6273 ComSafeArrayOutArg(aFlags));
6274 return rc;
6275#endif // VBOX_WITH_GUEST_PROPS
6276}
6277
6278STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6279 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6280{
6281 MediaData::AttachmentList atts;
6282
6283 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6284 if (FAILED(rc)) return rc;
6285
6286 SafeIfaceArray<IMediumAttachment> attachments(atts);
6287 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6288
6289 return S_OK;
6290}
6291
6292STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6293 LONG aControllerPort,
6294 LONG aDevice,
6295 IMediumAttachment **aAttachment)
6296{
6297 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6298 aControllerName, aControllerPort, aDevice));
6299
6300 CheckComArgStrNotEmptyOrNull(aControllerName);
6301 CheckComArgOutPointerValid(aAttachment);
6302
6303 AutoCaller autoCaller(this);
6304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6305
6306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6307
6308 *aAttachment = NULL;
6309
6310 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6311 aControllerName,
6312 aControllerPort,
6313 aDevice);
6314 if (pAttach.isNull())
6315 return setError(VBOX_E_OBJECT_NOT_FOUND,
6316 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6317 aDevice, aControllerPort, aControllerName);
6318
6319 pAttach.queryInterfaceTo(aAttachment);
6320
6321 return S_OK;
6322}
6323
6324STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6325 StorageBus_T aConnectionType,
6326 IStorageController **controller)
6327{
6328 CheckComArgStrNotEmptyOrNull(aName);
6329
6330 if ( (aConnectionType <= StorageBus_Null)
6331 || (aConnectionType > StorageBus_SAS))
6332 return setError(E_INVALIDARG,
6333 tr("Invalid connection type: %d"),
6334 aConnectionType);
6335
6336 AutoCaller autoCaller(this);
6337 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6338
6339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6340
6341 HRESULT rc = checkStateDependency(MutableStateDep);
6342 if (FAILED(rc)) return rc;
6343
6344 /* try to find one with the name first. */
6345 ComObjPtr<StorageController> ctrl;
6346
6347 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6348 if (SUCCEEDED(rc))
6349 return setError(VBOX_E_OBJECT_IN_USE,
6350 tr("Storage controller named '%ls' already exists"),
6351 aName);
6352
6353 ctrl.createObject();
6354
6355 /* get a new instance number for the storage controller */
6356 ULONG ulInstance = 0;
6357 bool fBootable = true;
6358 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6359 it != mStorageControllers->end();
6360 ++it)
6361 {
6362 if ((*it)->getStorageBus() == aConnectionType)
6363 {
6364 ULONG ulCurInst = (*it)->getInstance();
6365
6366 if (ulCurInst >= ulInstance)
6367 ulInstance = ulCurInst + 1;
6368
6369 /* Only one controller of each type can be marked as bootable. */
6370 if ((*it)->getBootable())
6371 fBootable = false;
6372 }
6373 }
6374
6375 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6376 if (FAILED(rc)) return rc;
6377
6378 setModified(IsModified_Storage);
6379 mStorageControllers.backup();
6380 mStorageControllers->push_back(ctrl);
6381
6382 ctrl.queryInterfaceTo(controller);
6383
6384 /* inform the direct session if any */
6385 alock.release();
6386 onStorageControllerChange();
6387
6388 return S_OK;
6389}
6390
6391STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6392 IStorageController **aStorageController)
6393{
6394 CheckComArgStrNotEmptyOrNull(aName);
6395
6396 AutoCaller autoCaller(this);
6397 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6398
6399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6400
6401 ComObjPtr<StorageController> ctrl;
6402
6403 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6404 if (SUCCEEDED(rc))
6405 ctrl.queryInterfaceTo(aStorageController);
6406
6407 return rc;
6408}
6409
6410STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6411 IStorageController **aStorageController)
6412{
6413 AutoCaller autoCaller(this);
6414 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6415
6416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6417
6418 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6419 it != mStorageControllers->end();
6420 ++it)
6421 {
6422 if ((*it)->getInstance() == aInstance)
6423 {
6424 (*it).queryInterfaceTo(aStorageController);
6425 return S_OK;
6426 }
6427 }
6428
6429 return setError(VBOX_E_OBJECT_NOT_FOUND,
6430 tr("Could not find a storage controller with instance number '%lu'"),
6431 aInstance);
6432}
6433
6434STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6435{
6436 AutoCaller autoCaller(this);
6437 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6438
6439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6440
6441 HRESULT rc = checkStateDependency(MutableStateDep);
6442 if (FAILED(rc)) return rc;
6443
6444 ComObjPtr<StorageController> ctrl;
6445
6446 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6447 if (SUCCEEDED(rc))
6448 {
6449 /* Ensure that only one controller of each type is marked as bootable. */
6450 if (fBootable == TRUE)
6451 {
6452 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6453 it != mStorageControllers->end();
6454 ++it)
6455 {
6456 ComObjPtr<StorageController> aCtrl = (*it);
6457
6458 if ( (aCtrl->getName() != Utf8Str(aName))
6459 && aCtrl->getBootable() == TRUE
6460 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6461 && aCtrl->getControllerType() == ctrl->getControllerType())
6462 {
6463 aCtrl->setBootable(FALSE);
6464 break;
6465 }
6466 }
6467 }
6468
6469 if (SUCCEEDED(rc))
6470 {
6471 ctrl->setBootable(fBootable);
6472 setModified(IsModified_Storage);
6473 }
6474 }
6475
6476 if (SUCCEEDED(rc))
6477 {
6478 /* inform the direct session if any */
6479 alock.release();
6480 onStorageControllerChange();
6481 }
6482
6483 return rc;
6484}
6485
6486STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6487{
6488 CheckComArgStrNotEmptyOrNull(aName);
6489
6490 AutoCaller autoCaller(this);
6491 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6492
6493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6494
6495 HRESULT rc = checkStateDependency(MutableStateDep);
6496 if (FAILED(rc)) return rc;
6497
6498 ComObjPtr<StorageController> ctrl;
6499 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6500 if (FAILED(rc)) return rc;
6501
6502 {
6503 /* find all attached devices to the appropriate storage controller and detach them all */
6504 // make a temporary list because detachDevice invalidates iterators into
6505 // mMediaData->mAttachments
6506 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6507
6508 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6509 it != llAttachments2.end();
6510 ++it)
6511 {
6512 MediumAttachment *pAttachTemp = *it;
6513
6514 AutoCaller localAutoCaller(pAttachTemp);
6515 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6516
6517 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6518
6519 if (pAttachTemp->getControllerName() == aName)
6520 {
6521 rc = detachDevice(pAttachTemp, alock, NULL);
6522 if (FAILED(rc)) return rc;
6523 }
6524 }
6525 }
6526
6527 /* We can remove it now. */
6528 setModified(IsModified_Storage);
6529 mStorageControllers.backup();
6530
6531 ctrl->unshare();
6532
6533 mStorageControllers->remove(ctrl);
6534
6535 /* inform the direct session if any */
6536 alock.release();
6537 onStorageControllerChange();
6538
6539 return S_OK;
6540}
6541
6542STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType,
6543 IUSBController **controller)
6544{
6545 if ( (aType <= USBControllerType_Null)
6546 || (aType >= USBControllerType_Last))
6547 return setError(E_INVALIDARG,
6548 tr("Invalid USB controller type: %d"),
6549 aType);
6550
6551 AutoCaller autoCaller(this);
6552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6553
6554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6555
6556 HRESULT rc = checkStateDependency(MutableStateDep);
6557 if (FAILED(rc)) return rc;
6558
6559 /* try to find one with the same type first. */
6560 ComObjPtr<USBController> ctrl;
6561
6562 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */);
6563 if (SUCCEEDED(rc))
6564 return setError(VBOX_E_OBJECT_IN_USE,
6565 tr("USB controller named '%ls' already exists"),
6566 aName);
6567
6568 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6569 ULONG maxInstances;
6570 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6571 if (FAILED(rc))
6572 return rc;
6573
6574 ULONG cInstances = getUSBControllerCountByType(aType);
6575 if (cInstances >= maxInstances)
6576 return setError(E_INVALIDARG,
6577 tr("Too many USB controllers of this type"));
6578
6579 ctrl.createObject();
6580
6581 rc = ctrl->init(this, aName, aType);
6582 if (FAILED(rc)) return rc;
6583
6584 setModified(IsModified_USB);
6585 mUSBControllers.backup();
6586 mUSBControllers->push_back(ctrl);
6587
6588 ctrl.queryInterfaceTo(controller);
6589
6590 /* inform the direct session if any */
6591 alock.release();
6592 onUSBControllerChange();
6593
6594 return S_OK;
6595}
6596
6597STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController)
6598{
6599 CheckComArgStrNotEmptyOrNull(aName);
6600
6601 AutoCaller autoCaller(this);
6602 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6603
6604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6605
6606 ComObjPtr<USBController> ctrl;
6607
6608 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6609 if (SUCCEEDED(rc))
6610 ctrl.queryInterfaceTo(aUSBController);
6611
6612 return rc;
6613}
6614
6615STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType,
6616 ULONG *aControllers)
6617{
6618 CheckComArgOutPointerValid(aControllers);
6619
6620 if ( (aType <= USBControllerType_Null)
6621 || (aType >= USBControllerType_Last))
6622 return setError(E_INVALIDARG,
6623 tr("Invalid USB controller type: %d"),
6624 aType);
6625
6626 AutoCaller autoCaller(this);
6627 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6628
6629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6630
6631 ComObjPtr<USBController> ctrl;
6632
6633 *aControllers = getUSBControllerCountByType(aType);
6634
6635 return S_OK;
6636}
6637
6638STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName)
6639{
6640 CheckComArgStrNotEmptyOrNull(aName);
6641
6642 AutoCaller autoCaller(this);
6643 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6644
6645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6646
6647 HRESULT rc = checkStateDependency(MutableStateDep);
6648 if (FAILED(rc)) return rc;
6649
6650 ComObjPtr<USBController> ctrl;
6651 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */);
6652 if (FAILED(rc)) return rc;
6653
6654 setModified(IsModified_USB);
6655 mUSBControllers.backup();
6656
6657 ctrl->unshare();
6658
6659 mUSBControllers->remove(ctrl);
6660
6661 /* inform the direct session if any */
6662 alock.release();
6663 onUSBControllerChange();
6664
6665 return S_OK;
6666}
6667
6668STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6669 ULONG *puOriginX,
6670 ULONG *puOriginY,
6671 ULONG *puWidth,
6672 ULONG *puHeight,
6673 BOOL *pfEnabled)
6674{
6675 LogFlowThisFunc(("\n"));
6676
6677 CheckComArgNotNull(puOriginX);
6678 CheckComArgNotNull(puOriginY);
6679 CheckComArgNotNull(puWidth);
6680 CheckComArgNotNull(puHeight);
6681 CheckComArgNotNull(pfEnabled);
6682
6683 uint32_t u32OriginX= 0;
6684 uint32_t u32OriginY= 0;
6685 uint32_t u32Width = 0;
6686 uint32_t u32Height = 0;
6687 uint16_t u16Flags = 0;
6688
6689 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6690 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6691 if (RT_FAILURE(vrc))
6692 {
6693#ifdef RT_OS_WINDOWS
6694 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6695 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6696 * So just assign fEnable to TRUE again.
6697 * The right fix would be to change GUI API wrappers to make sure that parameters
6698 * are changed only if API succeeds.
6699 */
6700 *pfEnabled = TRUE;
6701#endif
6702 return setError(VBOX_E_IPRT_ERROR,
6703 tr("Saved guest size is not available (%Rrc)"),
6704 vrc);
6705 }
6706
6707 *puOriginX = u32OriginX;
6708 *puOriginY = u32OriginY;
6709 *puWidth = u32Width;
6710 *puHeight = u32Height;
6711 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6712
6713 return S_OK;
6714}
6715
6716STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6717{
6718 LogFlowThisFunc(("\n"));
6719
6720 CheckComArgNotNull(aSize);
6721 CheckComArgNotNull(aWidth);
6722 CheckComArgNotNull(aHeight);
6723
6724 if (aScreenId != 0)
6725 return E_NOTIMPL;
6726
6727 AutoCaller autoCaller(this);
6728 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6729
6730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6731
6732 uint8_t *pu8Data = NULL;
6733 uint32_t cbData = 0;
6734 uint32_t u32Width = 0;
6735 uint32_t u32Height = 0;
6736
6737 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6738
6739 if (RT_FAILURE(vrc))
6740 return setError(VBOX_E_IPRT_ERROR,
6741 tr("Saved screenshot data is not available (%Rrc)"),
6742 vrc);
6743
6744 *aSize = cbData;
6745 *aWidth = u32Width;
6746 *aHeight = u32Height;
6747
6748 freeSavedDisplayScreenshot(pu8Data);
6749
6750 return S_OK;
6751}
6752
6753STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6754{
6755 LogFlowThisFunc(("\n"));
6756
6757 CheckComArgNotNull(aWidth);
6758 CheckComArgNotNull(aHeight);
6759 CheckComArgOutSafeArrayPointerValid(aData);
6760
6761 if (aScreenId != 0)
6762 return E_NOTIMPL;
6763
6764 AutoCaller autoCaller(this);
6765 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6766
6767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6768
6769 uint8_t *pu8Data = NULL;
6770 uint32_t cbData = 0;
6771 uint32_t u32Width = 0;
6772 uint32_t u32Height = 0;
6773
6774 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6775
6776 if (RT_FAILURE(vrc))
6777 return setError(VBOX_E_IPRT_ERROR,
6778 tr("Saved screenshot data is not available (%Rrc)"),
6779 vrc);
6780
6781 *aWidth = u32Width;
6782 *aHeight = u32Height;
6783
6784 com::SafeArray<BYTE> bitmap(cbData);
6785 /* Convert pixels to format expected by the API caller. */
6786 if (aBGR)
6787 {
6788 /* [0] B, [1] G, [2] R, [3] A. */
6789 for (unsigned i = 0; i < cbData; i += 4)
6790 {
6791 bitmap[i] = pu8Data[i];
6792 bitmap[i + 1] = pu8Data[i + 1];
6793 bitmap[i + 2] = pu8Data[i + 2];
6794 bitmap[i + 3] = 0xff;
6795 }
6796 }
6797 else
6798 {
6799 /* [0] R, [1] G, [2] B, [3] A. */
6800 for (unsigned i = 0; i < cbData; i += 4)
6801 {
6802 bitmap[i] = pu8Data[i + 2];
6803 bitmap[i + 1] = pu8Data[i + 1];
6804 bitmap[i + 2] = pu8Data[i];
6805 bitmap[i + 3] = 0xff;
6806 }
6807 }
6808 bitmap.detachTo(ComSafeArrayOutArg(aData));
6809
6810 freeSavedDisplayScreenshot(pu8Data);
6811
6812 return S_OK;
6813}
6814
6815
6816STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6817{
6818 LogFlowThisFunc(("\n"));
6819
6820 CheckComArgNotNull(aWidth);
6821 CheckComArgNotNull(aHeight);
6822 CheckComArgOutSafeArrayPointerValid(aData);
6823
6824 if (aScreenId != 0)
6825 return E_NOTIMPL;
6826
6827 AutoCaller autoCaller(this);
6828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6829
6830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6831
6832 uint8_t *pu8Data = NULL;
6833 uint32_t cbData = 0;
6834 uint32_t u32Width = 0;
6835 uint32_t u32Height = 0;
6836
6837 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6838
6839 if (RT_FAILURE(vrc))
6840 return setError(VBOX_E_IPRT_ERROR,
6841 tr("Saved screenshot data is not available (%Rrc)"),
6842 vrc);
6843
6844 *aWidth = u32Width;
6845 *aHeight = u32Height;
6846
6847 HRESULT rc = S_OK;
6848 uint8_t *pu8PNG = NULL;
6849 uint32_t cbPNG = 0;
6850 uint32_t cxPNG = 0;
6851 uint32_t cyPNG = 0;
6852
6853 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6854
6855 if (RT_SUCCESS(vrc))
6856 {
6857 com::SafeArray<BYTE> screenData(cbPNG);
6858 screenData.initFrom(pu8PNG, cbPNG);
6859 if (pu8PNG)
6860 RTMemFree(pu8PNG);
6861 screenData.detachTo(ComSafeArrayOutArg(aData));
6862 }
6863 else
6864 {
6865 if (pu8PNG)
6866 RTMemFree(pu8PNG);
6867 return setError(VBOX_E_IPRT_ERROR,
6868 tr("Could not convert screenshot to PNG (%Rrc)"),
6869 vrc);
6870 }
6871
6872 freeSavedDisplayScreenshot(pu8Data);
6873
6874 return rc;
6875}
6876
6877STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6878{
6879 LogFlowThisFunc(("\n"));
6880
6881 CheckComArgNotNull(aSize);
6882 CheckComArgNotNull(aWidth);
6883 CheckComArgNotNull(aHeight);
6884
6885 if (aScreenId != 0)
6886 return E_NOTIMPL;
6887
6888 AutoCaller autoCaller(this);
6889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6890
6891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6892
6893 uint8_t *pu8Data = NULL;
6894 uint32_t cbData = 0;
6895 uint32_t u32Width = 0;
6896 uint32_t u32Height = 0;
6897
6898 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6899
6900 if (RT_FAILURE(vrc))
6901 return setError(VBOX_E_IPRT_ERROR,
6902 tr("Saved screenshot data is not available (%Rrc)"),
6903 vrc);
6904
6905 *aSize = cbData;
6906 *aWidth = u32Width;
6907 *aHeight = u32Height;
6908
6909 freeSavedDisplayScreenshot(pu8Data);
6910
6911 return S_OK;
6912}
6913
6914STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6915{
6916 LogFlowThisFunc(("\n"));
6917
6918 CheckComArgNotNull(aWidth);
6919 CheckComArgNotNull(aHeight);
6920 CheckComArgOutSafeArrayPointerValid(aData);
6921
6922 if (aScreenId != 0)
6923 return E_NOTIMPL;
6924
6925 AutoCaller autoCaller(this);
6926 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6927
6928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6929
6930 uint8_t *pu8Data = NULL;
6931 uint32_t cbData = 0;
6932 uint32_t u32Width = 0;
6933 uint32_t u32Height = 0;
6934
6935 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6936
6937 if (RT_FAILURE(vrc))
6938 return setError(VBOX_E_IPRT_ERROR,
6939 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6940 vrc);
6941
6942 *aWidth = u32Width;
6943 *aHeight = u32Height;
6944
6945 com::SafeArray<BYTE> png(cbData);
6946 png.initFrom(pu8Data, cbData);
6947 png.detachTo(ComSafeArrayOutArg(aData));
6948
6949 freeSavedDisplayScreenshot(pu8Data);
6950
6951 return S_OK;
6952}
6953
6954STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6955{
6956 HRESULT rc = S_OK;
6957 LogFlowThisFunc(("\n"));
6958
6959 AutoCaller autoCaller(this);
6960 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6961
6962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6963
6964 if (!mHWData->mCPUHotPlugEnabled)
6965 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6966
6967 if (aCpu >= mHWData->mCPUCount)
6968 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6969
6970 if (mHWData->mCPUAttached[aCpu])
6971 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6972
6973 alock.release();
6974 rc = onCPUChange(aCpu, false);
6975 alock.acquire();
6976 if (FAILED(rc)) return rc;
6977
6978 setModified(IsModified_MachineData);
6979 mHWData.backup();
6980 mHWData->mCPUAttached[aCpu] = true;
6981
6982 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6983 if (Global::IsOnline(mData->mMachineState))
6984 saveSettings(NULL);
6985
6986 return S_OK;
6987}
6988
6989STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6990{
6991 HRESULT rc = S_OK;
6992 LogFlowThisFunc(("\n"));
6993
6994 AutoCaller autoCaller(this);
6995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6996
6997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6998
6999 if (!mHWData->mCPUHotPlugEnabled)
7000 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
7001
7002 if (aCpu >= SchemaDefs::MaxCPUCount)
7003 return setError(E_INVALIDARG,
7004 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
7005 SchemaDefs::MaxCPUCount);
7006
7007 if (!mHWData->mCPUAttached[aCpu])
7008 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
7009
7010 /* CPU 0 can't be detached */
7011 if (aCpu == 0)
7012 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
7013
7014 alock.release();
7015 rc = onCPUChange(aCpu, true);
7016 alock.acquire();
7017 if (FAILED(rc)) return rc;
7018
7019 setModified(IsModified_MachineData);
7020 mHWData.backup();
7021 mHWData->mCPUAttached[aCpu] = false;
7022
7023 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
7024 if (Global::IsOnline(mData->mMachineState))
7025 saveSettings(NULL);
7026
7027 return S_OK;
7028}
7029
7030STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
7031{
7032 LogFlowThisFunc(("\n"));
7033
7034 CheckComArgNotNull(aCpuAttached);
7035
7036 *aCpuAttached = false;
7037
7038 AutoCaller autoCaller(this);
7039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7040
7041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7042
7043 /* If hotplug is enabled the CPU is always enabled. */
7044 if (!mHWData->mCPUHotPlugEnabled)
7045 {
7046 if (aCpu < mHWData->mCPUCount)
7047 *aCpuAttached = true;
7048 }
7049 else
7050 {
7051 if (aCpu < SchemaDefs::MaxCPUCount)
7052 *aCpuAttached = mHWData->mCPUAttached[aCpu];
7053 }
7054
7055 return S_OK;
7056}
7057
7058STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
7059{
7060 CheckComArgOutPointerValid(aName);
7061
7062 AutoCaller autoCaller(this);
7063 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7064
7065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7066
7067 Utf8Str log = queryLogFilename(aIdx);
7068 if (!RTFileExists(log.c_str()))
7069 log.setNull();
7070 log.cloneTo(aName);
7071
7072 return S_OK;
7073}
7074
7075STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
7076{
7077 LogFlowThisFunc(("\n"));
7078 CheckComArgOutSafeArrayPointerValid(aData);
7079 if (aSize < 0)
7080 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
7081
7082 AutoCaller autoCaller(this);
7083 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7084
7085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7086
7087 HRESULT rc = S_OK;
7088 Utf8Str log = queryLogFilename(aIdx);
7089
7090 /* do not unnecessarily hold the lock while doing something which does
7091 * not need the lock and potentially takes a long time. */
7092 alock.release();
7093
7094 /* Limit the chunk size to 32K for now, as that gives better performance
7095 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
7096 * One byte expands to approx. 25 bytes of breathtaking XML. */
7097 size_t cbData = (size_t)RT_MIN(aSize, 32768);
7098 com::SafeArray<BYTE> logData(cbData);
7099
7100 RTFILE LogFile;
7101 int vrc = RTFileOpen(&LogFile, log.c_str(),
7102 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
7103 if (RT_SUCCESS(vrc))
7104 {
7105 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
7106 if (RT_SUCCESS(vrc))
7107 logData.resize(cbData);
7108 else
7109 rc = setError(VBOX_E_IPRT_ERROR,
7110 tr("Could not read log file '%s' (%Rrc)"),
7111 log.c_str(), vrc);
7112 RTFileClose(LogFile);
7113 }
7114 else
7115 rc = setError(VBOX_E_IPRT_ERROR,
7116 tr("Could not open log file '%s' (%Rrc)"),
7117 log.c_str(), vrc);
7118
7119 if (FAILED(rc))
7120 logData.resize(0);
7121 logData.detachTo(ComSafeArrayOutArg(aData));
7122
7123 return rc;
7124}
7125
7126
7127/**
7128 * Currently this method doesn't attach device to the running VM,
7129 * just makes sure it's plugged on next VM start.
7130 */
7131STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7132{
7133 AutoCaller autoCaller(this);
7134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7135
7136 // lock scope
7137 {
7138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7139
7140 HRESULT rc = checkStateDependency(MutableStateDep);
7141 if (FAILED(rc)) return rc;
7142
7143 ChipsetType_T aChipset = ChipsetType_PIIX3;
7144 COMGETTER(ChipsetType)(&aChipset);
7145
7146 if (aChipset != ChipsetType_ICH9)
7147 {
7148 return setError(E_INVALIDARG,
7149 tr("Host PCI attachment only supported with ICH9 chipset"));
7150 }
7151
7152 // check if device with this host PCI address already attached
7153 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7154 it != mHWData->mPCIDeviceAssignments.end();
7155 ++it)
7156 {
7157 LONG iHostAddress = -1;
7158 ComPtr<PCIDeviceAttachment> pAttach;
7159 pAttach = *it;
7160 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7161 if (iHostAddress == hostAddress)
7162 return setError(E_INVALIDARG,
7163 tr("Device with host PCI address already attached to this VM"));
7164 }
7165
7166 ComObjPtr<PCIDeviceAttachment> pda;
7167 char name[32];
7168
7169 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7170 Bstr bname(name);
7171 pda.createObject();
7172 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7173 setModified(IsModified_MachineData);
7174 mHWData.backup();
7175 mHWData->mPCIDeviceAssignments.push_back(pda);
7176 }
7177
7178 return S_OK;
7179}
7180
7181/**
7182 * Currently this method doesn't detach device from the running VM,
7183 * just makes sure it's not plugged on next VM start.
7184 */
7185STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7186{
7187 AutoCaller autoCaller(this);
7188 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7189
7190 ComObjPtr<PCIDeviceAttachment> pAttach;
7191 bool fRemoved = false;
7192 HRESULT rc;
7193
7194 // lock scope
7195 {
7196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7197
7198 rc = checkStateDependency(MutableStateDep);
7199 if (FAILED(rc)) return rc;
7200
7201 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7202 it != mHWData->mPCIDeviceAssignments.end();
7203 ++it)
7204 {
7205 LONG iHostAddress = -1;
7206 pAttach = *it;
7207 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7208 if (iHostAddress != -1 && iHostAddress == hostAddress)
7209 {
7210 setModified(IsModified_MachineData);
7211 mHWData.backup();
7212 mHWData->mPCIDeviceAssignments.remove(pAttach);
7213 fRemoved = true;
7214 break;
7215 }
7216 }
7217 }
7218
7219
7220 /* Fire event outside of the lock */
7221 if (fRemoved)
7222 {
7223 Assert(!pAttach.isNull());
7224 ComPtr<IEventSource> es;
7225 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7226 Assert(SUCCEEDED(rc));
7227 Bstr mid;
7228 rc = this->COMGETTER(Id)(mid.asOutParam());
7229 Assert(SUCCEEDED(rc));
7230 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7231 }
7232
7233 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7234 tr("No host PCI device %08x attached"),
7235 hostAddress
7236 );
7237}
7238
7239STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7240{
7241 CheckComArgOutSafeArrayPointerValid(aAssignments);
7242
7243 AutoCaller autoCaller(this);
7244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7245
7246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7247
7248 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7249 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7250
7251 return S_OK;
7252}
7253
7254STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7255{
7256 CheckComArgOutPointerValid(aBandwidthControl);
7257
7258 AutoCaller autoCaller(this);
7259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7260
7261 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7262
7263 return S_OK;
7264}
7265
7266STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7267{
7268 CheckComArgOutPointerValid(pfEnabled);
7269 AutoCaller autoCaller(this);
7270 HRESULT hrc = autoCaller.rc();
7271 if (SUCCEEDED(hrc))
7272 {
7273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7274 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7275 }
7276 return hrc;
7277}
7278
7279STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7280{
7281 AutoCaller autoCaller(this);
7282 HRESULT hrc = autoCaller.rc();
7283 if (SUCCEEDED(hrc))
7284 {
7285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7286 hrc = checkStateDependency(MutableStateDep);
7287 if (SUCCEEDED(hrc))
7288 {
7289 hrc = mHWData.backupEx();
7290 if (SUCCEEDED(hrc))
7291 {
7292 setModified(IsModified_MachineData);
7293 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7294 }
7295 }
7296 }
7297 return hrc;
7298}
7299
7300STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7301{
7302 CheckComArgOutPointerValid(pbstrConfig);
7303 AutoCaller autoCaller(this);
7304 HRESULT hrc = autoCaller.rc();
7305 if (SUCCEEDED(hrc))
7306 {
7307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7308 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7309 }
7310 return hrc;
7311}
7312
7313STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7314{
7315 CheckComArgStr(bstrConfig);
7316 AutoCaller autoCaller(this);
7317 HRESULT hrc = autoCaller.rc();
7318 if (SUCCEEDED(hrc))
7319 {
7320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7321 hrc = checkStateDependency(MutableStateDep);
7322 if (SUCCEEDED(hrc))
7323 {
7324 hrc = mHWData.backupEx();
7325 if (SUCCEEDED(hrc))
7326 {
7327 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7328 if (SUCCEEDED(hrc))
7329 setModified(IsModified_MachineData);
7330 }
7331 }
7332 }
7333 return hrc;
7334
7335}
7336
7337STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7338{
7339 CheckComArgOutPointerValid(pfAllow);
7340 AutoCaller autoCaller(this);
7341 HRESULT hrc = autoCaller.rc();
7342 if (SUCCEEDED(hrc))
7343 {
7344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7345 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7346 }
7347 return hrc;
7348}
7349
7350STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7351{
7352 AutoCaller autoCaller(this);
7353 HRESULT hrc = autoCaller.rc();
7354 if (SUCCEEDED(hrc))
7355 {
7356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7357 hrc = checkStateDependency(MutableStateDep);
7358 if (SUCCEEDED(hrc))
7359 {
7360 hrc = mHWData.backupEx();
7361 if (SUCCEEDED(hrc))
7362 {
7363 setModified(IsModified_MachineData);
7364 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7365 }
7366 }
7367 }
7368 return hrc;
7369}
7370
7371STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7372{
7373 CheckComArgOutPointerValid(pfEnabled);
7374 AutoCaller autoCaller(this);
7375 HRESULT hrc = autoCaller.rc();
7376 if (SUCCEEDED(hrc))
7377 {
7378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7379 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7380 }
7381 return hrc;
7382}
7383
7384STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7385{
7386 AutoCaller autoCaller(this);
7387 HRESULT hrc = autoCaller.rc();
7388 if (SUCCEEDED(hrc))
7389 {
7390 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7391 hrc = checkStateDependency(MutableStateDep);
7392 if ( SUCCEEDED(hrc)
7393 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7394 {
7395 AutostartDb *autostartDb = mParent->getAutostartDb();
7396 int vrc;
7397
7398 if (fEnabled)
7399 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7400 else
7401 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7402
7403 if (RT_SUCCESS(vrc))
7404 {
7405 hrc = mHWData.backupEx();
7406 if (SUCCEEDED(hrc))
7407 {
7408 setModified(IsModified_MachineData);
7409 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7410 }
7411 }
7412 else if (vrc == VERR_NOT_SUPPORTED)
7413 hrc = setError(VBOX_E_NOT_SUPPORTED,
7414 tr("The VM autostart feature is not supported on this platform"));
7415 else if (vrc == VERR_PATH_NOT_FOUND)
7416 hrc = setError(E_FAIL,
7417 tr("The path to the autostart database is not set"));
7418 else
7419 hrc = setError(E_UNEXPECTED,
7420 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7421 fEnabled ? "Adding" : "Removing",
7422 mUserData->s.strName.c_str(), vrc);
7423 }
7424 }
7425 return hrc;
7426}
7427
7428STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7429{
7430 CheckComArgOutPointerValid(puDelay);
7431 AutoCaller autoCaller(this);
7432 HRESULT hrc = autoCaller.rc();
7433 if (SUCCEEDED(hrc))
7434 {
7435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7436 *puDelay = mHWData->mAutostart.uAutostartDelay;
7437 }
7438 return hrc;
7439}
7440
7441STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7442{
7443 AutoCaller autoCaller(this);
7444 HRESULT hrc = autoCaller.rc();
7445 if (SUCCEEDED(hrc))
7446 {
7447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7448 hrc = checkStateDependency(MutableStateDep);
7449 if (SUCCEEDED(hrc))
7450 {
7451 hrc = mHWData.backupEx();
7452 if (SUCCEEDED(hrc))
7453 {
7454 setModified(IsModified_MachineData);
7455 mHWData->mAutostart.uAutostartDelay = uDelay;
7456 }
7457 }
7458 }
7459 return hrc;
7460}
7461
7462STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7463{
7464 CheckComArgOutPointerValid(penmAutostopType);
7465 AutoCaller autoCaller(this);
7466 HRESULT hrc = autoCaller.rc();
7467 if (SUCCEEDED(hrc))
7468 {
7469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7470 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7471 }
7472 return hrc;
7473}
7474
7475STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7476{
7477 AutoCaller autoCaller(this);
7478 HRESULT hrc = autoCaller.rc();
7479 if (SUCCEEDED(hrc))
7480 {
7481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7482 hrc = checkStateDependency(MutableStateDep);
7483 if ( SUCCEEDED(hrc)
7484 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7485 {
7486 AutostartDb *autostartDb = mParent->getAutostartDb();
7487 int vrc;
7488
7489 if (enmAutostopType != AutostopType_Disabled)
7490 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7491 else
7492 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7493
7494 if (RT_SUCCESS(vrc))
7495 {
7496 hrc = mHWData.backupEx();
7497 if (SUCCEEDED(hrc))
7498 {
7499 setModified(IsModified_MachineData);
7500 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7501 }
7502 }
7503 else if (vrc == VERR_NOT_SUPPORTED)
7504 hrc = setError(VBOX_E_NOT_SUPPORTED,
7505 tr("The VM autostop feature is not supported on this platform"));
7506 else if (vrc == VERR_PATH_NOT_FOUND)
7507 hrc = setError(E_FAIL,
7508 tr("The path to the autostart database is not set"));
7509 else
7510 hrc = setError(E_UNEXPECTED,
7511 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7512 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7513 mUserData->s.strName.c_str(), vrc);
7514 }
7515 }
7516 return hrc;
7517}
7518
7519STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7520{
7521 CheckComArgOutPointerValid(aDefaultFrontend);
7522 AutoCaller autoCaller(this);
7523 HRESULT hrc = autoCaller.rc();
7524 if (SUCCEEDED(hrc))
7525 {
7526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7527 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7528 }
7529 return hrc;
7530}
7531
7532STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7533{
7534 CheckComArgStr(aDefaultFrontend);
7535 AutoCaller autoCaller(this);
7536 HRESULT hrc = autoCaller.rc();
7537 if (SUCCEEDED(hrc))
7538 {
7539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7540 hrc = checkStateDependency(MutableOrSavedStateDep);
7541 if (SUCCEEDED(hrc))
7542 {
7543 hrc = mHWData.backupEx();
7544 if (SUCCEEDED(hrc))
7545 {
7546 setModified(IsModified_MachineData);
7547 mHWData->mDefaultFrontend = aDefaultFrontend;
7548 }
7549 }
7550 }
7551 return hrc;
7552}
7553
7554STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7555{
7556 CheckComArgSafeArrayNotNull(aIcon);
7557 CheckComArgOutSafeArrayPointerValid(aIcon);
7558 AutoCaller autoCaller(this);
7559 HRESULT hrc = autoCaller.rc();
7560 if (SUCCEEDED(hrc))
7561 {
7562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7563 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7564 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7565 icon.detachTo(ComSafeArrayOutArg(aIcon));
7566 }
7567 return hrc;
7568}
7569
7570STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7571{
7572 CheckComArgSafeArrayNotNull(aIcon);
7573 AutoCaller autoCaller(this);
7574 HRESULT hrc = autoCaller.rc();
7575 if (SUCCEEDED(hrc))
7576 {
7577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7578 hrc = checkStateDependency(MutableOrSavedStateDep);
7579 if (SUCCEEDED(hrc))
7580 {
7581 setModified(IsModified_MachineData);
7582 mUserData.backup();
7583 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7584 mUserData->mIcon.resize(icon.size());
7585 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7586 }
7587 }
7588 return hrc;
7589}
7590
7591STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable)
7592{
7593 CheckComArgOutPointerValid(aAvailable);
7594
7595 AutoCaller autoCaller(this);
7596 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7597
7598#ifdef VBOX_WITH_USB
7599 *aAvailable = true;
7600#else
7601 *aAvailable = false;
7602#endif
7603 return S_OK;
7604}
7605
7606STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7607{
7608 LogFlowFuncEnter();
7609
7610 CheckComArgNotNull(pTarget);
7611 CheckComArgOutPointerValid(pProgress);
7612
7613 /* Convert the options. */
7614 RTCList<CloneOptions_T> optList;
7615 if (options != NULL)
7616 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7617
7618 if (optList.contains(CloneOptions_Link))
7619 {
7620 if (!isSnapshotMachine())
7621 return setError(E_INVALIDARG,
7622 tr("Linked clone can only be created from a snapshot"));
7623 if (mode != CloneMode_MachineState)
7624 return setError(E_INVALIDARG,
7625 tr("Linked clone can only be created for a single machine state"));
7626 }
7627 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7628
7629 AutoCaller autoCaller(this);
7630 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7631
7632
7633 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7634
7635 HRESULT rc = pWorker->start(pProgress);
7636
7637 LogFlowFuncLeave();
7638
7639 return rc;
7640}
7641
7642// public methods for internal purposes
7643/////////////////////////////////////////////////////////////////////////////
7644
7645/**
7646 * Adds the given IsModified_* flag to the dirty flags of the machine.
7647 * This must be called either during loadSettings or under the machine write lock.
7648 * @param fl
7649 */
7650void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7651{
7652 mData->flModifications |= fl;
7653 if (fAllowStateModification && isStateModificationAllowed())
7654 mData->mCurrentStateModified = true;
7655}
7656
7657/**
7658 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7659 * care of the write locking.
7660 *
7661 * @param fModifications The flag to add.
7662 */
7663void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7664{
7665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7666 setModified(fModification, fAllowStateModification);
7667}
7668
7669/**
7670 * Saves the registry entry of this machine to the given configuration node.
7671 *
7672 * @param aEntryNode Node to save the registry entry to.
7673 *
7674 * @note locks this object for reading.
7675 */
7676HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7677{
7678 AutoLimitedCaller autoCaller(this);
7679 AssertComRCReturnRC(autoCaller.rc());
7680
7681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7682
7683 data.uuid = mData->mUuid;
7684 data.strSettingsFile = mData->m_strConfigFile;
7685
7686 return S_OK;
7687}
7688
7689/**
7690 * Calculates the absolute path of the given path taking the directory of the
7691 * machine settings file as the current directory.
7692 *
7693 * @param aPath Path to calculate the absolute path for.
7694 * @param aResult Where to put the result (used only on success, can be the
7695 * same Utf8Str instance as passed in @a aPath).
7696 * @return IPRT result.
7697 *
7698 * @note Locks this object for reading.
7699 */
7700int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7701{
7702 AutoCaller autoCaller(this);
7703 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7704
7705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7706
7707 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7708
7709 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7710
7711 strSettingsDir.stripFilename();
7712 char folder[RTPATH_MAX];
7713 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7714 if (RT_SUCCESS(vrc))
7715 aResult = folder;
7716
7717 return vrc;
7718}
7719
7720/**
7721 * Copies strSource to strTarget, making it relative to the machine folder
7722 * if it is a subdirectory thereof, or simply copying it otherwise.
7723 *
7724 * @param strSource Path to evaluate and copy.
7725 * @param strTarget Buffer to receive target path.
7726 *
7727 * @note Locks this object for reading.
7728 */
7729void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7730 Utf8Str &strTarget)
7731{
7732 AutoCaller autoCaller(this);
7733 AssertComRCReturn(autoCaller.rc(), (void)0);
7734
7735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7736
7737 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7738 // use strTarget as a temporary buffer to hold the machine settings dir
7739 strTarget = mData->m_strConfigFileFull;
7740 strTarget.stripFilename();
7741 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7742 {
7743 // is relative: then append what's left
7744 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7745 // for empty paths (only possible for subdirs) use "." to avoid
7746 // triggering default settings for not present config attributes.
7747 if (strTarget.isEmpty())
7748 strTarget = ".";
7749 }
7750 else
7751 // is not relative: then overwrite
7752 strTarget = strSource;
7753}
7754
7755/**
7756 * Returns the full path to the machine's log folder in the
7757 * \a aLogFolder argument.
7758 */
7759void Machine::getLogFolder(Utf8Str &aLogFolder)
7760{
7761 AutoCaller autoCaller(this);
7762 AssertComRCReturnVoid(autoCaller.rc());
7763
7764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7765
7766 char szTmp[RTPATH_MAX];
7767 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7768 if (RT_SUCCESS(vrc))
7769 {
7770 if (szTmp[0] && !mUserData.isNull())
7771 {
7772 char szTmp2[RTPATH_MAX];
7773 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7774 if (RT_SUCCESS(vrc))
7775 aLogFolder = BstrFmt("%s%c%s",
7776 szTmp2,
7777 RTPATH_DELIMITER,
7778 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7779 }
7780 else
7781 vrc = VERR_PATH_IS_RELATIVE;
7782 }
7783
7784 if (RT_FAILURE(vrc))
7785 {
7786 // fallback if VBOX_USER_LOGHOME is not set or invalid
7787 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7788 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7789 aLogFolder.append(RTPATH_DELIMITER);
7790 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7791 }
7792}
7793
7794/**
7795 * Returns the full path to the machine's log file for an given index.
7796 */
7797Utf8Str Machine::queryLogFilename(ULONG idx)
7798{
7799 Utf8Str logFolder;
7800 getLogFolder(logFolder);
7801 Assert(logFolder.length());
7802 Utf8Str log;
7803 if (idx == 0)
7804 log = Utf8StrFmt("%s%cVBox.log",
7805 logFolder.c_str(), RTPATH_DELIMITER);
7806 else
7807 log = Utf8StrFmt("%s%cVBox.log.%d",
7808 logFolder.c_str(), RTPATH_DELIMITER, idx);
7809 return log;
7810}
7811
7812/**
7813 * Composes a unique saved state filename based on the current system time. The filename is
7814 * granular to the second so this will work so long as no more than one snapshot is taken on
7815 * a machine per second.
7816 *
7817 * Before version 4.1, we used this formula for saved state files:
7818 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7819 * which no longer works because saved state files can now be shared between the saved state of the
7820 * "saved" machine and an online snapshot, and the following would cause problems:
7821 * 1) save machine
7822 * 2) create online snapshot from that machine state --> reusing saved state file
7823 * 3) save machine again --> filename would be reused, breaking the online snapshot
7824 *
7825 * So instead we now use a timestamp.
7826 *
7827 * @param str
7828 */
7829void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7830{
7831 AutoCaller autoCaller(this);
7832 AssertComRCReturnVoid(autoCaller.rc());
7833
7834 {
7835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7836 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7837 }
7838
7839 RTTIMESPEC ts;
7840 RTTimeNow(&ts);
7841 RTTIME time;
7842 RTTimeExplode(&time, &ts);
7843
7844 strStateFilePath += RTPATH_DELIMITER;
7845 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7846 time.i32Year, time.u8Month, time.u8MonthDay,
7847 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7848}
7849
7850/**
7851 * Returns the full path to the default video capture file.
7852 */
7853void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7854{
7855 AutoCaller autoCaller(this);
7856 AssertComRCReturnVoid(autoCaller.rc());
7857
7858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7859
7860 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7861 strFile.stripExt(); // path/to/machinesfolder/vmname/vmname
7862 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7863}
7864
7865/**
7866 * Returns whether at least one USB controller is present for the VM.
7867 */
7868bool Machine::isUSBControllerPresent()
7869{
7870 AutoCaller autoCaller(this);
7871 AssertComRCReturn(autoCaller.rc(), false);
7872
7873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7874
7875 return (mUSBControllers->size() > 0);
7876}
7877
7878/**
7879 * @note Locks this object for writing, calls the client process
7880 * (inside the lock).
7881 */
7882HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7883 const Utf8Str &strFrontend,
7884 const Utf8Str &strEnvironment,
7885 ProgressProxy *aProgress)
7886{
7887 LogFlowThisFuncEnter();
7888
7889 AssertReturn(aControl, E_FAIL);
7890 AssertReturn(aProgress, E_FAIL);
7891 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7892
7893 AutoCaller autoCaller(this);
7894 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7895
7896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7897
7898 if (!mData->mRegistered)
7899 return setError(E_UNEXPECTED,
7900 tr("The machine '%s' is not registered"),
7901 mUserData->s.strName.c_str());
7902
7903 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7904
7905 if ( mData->mSession.mState == SessionState_Locked
7906 || mData->mSession.mState == SessionState_Spawning
7907 || mData->mSession.mState == SessionState_Unlocking)
7908 return setError(VBOX_E_INVALID_OBJECT_STATE,
7909 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7910 mUserData->s.strName.c_str());
7911
7912 /* may not be busy */
7913 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7914
7915 /* get the path to the executable */
7916 char szPath[RTPATH_MAX];
7917 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7918 size_t sz = strlen(szPath);
7919 szPath[sz++] = RTPATH_DELIMITER;
7920 szPath[sz] = 0;
7921 char *cmd = szPath + sz;
7922 sz = sizeof(szPath) - sz;
7923
7924 int vrc = VINF_SUCCESS;
7925 RTPROCESS pid = NIL_RTPROCESS;
7926
7927 RTENV env = RTENV_DEFAULT;
7928
7929 if (!strEnvironment.isEmpty())
7930 {
7931 char *newEnvStr = NULL;
7932
7933 do
7934 {
7935 /* clone the current environment */
7936 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7937 AssertRCBreakStmt(vrc2, vrc = vrc2);
7938
7939 newEnvStr = RTStrDup(strEnvironment.c_str());
7940 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7941
7942 /* put new variables to the environment
7943 * (ignore empty variable names here since RTEnv API
7944 * intentionally doesn't do that) */
7945 char *var = newEnvStr;
7946 for (char *p = newEnvStr; *p; ++p)
7947 {
7948 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7949 {
7950 *p = '\0';
7951 if (*var)
7952 {
7953 char *val = strchr(var, '=');
7954 if (val)
7955 {
7956 *val++ = '\0';
7957 vrc2 = RTEnvSetEx(env, var, val);
7958 }
7959 else
7960 vrc2 = RTEnvUnsetEx(env, var);
7961 if (RT_FAILURE(vrc2))
7962 break;
7963 }
7964 var = p + 1;
7965 }
7966 }
7967 if (RT_SUCCESS(vrc2) && *var)
7968 vrc2 = RTEnvPutEx(env, var);
7969
7970 AssertRCBreakStmt(vrc2, vrc = vrc2);
7971 }
7972 while (0);
7973
7974 if (newEnvStr != NULL)
7975 RTStrFree(newEnvStr);
7976 }
7977
7978#ifdef VBOX_WITH_QTGUI
7979 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7980 {
7981# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7982 /* Modify the base path so that we don't need to use ".." below. */
7983 RTPathStripTrailingSlash(szPath);
7984 RTPathStripFilename(szPath);
7985 sz = strlen(szPath);
7986 cmd = szPath + sz;
7987 sz = sizeof(szPath) - sz;
7988
7989#define OSX_APP_NAME "VirtualBoxVM"
7990#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7991
7992 Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7993 if ( strAppOverride.contains(".")
7994 || strAppOverride.contains("/")
7995 || strAppOverride.contains("\\")
7996 || strAppOverride.contains(":"))
7997 strAppOverride.setNull();
7998 Utf8Str strAppPath;
7999 if (!strAppOverride.isEmpty())
8000 {
8001 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
8002 Utf8Str strFullPath(szPath);
8003 strFullPath.append(strAppPath);
8004 /* there is a race, but people using this deserve the failure */
8005 if (!RTFileExists(strFullPath.c_str()))
8006 strAppOverride.setNull();
8007 }
8008 if (strAppOverride.isEmpty())
8009 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
8010 const char *VirtualBox_exe = strAppPath.c_str();
8011 AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED);
8012# else
8013 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
8014 Assert(sz >= sizeof(VirtualBox_exe));
8015# endif
8016 strcpy(cmd, VirtualBox_exe);
8017
8018 Utf8Str idStr = mData->mUuid.toString();
8019 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
8020 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8021 }
8022#else /* !VBOX_WITH_QTGUI */
8023 if (0)
8024 ;
8025#endif /* VBOX_WITH_QTGUI */
8026
8027 else
8028
8029#ifdef VBOX_WITH_VBOXSDL
8030 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
8031 {
8032 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
8033 Assert(sz >= sizeof(VBoxSDL_exe));
8034 strcpy(cmd, VBoxSDL_exe);
8035
8036 Utf8Str idStr = mData->mUuid.toString();
8037 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
8038 vrc = RTProcCreate(szPath, args, env, 0, &pid);
8039 }
8040#else /* !VBOX_WITH_VBOXSDL */
8041 if (0)
8042 ;
8043#endif /* !VBOX_WITH_VBOXSDL */
8044
8045 else
8046
8047#ifdef VBOX_WITH_HEADLESS
8048 if ( strFrontend == "headless"
8049 || strFrontend == "capture"
8050 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
8051 )
8052 {
8053 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
8054 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
8055 * and a VM works even if the server has not been installed.
8056 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
8057 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
8058 * differently in 4.0 and 3.x.
8059 */
8060 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
8061 Assert(sz >= sizeof(VBoxHeadless_exe));
8062 strcpy(cmd, VBoxHeadless_exe);
8063
8064 Utf8Str idStr = mData->mUuid.toString();
8065 /* Leave space for "--capture" arg. */
8066 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
8067 "--startvm", idStr.c_str(),
8068 "--vrde", "config",
8069 0, /* For "--capture". */
8070 0 };
8071 if (strFrontend == "capture")
8072 {
8073 unsigned pos = RT_ELEMENTS(args) - 2;
8074 args[pos] = "--capture";
8075 }
8076 vrc = RTProcCreate(szPath, args, env,
8077#ifdef RT_OS_WINDOWS
8078 RTPROC_FLAGS_NO_WINDOW
8079#else
8080 0
8081#endif
8082 , &pid);
8083 }
8084#else /* !VBOX_WITH_HEADLESS */
8085 if (0)
8086 ;
8087#endif /* !VBOX_WITH_HEADLESS */
8088 else
8089 {
8090 RTEnvDestroy(env);
8091 return setError(E_INVALIDARG,
8092 tr("Invalid frontend name: '%s'"),
8093 strFrontend.c_str());
8094 }
8095
8096 RTEnvDestroy(env);
8097
8098 if (RT_FAILURE(vrc))
8099 return setError(VBOX_E_IPRT_ERROR,
8100 tr("Could not launch a process for the machine '%s' (%Rrc)"),
8101 mUserData->s.strName.c_str(), vrc);
8102
8103 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
8104
8105 /*
8106 * Note that we don't release the lock here before calling the client,
8107 * because it doesn't need to call us back if called with a NULL argument.
8108 * Releasing the lock here is dangerous because we didn't prepare the
8109 * launch data yet, but the client we've just started may happen to be
8110 * too fast and call LockMachine() that will fail (because of PID, etc.),
8111 * so that the Machine will never get out of the Spawning session state.
8112 */
8113
8114 /* inform the session that it will be a remote one */
8115 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8116 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8117 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8118
8119 if (FAILED(rc))
8120 {
8121 /* restore the session state */
8122 mData->mSession.mState = SessionState_Unlocked;
8123 /* The failure may occur w/o any error info (from RPC), so provide one */
8124 return setError(VBOX_E_VM_ERROR,
8125 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8126 }
8127
8128 /* attach launch data to the machine */
8129 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8130 mData->mSession.mRemoteControls.push_back(aControl);
8131 mData->mSession.mProgress = aProgress;
8132 mData->mSession.mPID = pid;
8133 mData->mSession.mState = SessionState_Spawning;
8134 mData->mSession.mType = strFrontend;
8135
8136 LogFlowThisFuncLeave();
8137 return S_OK;
8138}
8139
8140/**
8141 * Returns @c true if the given session machine instance has an open direct
8142 * session (and optionally also for direct sessions which are closing) and
8143 * returns the session control machine instance if so.
8144 *
8145 * Note that when the method returns @c false, the arguments remain unchanged.
8146 *
8147 * @param aMachine Session machine object.
8148 * @param aControl Direct session control object (optional).
8149 * @param aAllowClosing If true then additionally a session which is currently
8150 * being closed will also be allowed.
8151 *
8152 * @note locks this object for reading.
8153 */
8154bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8155 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8156 bool aAllowClosing /*= false*/)
8157{
8158 AutoLimitedCaller autoCaller(this);
8159 AssertComRCReturn(autoCaller.rc(), false);
8160
8161 /* just return false for inaccessible machines */
8162 if (autoCaller.state() != Ready)
8163 return false;
8164
8165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8166
8167 if ( mData->mSession.mState == SessionState_Locked
8168 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8169 )
8170 {
8171 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8172
8173 aMachine = mData->mSession.mMachine;
8174
8175 if (aControl != NULL)
8176 *aControl = mData->mSession.mDirectControl;
8177
8178 return true;
8179 }
8180
8181 return false;
8182}
8183
8184/**
8185 * Returns @c true if the given machine has an spawning direct session.
8186 *
8187 * @note locks this object for reading.
8188 */
8189bool Machine::isSessionSpawning()
8190{
8191 AutoLimitedCaller autoCaller(this);
8192 AssertComRCReturn(autoCaller.rc(), false);
8193
8194 /* just return false for inaccessible machines */
8195 if (autoCaller.state() != Ready)
8196 return false;
8197
8198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8199
8200 if (mData->mSession.mState == SessionState_Spawning)
8201 return true;
8202
8203 return false;
8204}
8205
8206/**
8207 * Called from the client watcher thread to check for unexpected client process
8208 * death during Session_Spawning state (e.g. before it successfully opened a
8209 * direct session).
8210 *
8211 * On Win32 and on OS/2, this method is called only when we've got the
8212 * direct client's process termination notification, so it always returns @c
8213 * true.
8214 *
8215 * On other platforms, this method returns @c true if the client process is
8216 * terminated and @c false if it's still alive.
8217 *
8218 * @note Locks this object for writing.
8219 */
8220bool Machine::checkForSpawnFailure()
8221{
8222 AutoCaller autoCaller(this);
8223 if (!autoCaller.isOk())
8224 {
8225 /* nothing to do */
8226 LogFlowThisFunc(("Already uninitialized!\n"));
8227 return true;
8228 }
8229
8230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8231
8232 if (mData->mSession.mState != SessionState_Spawning)
8233 {
8234 /* nothing to do */
8235 LogFlowThisFunc(("Not spawning any more!\n"));
8236 return true;
8237 }
8238
8239 HRESULT rc = S_OK;
8240
8241 /* PID not yet initialized, skip check. */
8242 if (mData->mSession.mPID == NIL_RTPROCESS)
8243 return false;
8244
8245 RTPROCSTATUS status;
8246 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8247
8248 if (vrc != VERR_PROCESS_RUNNING)
8249 {
8250 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8251 rc = setError(E_FAIL,
8252 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8253 getName().c_str(), status.iStatus);
8254 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8255 rc = setError(E_FAIL,
8256 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8257 getName().c_str(), status.iStatus);
8258 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8259 rc = setError(E_FAIL,
8260 tr("The virtual machine '%s' has terminated abnormally"),
8261 getName().c_str(), status.iStatus);
8262 else
8263 rc = setError(E_FAIL,
8264 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8265 getName().c_str(), vrc);
8266 }
8267
8268 if (FAILED(rc))
8269 {
8270 /* Close the remote session, remove the remote control from the list
8271 * and reset session state to Closed (@note keep the code in sync with
8272 * the relevant part in LockMachine()). */
8273
8274 Assert(mData->mSession.mRemoteControls.size() == 1);
8275 if (mData->mSession.mRemoteControls.size() == 1)
8276 {
8277 ErrorInfoKeeper eik;
8278 mData->mSession.mRemoteControls.front()->Uninitialize();
8279 }
8280
8281 mData->mSession.mRemoteControls.clear();
8282 mData->mSession.mState = SessionState_Unlocked;
8283
8284 /* finalize the progress after setting the state */
8285 if (!mData->mSession.mProgress.isNull())
8286 {
8287 mData->mSession.mProgress->notifyComplete(rc);
8288 mData->mSession.mProgress.setNull();
8289 }
8290
8291 mData->mSession.mPID = NIL_RTPROCESS;
8292
8293 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8294 return true;
8295 }
8296
8297 return false;
8298}
8299
8300/**
8301 * Checks whether the machine can be registered. If so, commits and saves
8302 * all settings.
8303 *
8304 * @note Must be called from mParent's write lock. Locks this object and
8305 * children for writing.
8306 */
8307HRESULT Machine::prepareRegister()
8308{
8309 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8310
8311 AutoLimitedCaller autoCaller(this);
8312 AssertComRCReturnRC(autoCaller.rc());
8313
8314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8315
8316 /* wait for state dependents to drop to zero */
8317 ensureNoStateDependencies();
8318
8319 if (!mData->mAccessible)
8320 return setError(VBOX_E_INVALID_OBJECT_STATE,
8321 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8322 mUserData->s.strName.c_str(),
8323 mData->mUuid.toString().c_str());
8324
8325 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8326
8327 if (mData->mRegistered)
8328 return setError(VBOX_E_INVALID_OBJECT_STATE,
8329 tr("The machine '%s' with UUID {%s} is already registered"),
8330 mUserData->s.strName.c_str(),
8331 mData->mUuid.toString().c_str());
8332
8333 HRESULT rc = S_OK;
8334
8335 // Ensure the settings are saved. If we are going to be registered and
8336 // no config file exists yet, create it by calling saveSettings() too.
8337 if ( (mData->flModifications)
8338 || (!mData->pMachineConfigFile->fileExists())
8339 )
8340 {
8341 rc = saveSettings(NULL);
8342 // no need to check whether VirtualBox.xml needs saving too since
8343 // we can't have a machine XML file rename pending
8344 if (FAILED(rc)) return rc;
8345 }
8346
8347 /* more config checking goes here */
8348
8349 if (SUCCEEDED(rc))
8350 {
8351 /* we may have had implicit modifications we want to fix on success */
8352 commit();
8353
8354 mData->mRegistered = true;
8355 }
8356 else
8357 {
8358 /* we may have had implicit modifications we want to cancel on failure*/
8359 rollback(false /* aNotify */);
8360 }
8361
8362 return rc;
8363}
8364
8365/**
8366 * Increases the number of objects dependent on the machine state or on the
8367 * registered state. Guarantees that these two states will not change at least
8368 * until #releaseStateDependency() is called.
8369 *
8370 * Depending on the @a aDepType value, additional state checks may be made.
8371 * These checks will set extended error info on failure. See
8372 * #checkStateDependency() for more info.
8373 *
8374 * If this method returns a failure, the dependency is not added and the caller
8375 * is not allowed to rely on any particular machine state or registration state
8376 * value and may return the failed result code to the upper level.
8377 *
8378 * @param aDepType Dependency type to add.
8379 * @param aState Current machine state (NULL if not interested).
8380 * @param aRegistered Current registered state (NULL if not interested).
8381 *
8382 * @note Locks this object for writing.
8383 */
8384HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8385 MachineState_T *aState /* = NULL */,
8386 BOOL *aRegistered /* = NULL */)
8387{
8388 AutoCaller autoCaller(this);
8389 AssertComRCReturnRC(autoCaller.rc());
8390
8391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8392
8393 HRESULT rc = checkStateDependency(aDepType);
8394 if (FAILED(rc)) return rc;
8395
8396 {
8397 if (mData->mMachineStateChangePending != 0)
8398 {
8399 /* ensureNoStateDependencies() is waiting for state dependencies to
8400 * drop to zero so don't add more. It may make sense to wait a bit
8401 * and retry before reporting an error (since the pending state
8402 * transition should be really quick) but let's just assert for
8403 * now to see if it ever happens on practice. */
8404
8405 AssertFailed();
8406
8407 return setError(E_ACCESSDENIED,
8408 tr("Machine state change is in progress. Please retry the operation later."));
8409 }
8410
8411 ++mData->mMachineStateDeps;
8412 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8413 }
8414
8415 if (aState)
8416 *aState = mData->mMachineState;
8417 if (aRegistered)
8418 *aRegistered = mData->mRegistered;
8419
8420 return S_OK;
8421}
8422
8423/**
8424 * Decreases the number of objects dependent on the machine state.
8425 * Must always complete the #addStateDependency() call after the state
8426 * dependency is no more necessary.
8427 */
8428void Machine::releaseStateDependency()
8429{
8430 AutoCaller autoCaller(this);
8431 AssertComRCReturnVoid(autoCaller.rc());
8432
8433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8434
8435 /* releaseStateDependency() w/o addStateDependency()? */
8436 AssertReturnVoid(mData->mMachineStateDeps != 0);
8437 -- mData->mMachineStateDeps;
8438
8439 if (mData->mMachineStateDeps == 0)
8440 {
8441 /* inform ensureNoStateDependencies() that there are no more deps */
8442 if (mData->mMachineStateChangePending != 0)
8443 {
8444 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8445 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8446 }
8447 }
8448}
8449
8450Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8451{
8452 /* start with nothing found */
8453 Utf8Str strResult("");
8454
8455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8456
8457 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8458 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8459 // found:
8460 strResult = it->second; // source is a Utf8Str
8461
8462 return strResult;
8463}
8464
8465// protected methods
8466/////////////////////////////////////////////////////////////////////////////
8467
8468/**
8469 * Performs machine state checks based on the @a aDepType value. If a check
8470 * fails, this method will set extended error info, otherwise it will return
8471 * S_OK. It is supposed, that on failure, the caller will immediately return
8472 * the return value of this method to the upper level.
8473 *
8474 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8475 *
8476 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8477 * current state of this machine object allows to change settings of the
8478 * machine (i.e. the machine is not registered, or registered but not running
8479 * and not saved). It is useful to call this method from Machine setters
8480 * before performing any change.
8481 *
8482 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8483 * as for MutableStateDep except that if the machine is saved, S_OK is also
8484 * returned. This is useful in setters which allow changing machine
8485 * properties when it is in the saved state.
8486 *
8487 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8488 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8489 * Aborted).
8490 *
8491 * @param aDepType Dependency type to check.
8492 *
8493 * @note Non Machine based classes should use #addStateDependency() and
8494 * #releaseStateDependency() methods or the smart AutoStateDependency
8495 * template.
8496 *
8497 * @note This method must be called from under this object's read or write
8498 * lock.
8499 */
8500HRESULT Machine::checkStateDependency(StateDependency aDepType)
8501{
8502 switch (aDepType)
8503 {
8504 case AnyStateDep:
8505 {
8506 break;
8507 }
8508 case MutableStateDep:
8509 {
8510 if ( mData->mRegistered
8511 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8512 || ( mData->mMachineState != MachineState_Paused
8513 && mData->mMachineState != MachineState_Running
8514 && mData->mMachineState != MachineState_Aborted
8515 && mData->mMachineState != MachineState_Teleported
8516 && mData->mMachineState != MachineState_PoweredOff
8517 )
8518 )
8519 )
8520 return setError(VBOX_E_INVALID_VM_STATE,
8521 tr("The machine is not mutable (state is %s)"),
8522 Global::stringifyMachineState(mData->mMachineState));
8523 break;
8524 }
8525 case MutableOrSavedStateDep:
8526 {
8527 if ( mData->mRegistered
8528 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8529 || ( mData->mMachineState != MachineState_Paused
8530 && mData->mMachineState != MachineState_Running
8531 && mData->mMachineState != MachineState_Aborted
8532 && mData->mMachineState != MachineState_Teleported
8533 && mData->mMachineState != MachineState_Saved
8534 && mData->mMachineState != MachineState_PoweredOff
8535 )
8536 )
8537 )
8538 return setError(VBOX_E_INVALID_VM_STATE,
8539 tr("The machine is not mutable (state is %s)"),
8540 Global::stringifyMachineState(mData->mMachineState));
8541 break;
8542 }
8543 case OfflineStateDep:
8544 {
8545 if ( mData->mRegistered
8546 && ( !isSessionMachine()
8547 || ( mData->mMachineState != MachineState_PoweredOff
8548 && mData->mMachineState != MachineState_Saved
8549 && mData->mMachineState != MachineState_Aborted
8550 && mData->mMachineState != MachineState_Teleported
8551 )
8552 )
8553 )
8554 return setError(VBOX_E_INVALID_VM_STATE,
8555 tr("The machine is not offline (state is %s)"),
8556 Global::stringifyMachineState(mData->mMachineState));
8557 break;
8558 }
8559 }
8560
8561 return S_OK;
8562}
8563
8564/**
8565 * Helper to initialize all associated child objects and allocate data
8566 * structures.
8567 *
8568 * This method must be called as a part of the object's initialization procedure
8569 * (usually done in the #init() method).
8570 *
8571 * @note Must be called only from #init() or from #registeredInit().
8572 */
8573HRESULT Machine::initDataAndChildObjects()
8574{
8575 AutoCaller autoCaller(this);
8576 AssertComRCReturnRC(autoCaller.rc());
8577 AssertComRCReturn(autoCaller.state() == InInit ||
8578 autoCaller.state() == Limited, E_FAIL);
8579
8580 AssertReturn(!mData->mAccessible, E_FAIL);
8581
8582 /* allocate data structures */
8583 mSSData.allocate();
8584 mUserData.allocate();
8585 mHWData.allocate();
8586 mMediaData.allocate();
8587 mStorageControllers.allocate();
8588 mUSBControllers.allocate();
8589
8590 /* initialize mOSTypeId */
8591 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8592
8593 /* create associated BIOS settings object */
8594 unconst(mBIOSSettings).createObject();
8595 mBIOSSettings->init(this);
8596
8597 /* create an associated VRDE object (default is disabled) */
8598 unconst(mVRDEServer).createObject();
8599 mVRDEServer->init(this);
8600
8601 /* create associated serial port objects */
8602 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8603 {
8604 unconst(mSerialPorts[slot]).createObject();
8605 mSerialPorts[slot]->init(this, slot);
8606 }
8607
8608 /* create associated parallel port objects */
8609 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8610 {
8611 unconst(mParallelPorts[slot]).createObject();
8612 mParallelPorts[slot]->init(this, slot);
8613 }
8614
8615 /* create the audio adapter object (always present, default is disabled) */
8616 unconst(mAudioAdapter).createObject();
8617 mAudioAdapter->init(this);
8618
8619 /* create the USB device filters object (always present) */
8620 unconst(mUSBDeviceFilters).createObject();
8621 mUSBDeviceFilters->init(this);
8622
8623 /* create associated network adapter objects */
8624 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8625 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8626 {
8627 unconst(mNetworkAdapters[slot]).createObject();
8628 mNetworkAdapters[slot]->init(this, slot);
8629 }
8630
8631 /* create the bandwidth control */
8632 unconst(mBandwidthControl).createObject();
8633 mBandwidthControl->init(this);
8634
8635 return S_OK;
8636}
8637
8638/**
8639 * Helper to uninitialize all associated child objects and to free all data
8640 * structures.
8641 *
8642 * This method must be called as a part of the object's uninitialization
8643 * procedure (usually done in the #uninit() method).
8644 *
8645 * @note Must be called only from #uninit() or from #registeredInit().
8646 */
8647void Machine::uninitDataAndChildObjects()
8648{
8649 AutoCaller autoCaller(this);
8650 AssertComRCReturnVoid(autoCaller.rc());
8651 AssertComRCReturnVoid( autoCaller.state() == InUninit
8652 || autoCaller.state() == Limited);
8653
8654 /* tell all our other child objects we've been uninitialized */
8655 if (mBandwidthControl)
8656 {
8657 mBandwidthControl->uninit();
8658 unconst(mBandwidthControl).setNull();
8659 }
8660
8661 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8662 {
8663 if (mNetworkAdapters[slot])
8664 {
8665 mNetworkAdapters[slot]->uninit();
8666 unconst(mNetworkAdapters[slot]).setNull();
8667 }
8668 }
8669
8670 if (mUSBDeviceFilters)
8671 {
8672 mUSBDeviceFilters->uninit();
8673 unconst(mUSBDeviceFilters).setNull();
8674 }
8675
8676 if (mAudioAdapter)
8677 {
8678 mAudioAdapter->uninit();
8679 unconst(mAudioAdapter).setNull();
8680 }
8681
8682 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8683 {
8684 if (mParallelPorts[slot])
8685 {
8686 mParallelPorts[slot]->uninit();
8687 unconst(mParallelPorts[slot]).setNull();
8688 }
8689 }
8690
8691 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8692 {
8693 if (mSerialPorts[slot])
8694 {
8695 mSerialPorts[slot]->uninit();
8696 unconst(mSerialPorts[slot]).setNull();
8697 }
8698 }
8699
8700 if (mVRDEServer)
8701 {
8702 mVRDEServer->uninit();
8703 unconst(mVRDEServer).setNull();
8704 }
8705
8706 if (mBIOSSettings)
8707 {
8708 mBIOSSettings->uninit();
8709 unconst(mBIOSSettings).setNull();
8710 }
8711
8712 /* Deassociate media (only when a real Machine or a SnapshotMachine
8713 * instance is uninitialized; SessionMachine instances refer to real
8714 * Machine media). This is necessary for a clean re-initialization of
8715 * the VM after successfully re-checking the accessibility state. Note
8716 * that in case of normal Machine or SnapshotMachine uninitialization (as
8717 * a result of unregistering or deleting the snapshot), outdated media
8718 * attachments will already be uninitialized and deleted, so this
8719 * code will not affect them. */
8720 if ( !!mMediaData
8721 && (!isSessionMachine())
8722 )
8723 {
8724 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8725 it != mMediaData->mAttachments.end();
8726 ++it)
8727 {
8728 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8729 if (pMedium.isNull())
8730 continue;
8731 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8732 AssertComRC(rc);
8733 }
8734 }
8735
8736 if (!isSessionMachine() && !isSnapshotMachine())
8737 {
8738 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8739 if (mData->mFirstSnapshot)
8740 {
8741 // snapshots tree is protected by machine write lock; strictly
8742 // this isn't necessary here since we're deleting the entire
8743 // machine, but otherwise we assert in Snapshot::uninit()
8744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8745 mData->mFirstSnapshot->uninit();
8746 mData->mFirstSnapshot.setNull();
8747 }
8748
8749 mData->mCurrentSnapshot.setNull();
8750 }
8751
8752 /* free data structures (the essential mData structure is not freed here
8753 * since it may be still in use) */
8754 mMediaData.free();
8755 mStorageControllers.free();
8756 mUSBControllers.free();
8757 mHWData.free();
8758 mUserData.free();
8759 mSSData.free();
8760}
8761
8762/**
8763 * Returns a pointer to the Machine object for this machine that acts like a
8764 * parent for complex machine data objects such as shared folders, etc.
8765 *
8766 * For primary Machine objects and for SnapshotMachine objects, returns this
8767 * object's pointer itself. For SessionMachine objects, returns the peer
8768 * (primary) machine pointer.
8769 */
8770Machine* Machine::getMachine()
8771{
8772 if (isSessionMachine())
8773 return (Machine*)mPeer;
8774 return this;
8775}
8776
8777/**
8778 * Makes sure that there are no machine state dependents. If necessary, waits
8779 * for the number of dependents to drop to zero.
8780 *
8781 * Make sure this method is called from under this object's write lock to
8782 * guarantee that no new dependents may be added when this method returns
8783 * control to the caller.
8784 *
8785 * @note Locks this object for writing. The lock will be released while waiting
8786 * (if necessary).
8787 *
8788 * @warning To be used only in methods that change the machine state!
8789 */
8790void Machine::ensureNoStateDependencies()
8791{
8792 AssertReturnVoid(isWriteLockOnCurrentThread());
8793
8794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8795
8796 /* Wait for all state dependents if necessary */
8797 if (mData->mMachineStateDeps != 0)
8798 {
8799 /* lazy semaphore creation */
8800 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8801 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8802
8803 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8804 mData->mMachineStateDeps));
8805
8806 ++mData->mMachineStateChangePending;
8807
8808 /* reset the semaphore before waiting, the last dependent will signal
8809 * it */
8810 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8811
8812 alock.release();
8813
8814 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8815
8816 alock.acquire();
8817
8818 -- mData->mMachineStateChangePending;
8819 }
8820}
8821
8822/**
8823 * Changes the machine state and informs callbacks.
8824 *
8825 * This method is not intended to fail so it either returns S_OK or asserts (and
8826 * returns a failure).
8827 *
8828 * @note Locks this object for writing.
8829 */
8830HRESULT Machine::setMachineState(MachineState_T aMachineState)
8831{
8832 LogFlowThisFuncEnter();
8833 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8834
8835 AutoCaller autoCaller(this);
8836 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8837
8838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8839
8840 /* wait for state dependents to drop to zero */
8841 ensureNoStateDependencies();
8842
8843 if (mData->mMachineState != aMachineState)
8844 {
8845 mData->mMachineState = aMachineState;
8846
8847 RTTimeNow(&mData->mLastStateChange);
8848
8849 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8850 }
8851
8852 LogFlowThisFuncLeave();
8853 return S_OK;
8854}
8855
8856/**
8857 * Searches for a shared folder with the given logical name
8858 * in the collection of shared folders.
8859 *
8860 * @param aName logical name of the shared folder
8861 * @param aSharedFolder where to return the found object
8862 * @param aSetError whether to set the error info if the folder is
8863 * not found
8864 * @return
8865 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8866 *
8867 * @note
8868 * must be called from under the object's lock!
8869 */
8870HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8871 ComObjPtr<SharedFolder> &aSharedFolder,
8872 bool aSetError /* = false */)
8873{
8874 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8875 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8876 it != mHWData->mSharedFolders.end();
8877 ++it)
8878 {
8879 SharedFolder *pSF = *it;
8880 AutoCaller autoCaller(pSF);
8881 if (pSF->getName() == aName)
8882 {
8883 aSharedFolder = pSF;
8884 rc = S_OK;
8885 break;
8886 }
8887 }
8888
8889 if (aSetError && FAILED(rc))
8890 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8891
8892 return rc;
8893}
8894
8895/**
8896 * Initializes all machine instance data from the given settings structures
8897 * from XML. The exception is the machine UUID which needs special handling
8898 * depending on the caller's use case, so the caller needs to set that herself.
8899 *
8900 * This gets called in several contexts during machine initialization:
8901 *
8902 * -- When machine XML exists on disk already and needs to be loaded into memory,
8903 * for example, from registeredInit() to load all registered machines on
8904 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8905 * attached to the machine should be part of some media registry already.
8906 *
8907 * -- During OVF import, when a machine config has been constructed from an
8908 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8909 * ensure that the media listed as attachments in the config (which have
8910 * been imported from the OVF) receive the correct registry ID.
8911 *
8912 * -- During VM cloning.
8913 *
8914 * @param config Machine settings from XML.
8915 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8916 * @return
8917 */
8918HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8919 const Guid *puuidRegistry)
8920{
8921 // copy name, description, OS type, teleporter, UTC etc.
8922 mUserData->s = config.machineUserData;
8923
8924 // Decode the Icon overide data from config userdata and set onto Machine.
8925 #define DECODE_STR_MAX _1M
8926 const char* pszStr = config.machineUserData.ovIcon.c_str();
8927 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8928 if (cbOut > DECODE_STR_MAX)
8929 return setError(E_FAIL,
8930 tr("Icon Data too long.'%d' > '%d'"),
8931 cbOut,
8932 DECODE_STR_MAX);
8933 com::SafeArray<BYTE> iconByte(cbOut);
8934 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8935 if (FAILED(rc))
8936 return setError(E_FAIL,
8937 tr("Failure to Decode Icon Data. '%s' (%d)"),
8938 pszStr,
8939 rc);
8940 mUserData->mIcon.resize(iconByte.size());
8941 memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size());
8942
8943 // look up the object by Id to check it is valid
8944 ComPtr<IGuestOSType> guestOSType;
8945 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8946 guestOSType.asOutParam());
8947 if (FAILED(rc)) return rc;
8948
8949 // stateFile (optional)
8950 if (config.strStateFile.isEmpty())
8951 mSSData->strStateFilePath.setNull();
8952 else
8953 {
8954 Utf8Str stateFilePathFull(config.strStateFile);
8955 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8956 if (RT_FAILURE(vrc))
8957 return setError(E_FAIL,
8958 tr("Invalid saved state file path '%s' (%Rrc)"),
8959 config.strStateFile.c_str(),
8960 vrc);
8961 mSSData->strStateFilePath = stateFilePathFull;
8962 }
8963
8964 // snapshot folder needs special processing so set it again
8965 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8966 if (FAILED(rc)) return rc;
8967
8968 /* Copy the extra data items (Not in any case config is already the same as
8969 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8970 * make sure the extra data map is copied). */
8971 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8972
8973 /* currentStateModified (optional, default is true) */
8974 mData->mCurrentStateModified = config.fCurrentStateModified;
8975
8976 mData->mLastStateChange = config.timeLastStateChange;
8977
8978 /*
8979 * note: all mUserData members must be assigned prior this point because
8980 * we need to commit changes in order to let mUserData be shared by all
8981 * snapshot machine instances.
8982 */
8983 mUserData.commitCopy();
8984
8985 // machine registry, if present (must be loaded before snapshots)
8986 if (config.canHaveOwnMediaRegistry())
8987 {
8988 // determine machine folder
8989 Utf8Str strMachineFolder = getSettingsFileFull();
8990 strMachineFolder.stripFilename();
8991 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8992 config.mediaRegistry,
8993 strMachineFolder);
8994 if (FAILED(rc)) return rc;
8995 }
8996
8997 /* Snapshot node (optional) */
8998 size_t cRootSnapshots;
8999 if ((cRootSnapshots = config.llFirstSnapshot.size()))
9000 {
9001 // there must be only one root snapshot
9002 Assert(cRootSnapshots == 1);
9003
9004 const settings::Snapshot &snap = config.llFirstSnapshot.front();
9005
9006 rc = loadSnapshot(snap,
9007 config.uuidCurrentSnapshot,
9008 NULL); // no parent == first snapshot
9009 if (FAILED(rc)) return rc;
9010 }
9011
9012 // hardware data
9013 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9014 if (FAILED(rc)) return rc;
9015
9016 // load storage controllers
9017 rc = loadStorageControllers(config.storageMachine,
9018 puuidRegistry,
9019 NULL /* puuidSnapshot */);
9020 if (FAILED(rc)) return rc;
9021
9022 /*
9023 * NOTE: the assignment below must be the last thing to do,
9024 * otherwise it will be not possible to change the settings
9025 * somewhere in the code above because all setters will be
9026 * blocked by checkStateDependency(MutableStateDep).
9027 */
9028
9029 /* set the machine state to Aborted or Saved when appropriate */
9030 if (config.fAborted)
9031 {
9032 mSSData->strStateFilePath.setNull();
9033
9034 /* no need to use setMachineState() during init() */
9035 mData->mMachineState = MachineState_Aborted;
9036 }
9037 else if (!mSSData->strStateFilePath.isEmpty())
9038 {
9039 /* no need to use setMachineState() during init() */
9040 mData->mMachineState = MachineState_Saved;
9041 }
9042
9043 // after loading settings, we are no longer different from the XML on disk
9044 mData->flModifications = 0;
9045
9046 return S_OK;
9047}
9048
9049/**
9050 * Recursively loads all snapshots starting from the given.
9051 *
9052 * @param aNode <Snapshot> node.
9053 * @param aCurSnapshotId Current snapshot ID from the settings file.
9054 * @param aParentSnapshot Parent snapshot.
9055 */
9056HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
9057 const Guid &aCurSnapshotId,
9058 Snapshot *aParentSnapshot)
9059{
9060 AssertReturn(!isSnapshotMachine(), E_FAIL);
9061 AssertReturn(!isSessionMachine(), E_FAIL);
9062
9063 HRESULT rc = S_OK;
9064
9065 Utf8Str strStateFile;
9066 if (!data.strStateFile.isEmpty())
9067 {
9068 /* optional */
9069 strStateFile = data.strStateFile;
9070 int vrc = calculateFullPath(strStateFile, strStateFile);
9071 if (RT_FAILURE(vrc))
9072 return setError(E_FAIL,
9073 tr("Invalid saved state file path '%s' (%Rrc)"),
9074 strStateFile.c_str(),
9075 vrc);
9076 }
9077
9078 /* create a snapshot machine object */
9079 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9080 pSnapshotMachine.createObject();
9081 rc = pSnapshotMachine->initFromSettings(this,
9082 data.hardware,
9083 &data.debugging,
9084 &data.autostart,
9085 data.storage,
9086 data.uuid.ref(),
9087 strStateFile);
9088 if (FAILED(rc)) return rc;
9089
9090 /* create a snapshot object */
9091 ComObjPtr<Snapshot> pSnapshot;
9092 pSnapshot.createObject();
9093 /* initialize the snapshot */
9094 rc = pSnapshot->init(mParent, // VirtualBox object
9095 data.uuid,
9096 data.strName,
9097 data.strDescription,
9098 data.timestamp,
9099 pSnapshotMachine,
9100 aParentSnapshot);
9101 if (FAILED(rc)) return rc;
9102
9103 /* memorize the first snapshot if necessary */
9104 if (!mData->mFirstSnapshot)
9105 mData->mFirstSnapshot = pSnapshot;
9106
9107 /* memorize the current snapshot when appropriate */
9108 if ( !mData->mCurrentSnapshot
9109 && pSnapshot->getId() == aCurSnapshotId
9110 )
9111 mData->mCurrentSnapshot = pSnapshot;
9112
9113 // now create the children
9114 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9115 it != data.llChildSnapshots.end();
9116 ++it)
9117 {
9118 const settings::Snapshot &childData = *it;
9119 // recurse
9120 rc = loadSnapshot(childData,
9121 aCurSnapshotId,
9122 pSnapshot); // parent = the one we created above
9123 if (FAILED(rc)) return rc;
9124 }
9125
9126 return rc;
9127}
9128
9129/**
9130 * Loads settings into mHWData.
9131 *
9132 * @param data Reference to the hardware settings.
9133 * @param pDbg Pointer to the debugging settings.
9134 * @param pAutostart Pointer to the autostart settings.
9135 */
9136HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9137 const settings::Autostart *pAutostart)
9138{
9139 AssertReturn(!isSessionMachine(), E_FAIL);
9140
9141 HRESULT rc = S_OK;
9142
9143 try
9144 {
9145 /* The hardware version attribute (optional). */
9146 mHWData->mHWVersion = data.strVersion;
9147 mHWData->mHardwareUUID = data.uuid;
9148
9149 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9150 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9151 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9152 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9153 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9154 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9155 mHWData->mPAEEnabled = data.fPAE;
9156 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9157 mHWData->mLongMode = data.enmLongMode;
9158 mHWData->mCPUCount = data.cCPUs;
9159 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9160 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9161
9162 // cpu
9163 if (mHWData->mCPUHotPlugEnabled)
9164 {
9165 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9166 it != data.llCpus.end();
9167 ++it)
9168 {
9169 const settings::Cpu &cpu = *it;
9170
9171 mHWData->mCPUAttached[cpu.ulId] = true;
9172 }
9173 }
9174
9175 // cpuid leafs
9176 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9177 it != data.llCpuIdLeafs.end();
9178 ++it)
9179 {
9180 const settings::CpuIdLeaf &leaf = *it;
9181
9182 switch (leaf.ulId)
9183 {
9184 case 0x0:
9185 case 0x1:
9186 case 0x2:
9187 case 0x3:
9188 case 0x4:
9189 case 0x5:
9190 case 0x6:
9191 case 0x7:
9192 case 0x8:
9193 case 0x9:
9194 case 0xA:
9195 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9196 break;
9197
9198 case 0x80000000:
9199 case 0x80000001:
9200 case 0x80000002:
9201 case 0x80000003:
9202 case 0x80000004:
9203 case 0x80000005:
9204 case 0x80000006:
9205 case 0x80000007:
9206 case 0x80000008:
9207 case 0x80000009:
9208 case 0x8000000A:
9209 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9210 break;
9211
9212 default:
9213 /* just ignore */
9214 break;
9215 }
9216 }
9217
9218 mHWData->mMemorySize = data.ulMemorySizeMB;
9219 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9220
9221 // boot order
9222 for (size_t i = 0;
9223 i < RT_ELEMENTS(mHWData->mBootOrder);
9224 i++)
9225 {
9226 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9227 if (it == data.mapBootOrder.end())
9228 mHWData->mBootOrder[i] = DeviceType_Null;
9229 else
9230 mHWData->mBootOrder[i] = it->second;
9231 }
9232
9233 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9234 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9235 mHWData->mMonitorCount = data.cMonitors;
9236 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9237 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9238 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9239 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9240 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9241 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9242 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9243 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9244 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9245 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9246 if (!data.strVideoCaptureFile.isEmpty())
9247 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9248 else
9249 mHWData->mVideoCaptureFile.setNull();
9250 mHWData->mFirmwareType = data.firmwareType;
9251 mHWData->mPointingHIDType = data.pointingHIDType;
9252 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9253 mHWData->mChipsetType = data.chipsetType;
9254 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9255 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9256 mHWData->mHPETEnabled = data.fHPETEnabled;
9257
9258 /* VRDEServer */
9259 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9260 if (FAILED(rc)) return rc;
9261
9262 /* BIOS */
9263 rc = mBIOSSettings->loadSettings(data.biosSettings);
9264 if (FAILED(rc)) return rc;
9265
9266 // Bandwidth control (must come before network adapters)
9267 rc = mBandwidthControl->loadSettings(data.ioSettings);
9268 if (FAILED(rc)) return rc;
9269
9270 /* Shared folders */
9271 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
9272 it != data.usbSettings.llUSBControllers.end();
9273 ++it)
9274 {
9275 const settings::USBController &settingsCtrl = *it;
9276 ComObjPtr<USBController> newCtrl;
9277
9278 newCtrl.createObject();
9279 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9280 mUSBControllers->push_back(newCtrl);
9281 }
9282
9283 /* USB device filters */
9284 rc = mUSBDeviceFilters->loadSettings(data.usbSettings);
9285 if (FAILED(rc)) return rc;
9286
9287 // network adapters
9288 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9289 uint32_t oldCount = mNetworkAdapters.size();
9290 if (newCount > oldCount)
9291 {
9292 mNetworkAdapters.resize(newCount);
9293 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9294 {
9295 unconst(mNetworkAdapters[slot]).createObject();
9296 mNetworkAdapters[slot]->init(this, slot);
9297 }
9298 }
9299 else if (newCount < oldCount)
9300 mNetworkAdapters.resize(newCount);
9301 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9302 it != data.llNetworkAdapters.end();
9303 ++it)
9304 {
9305 const settings::NetworkAdapter &nic = *it;
9306
9307 /* slot unicity is guaranteed by XML Schema */
9308 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9309 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9310 if (FAILED(rc)) return rc;
9311 }
9312
9313 // serial ports
9314 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9315 it != data.llSerialPorts.end();
9316 ++it)
9317 {
9318 const settings::SerialPort &s = *it;
9319
9320 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9321 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9322 if (FAILED(rc)) return rc;
9323 }
9324
9325 // parallel ports (optional)
9326 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9327 it != data.llParallelPorts.end();
9328 ++it)
9329 {
9330 const settings::ParallelPort &p = *it;
9331
9332 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9333 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9334 if (FAILED(rc)) return rc;
9335 }
9336
9337 /* AudioAdapter */
9338 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9339 if (FAILED(rc)) return rc;
9340
9341 /* Shared folders */
9342 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9343 it != data.llSharedFolders.end();
9344 ++it)
9345 {
9346 const settings::SharedFolder &sf = *it;
9347
9348 ComObjPtr<SharedFolder> sharedFolder;
9349 /* Check for double entries. Not allowed! */
9350 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9351 if (SUCCEEDED(rc))
9352 return setError(VBOX_E_OBJECT_IN_USE,
9353 tr("Shared folder named '%s' already exists"),
9354 sf.strName.c_str());
9355
9356 /* Create the new shared folder. Don't break on error. This will be
9357 * reported when the machine starts. */
9358 sharedFolder.createObject();
9359 rc = sharedFolder->init(getMachine(),
9360 sf.strName,
9361 sf.strHostPath,
9362 RT_BOOL(sf.fWritable),
9363 RT_BOOL(sf.fAutoMount),
9364 false /* fFailOnError */);
9365 if (FAILED(rc)) return rc;
9366 mHWData->mSharedFolders.push_back(sharedFolder);
9367 }
9368
9369 // Clipboard
9370 mHWData->mClipboardMode = data.clipboardMode;
9371
9372 // drag'n'drop
9373 mHWData->mDragAndDropMode = data.dragAndDropMode;
9374
9375 // guest settings
9376 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9377
9378 // IO settings
9379 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9380 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9381
9382 // Host PCI devices
9383 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9384 it != data.pciAttachments.end();
9385 ++it)
9386 {
9387 const settings::HostPCIDeviceAttachment &hpda = *it;
9388 ComObjPtr<PCIDeviceAttachment> pda;
9389
9390 pda.createObject();
9391 pda->loadSettings(this, hpda);
9392 mHWData->mPCIDeviceAssignments.push_back(pda);
9393 }
9394
9395 /*
9396 * (The following isn't really real hardware, but it lives in HWData
9397 * for reasons of convenience.)
9398 */
9399
9400#ifdef VBOX_WITH_GUEST_PROPS
9401 /* Guest properties (optional) */
9402 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9403 it != data.llGuestProperties.end();
9404 ++it)
9405 {
9406 const settings::GuestProperty &prop = *it;
9407 uint32_t fFlags = guestProp::NILFLAG;
9408 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9409 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9410 mHWData->mGuestProperties[prop.strName] = property;
9411 }
9412
9413 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9414#endif /* VBOX_WITH_GUEST_PROPS defined */
9415
9416 rc = loadDebugging(pDbg);
9417 if (FAILED(rc))
9418 return rc;
9419
9420 mHWData->mAutostart = *pAutostart;
9421
9422 /* default frontend */
9423 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9424 }
9425 catch(std::bad_alloc &)
9426 {
9427 return E_OUTOFMEMORY;
9428 }
9429
9430 AssertComRC(rc);
9431 return rc;
9432}
9433
9434/**
9435 * Called from Machine::loadHardware() to load the debugging settings of the
9436 * machine.
9437 *
9438 * @param pDbg Pointer to the settings.
9439 */
9440HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9441{
9442 mHWData->mDebugging = *pDbg;
9443 /* no more processing currently required, this will probably change. */
9444 return S_OK;
9445}
9446
9447/**
9448 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9449 *
9450 * @param data
9451 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9452 * @param puuidSnapshot
9453 * @return
9454 */
9455HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9456 const Guid *puuidRegistry,
9457 const Guid *puuidSnapshot)
9458{
9459 AssertReturn(!isSessionMachine(), E_FAIL);
9460
9461 HRESULT rc = S_OK;
9462
9463 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9464 it != data.llStorageControllers.end();
9465 ++it)
9466 {
9467 const settings::StorageController &ctlData = *it;
9468
9469 ComObjPtr<StorageController> pCtl;
9470 /* Try to find one with the name first. */
9471 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9472 if (SUCCEEDED(rc))
9473 return setError(VBOX_E_OBJECT_IN_USE,
9474 tr("Storage controller named '%s' already exists"),
9475 ctlData.strName.c_str());
9476
9477 pCtl.createObject();
9478 rc = pCtl->init(this,
9479 ctlData.strName,
9480 ctlData.storageBus,
9481 ctlData.ulInstance,
9482 ctlData.fBootable);
9483 if (FAILED(rc)) return rc;
9484
9485 mStorageControllers->push_back(pCtl);
9486
9487 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9488 if (FAILED(rc)) return rc;
9489
9490 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9491 if (FAILED(rc)) return rc;
9492
9493 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9494 if (FAILED(rc)) return rc;
9495
9496 /* Set IDE emulation settings (only for AHCI controller). */
9497 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9498 {
9499 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9500 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9501 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9502 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9503 )
9504 return rc;
9505 }
9506
9507 /* Load the attached devices now. */
9508 rc = loadStorageDevices(pCtl,
9509 ctlData,
9510 puuidRegistry,
9511 puuidSnapshot);
9512 if (FAILED(rc)) return rc;
9513 }
9514
9515 return S_OK;
9516}
9517
9518/**
9519 * Called from loadStorageControllers for a controller's devices.
9520 *
9521 * @param aStorageController
9522 * @param data
9523 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9524 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9525 * @return
9526 */
9527HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9528 const settings::StorageController &data,
9529 const Guid *puuidRegistry,
9530 const Guid *puuidSnapshot)
9531{
9532 HRESULT rc = S_OK;
9533
9534 /* paranoia: detect duplicate attachments */
9535 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9536 it != data.llAttachedDevices.end();
9537 ++it)
9538 {
9539 const settings::AttachedDevice &ad = *it;
9540
9541 for (settings::AttachedDevicesList::const_iterator it2 = it;
9542 it2 != data.llAttachedDevices.end();
9543 ++it2)
9544 {
9545 if (it == it2)
9546 continue;
9547
9548 const settings::AttachedDevice &ad2 = *it2;
9549
9550 if ( ad.lPort == ad2.lPort
9551 && ad.lDevice == ad2.lDevice)
9552 {
9553 return setError(E_FAIL,
9554 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9555 aStorageController->getName().c_str(),
9556 ad.lPort,
9557 ad.lDevice,
9558 mUserData->s.strName.c_str());
9559 }
9560 }
9561 }
9562
9563 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9564 it != data.llAttachedDevices.end();
9565 ++it)
9566 {
9567 const settings::AttachedDevice &dev = *it;
9568 ComObjPtr<Medium> medium;
9569
9570 switch (dev.deviceType)
9571 {
9572 case DeviceType_Floppy:
9573 case DeviceType_DVD:
9574 if (dev.strHostDriveSrc.isNotEmpty())
9575 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9576 else
9577 rc = mParent->findRemoveableMedium(dev.deviceType,
9578 dev.uuid,
9579 false /* fRefresh */,
9580 false /* aSetError */,
9581 medium);
9582 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9583 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9584 rc = S_OK;
9585 break;
9586
9587 case DeviceType_HardDisk:
9588 {
9589 /* find a hard disk by UUID */
9590 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9591 if (FAILED(rc))
9592 {
9593 if (isSnapshotMachine())
9594 {
9595 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9596 // so the user knows that the bad disk is in a snapshot somewhere
9597 com::ErrorInfo info;
9598 return setError(E_FAIL,
9599 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9600 puuidSnapshot->raw(),
9601 info.getText().raw());
9602 }
9603 else
9604 return rc;
9605 }
9606
9607 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9608
9609 if (medium->getType() == MediumType_Immutable)
9610 {
9611 if (isSnapshotMachine())
9612 return setError(E_FAIL,
9613 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9614 "of the virtual machine '%s' ('%s')"),
9615 medium->getLocationFull().c_str(),
9616 dev.uuid.raw(),
9617 puuidSnapshot->raw(),
9618 mUserData->s.strName.c_str(),
9619 mData->m_strConfigFileFull.c_str());
9620
9621 return setError(E_FAIL,
9622 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9623 medium->getLocationFull().c_str(),
9624 dev.uuid.raw(),
9625 mUserData->s.strName.c_str(),
9626 mData->m_strConfigFileFull.c_str());
9627 }
9628
9629 if (medium->getType() == MediumType_MultiAttach)
9630 {
9631 if (isSnapshotMachine())
9632 return setError(E_FAIL,
9633 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9634 "of the virtual machine '%s' ('%s')"),
9635 medium->getLocationFull().c_str(),
9636 dev.uuid.raw(),
9637 puuidSnapshot->raw(),
9638 mUserData->s.strName.c_str(),
9639 mData->m_strConfigFileFull.c_str());
9640
9641 return setError(E_FAIL,
9642 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9643 medium->getLocationFull().c_str(),
9644 dev.uuid.raw(),
9645 mUserData->s.strName.c_str(),
9646 mData->m_strConfigFileFull.c_str());
9647 }
9648
9649 if ( !isSnapshotMachine()
9650 && medium->getChildren().size() != 0
9651 )
9652 return setError(E_FAIL,
9653 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9654 "because it has %d differencing child hard disks"),
9655 medium->getLocationFull().c_str(),
9656 dev.uuid.raw(),
9657 mUserData->s.strName.c_str(),
9658 mData->m_strConfigFileFull.c_str(),
9659 medium->getChildren().size());
9660
9661 if (findAttachment(mMediaData->mAttachments,
9662 medium))
9663 return setError(E_FAIL,
9664 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9665 medium->getLocationFull().c_str(),
9666 dev.uuid.raw(),
9667 mUserData->s.strName.c_str(),
9668 mData->m_strConfigFileFull.c_str());
9669
9670 break;
9671 }
9672
9673 default:
9674 return setError(E_FAIL,
9675 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9676 medium->getLocationFull().c_str(),
9677 mUserData->s.strName.c_str(),
9678 mData->m_strConfigFileFull.c_str());
9679 }
9680
9681 if (FAILED(rc))
9682 break;
9683
9684 /* Bandwidth groups are loaded at this point. */
9685 ComObjPtr<BandwidthGroup> pBwGroup;
9686
9687 if (!dev.strBwGroup.isEmpty())
9688 {
9689 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9690 if (FAILED(rc))
9691 return setError(E_FAIL,
9692 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9693 medium->getLocationFull().c_str(),
9694 dev.strBwGroup.c_str(),
9695 mUserData->s.strName.c_str(),
9696 mData->m_strConfigFileFull.c_str());
9697 pBwGroup->reference();
9698 }
9699
9700 const Bstr controllerName = aStorageController->getName();
9701 ComObjPtr<MediumAttachment> pAttachment;
9702 pAttachment.createObject();
9703 rc = pAttachment->init(this,
9704 medium,
9705 controllerName,
9706 dev.lPort,
9707 dev.lDevice,
9708 dev.deviceType,
9709 false,
9710 dev.fPassThrough,
9711 dev.fTempEject,
9712 dev.fNonRotational,
9713 dev.fDiscard,
9714 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9715 if (FAILED(rc)) break;
9716
9717 /* associate the medium with this machine and snapshot */
9718 if (!medium.isNull())
9719 {
9720 AutoCaller medCaller(medium);
9721 if (FAILED(medCaller.rc())) return medCaller.rc();
9722 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9723
9724 if (isSnapshotMachine())
9725 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9726 else
9727 rc = medium->addBackReference(mData->mUuid);
9728 /* If the medium->addBackReference fails it sets an appropriate
9729 * error message, so no need to do any guesswork here. */
9730
9731 if (puuidRegistry)
9732 // caller wants registry ID to be set on all attached media (OVF import case)
9733 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9734 }
9735
9736 if (FAILED(rc))
9737 break;
9738
9739 /* back up mMediaData to let registeredInit() properly rollback on failure
9740 * (= limited accessibility) */
9741 setModified(IsModified_Storage);
9742 mMediaData.backup();
9743 mMediaData->mAttachments.push_back(pAttachment);
9744 }
9745
9746 return rc;
9747}
9748
9749/**
9750 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9751 *
9752 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9753 * @param aSnapshot where to return the found snapshot
9754 * @param aSetError true to set extended error info on failure
9755 */
9756HRESULT Machine::findSnapshotById(const Guid &aId,
9757 ComObjPtr<Snapshot> &aSnapshot,
9758 bool aSetError /* = false */)
9759{
9760 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9761
9762 if (!mData->mFirstSnapshot)
9763 {
9764 if (aSetError)
9765 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9766 return E_FAIL;
9767 }
9768
9769 if (aId.isZero())
9770 aSnapshot = mData->mFirstSnapshot;
9771 else
9772 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9773
9774 if (!aSnapshot)
9775 {
9776 if (aSetError)
9777 return setError(E_FAIL,
9778 tr("Could not find a snapshot with UUID {%s}"),
9779 aId.toString().c_str());
9780 return E_FAIL;
9781 }
9782
9783 return S_OK;
9784}
9785
9786/**
9787 * Returns the snapshot with the given name or fails of no such snapshot.
9788 *
9789 * @param aName snapshot name to find
9790 * @param aSnapshot where to return the found snapshot
9791 * @param aSetError true to set extended error info on failure
9792 */
9793HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9794 ComObjPtr<Snapshot> &aSnapshot,
9795 bool aSetError /* = false */)
9796{
9797 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9798
9799 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9800
9801 if (!mData->mFirstSnapshot)
9802 {
9803 if (aSetError)
9804 return setError(VBOX_E_OBJECT_NOT_FOUND,
9805 tr("This machine does not have any snapshots"));
9806 return VBOX_E_OBJECT_NOT_FOUND;
9807 }
9808
9809 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9810
9811 if (!aSnapshot)
9812 {
9813 if (aSetError)
9814 return setError(VBOX_E_OBJECT_NOT_FOUND,
9815 tr("Could not find a snapshot named '%s'"), strName.c_str());
9816 return VBOX_E_OBJECT_NOT_FOUND;
9817 }
9818
9819 return S_OK;
9820}
9821
9822/**
9823 * Returns a storage controller object with the given name.
9824 *
9825 * @param aName storage controller name to find
9826 * @param aStorageController where to return the found storage controller
9827 * @param aSetError true to set extended error info on failure
9828 */
9829HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9830 ComObjPtr<StorageController> &aStorageController,
9831 bool aSetError /* = false */)
9832{
9833 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9834
9835 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9836 it != mStorageControllers->end();
9837 ++it)
9838 {
9839 if ((*it)->getName() == aName)
9840 {
9841 aStorageController = (*it);
9842 return S_OK;
9843 }
9844 }
9845
9846 if (aSetError)
9847 return setError(VBOX_E_OBJECT_NOT_FOUND,
9848 tr("Could not find a storage controller named '%s'"),
9849 aName.c_str());
9850 return VBOX_E_OBJECT_NOT_FOUND;
9851}
9852
9853/**
9854 * Returns a USB controller object with the given name.
9855 *
9856 * @param aName USB controller name to find
9857 * @param aUSBController where to return the found USB controller
9858 * @param aSetError true to set extended error info on failure
9859 */
9860HRESULT Machine::getUSBControllerByName(const Utf8Str &aName,
9861 ComObjPtr<USBController> &aUSBController,
9862 bool aSetError /* = false */)
9863{
9864 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9865
9866 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9867 it != mUSBControllers->end();
9868 ++it)
9869 {
9870 if ((*it)->getName() == aName)
9871 {
9872 aUSBController = (*it);
9873 return S_OK;
9874 }
9875 }
9876
9877 if (aSetError)
9878 return setError(VBOX_E_OBJECT_NOT_FOUND,
9879 tr("Could not find a storage controller named '%s'"),
9880 aName.c_str());
9881 return VBOX_E_OBJECT_NOT_FOUND;
9882}
9883
9884/**
9885 * Returns the number of USB controller instance of the given type.
9886 *
9887 * @param enmType USB controller type.
9888 */
9889ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType)
9890{
9891 ULONG cCtrls = 0;
9892
9893 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9894 it != mUSBControllers->end();
9895 ++it)
9896 {
9897 if ((*it)->getControllerType() == enmType)
9898 cCtrls++;
9899 }
9900
9901 return cCtrls;
9902}
9903
9904HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9905 MediaData::AttachmentList &atts)
9906{
9907 AutoCaller autoCaller(this);
9908 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9909
9910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9911
9912 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9913 it != mMediaData->mAttachments.end();
9914 ++it)
9915 {
9916 const ComObjPtr<MediumAttachment> &pAtt = *it;
9917
9918 // should never happen, but deal with NULL pointers in the list.
9919 AssertStmt(!pAtt.isNull(), continue);
9920
9921 // getControllerName() needs caller+read lock
9922 AutoCaller autoAttCaller(pAtt);
9923 if (FAILED(autoAttCaller.rc()))
9924 {
9925 atts.clear();
9926 return autoAttCaller.rc();
9927 }
9928 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9929
9930 if (pAtt->getControllerName() == aName)
9931 atts.push_back(pAtt);
9932 }
9933
9934 return S_OK;
9935}
9936
9937/**
9938 * Helper for #saveSettings. Cares about renaming the settings directory and
9939 * file if the machine name was changed and about creating a new settings file
9940 * if this is a new machine.
9941 *
9942 * @note Must be never called directly but only from #saveSettings().
9943 */
9944HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9945{
9946 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9947
9948 HRESULT rc = S_OK;
9949
9950 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9951
9952 /// @todo need to handle primary group change, too
9953
9954 /* attempt to rename the settings file if machine name is changed */
9955 if ( mUserData->s.fNameSync
9956 && mUserData.isBackedUp()
9957 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9958 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9959 )
9960 {
9961 bool dirRenamed = false;
9962 bool fileRenamed = false;
9963
9964 Utf8Str configFile, newConfigFile;
9965 Utf8Str configFilePrev, newConfigFilePrev;
9966 Utf8Str configDir, newConfigDir;
9967
9968 do
9969 {
9970 int vrc = VINF_SUCCESS;
9971
9972 Utf8Str name = mUserData.backedUpData()->s.strName;
9973 Utf8Str newName = mUserData->s.strName;
9974 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9975 if (group == "/")
9976 group.setNull();
9977 Utf8Str newGroup = mUserData->s.llGroups.front();
9978 if (newGroup == "/")
9979 newGroup.setNull();
9980
9981 configFile = mData->m_strConfigFileFull;
9982
9983 /* first, rename the directory if it matches the group and machine name */
9984 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9985 group.c_str(), RTPATH_DELIMITER, name.c_str());
9986 /** @todo hack, make somehow use of ComposeMachineFilename */
9987 if (mUserData->s.fDirectoryIncludesUUID)
9988 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9989 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9990 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9991 /** @todo hack, make somehow use of ComposeMachineFilename */
9992 if (mUserData->s.fDirectoryIncludesUUID)
9993 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9994 configDir = configFile;
9995 configDir.stripFilename();
9996 newConfigDir = configDir;
9997 if ( configDir.length() >= groupPlusName.length()
9998 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9999 {
10000 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
10001 Utf8Str newConfigBaseDir(newConfigDir);
10002 newConfigDir.append(newGroupPlusName);
10003 /* consistency: use \ if appropriate on the platform */
10004 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
10005 /* new dir and old dir cannot be equal here because of 'if'
10006 * above and because name != newName */
10007 Assert(configDir != newConfigDir);
10008 if (!fSettingsFileIsNew)
10009 {
10010 /* perform real rename only if the machine is not new */
10011 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10012 if ( vrc == VERR_FILE_NOT_FOUND
10013 || vrc == VERR_PATH_NOT_FOUND)
10014 {
10015 /* create the parent directory, then retry renaming */
10016 Utf8Str parent(newConfigDir);
10017 parent.stripFilename();
10018 (void)RTDirCreateFullPath(parent.c_str(), 0700);
10019 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
10020 }
10021 if (RT_FAILURE(vrc))
10022 {
10023 rc = setError(E_FAIL,
10024 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
10025 configDir.c_str(),
10026 newConfigDir.c_str(),
10027 vrc);
10028 break;
10029 }
10030 /* delete subdirectories which are no longer needed */
10031 Utf8Str dir(configDir);
10032 dir.stripFilename();
10033 while (dir != newConfigBaseDir && dir != ".")
10034 {
10035 vrc = RTDirRemove(dir.c_str());
10036 if (RT_FAILURE(vrc))
10037 break;
10038 dir.stripFilename();
10039 }
10040 dirRenamed = true;
10041 }
10042 }
10043
10044 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10045 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10046
10047 /* then try to rename the settings file itself */
10048 if (newConfigFile != configFile)
10049 {
10050 /* get the path to old settings file in renamed directory */
10051 configFile = Utf8StrFmt("%s%c%s",
10052 newConfigDir.c_str(),
10053 RTPATH_DELIMITER,
10054 RTPathFilename(configFile.c_str()));
10055 if (!fSettingsFileIsNew)
10056 {
10057 /* perform real rename only if the machine is not new */
10058 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10059 if (RT_FAILURE(vrc))
10060 {
10061 rc = setError(E_FAIL,
10062 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10063 configFile.c_str(),
10064 newConfigFile.c_str(),
10065 vrc);
10066 break;
10067 }
10068 fileRenamed = true;
10069 configFilePrev = configFile;
10070 configFilePrev += "-prev";
10071 newConfigFilePrev = newConfigFile;
10072 newConfigFilePrev += "-prev";
10073 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10074 }
10075 }
10076
10077 // update m_strConfigFileFull amd mConfigFile
10078 mData->m_strConfigFileFull = newConfigFile;
10079 // compute the relative path too
10080 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10081
10082 // store the old and new so that VirtualBox::saveSettings() can update
10083 // the media registry
10084 if ( mData->mRegistered
10085 && configDir != newConfigDir)
10086 {
10087 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
10088
10089 if (pfNeedsGlobalSaveSettings)
10090 *pfNeedsGlobalSaveSettings = true;
10091 }
10092
10093 // in the saved state file path, replace the old directory with the new directory
10094 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10095 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
10096
10097 // and do the same thing for the saved state file paths of all the online snapshots
10098 if (mData->mFirstSnapshot)
10099 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
10100 newConfigDir.c_str());
10101 }
10102 while (0);
10103
10104 if (FAILED(rc))
10105 {
10106 /* silently try to rename everything back */
10107 if (fileRenamed)
10108 {
10109 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10110 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10111 }
10112 if (dirRenamed)
10113 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10114 }
10115
10116 if (FAILED(rc)) return rc;
10117 }
10118
10119 if (fSettingsFileIsNew)
10120 {
10121 /* create a virgin config file */
10122 int vrc = VINF_SUCCESS;
10123
10124 /* ensure the settings directory exists */
10125 Utf8Str path(mData->m_strConfigFileFull);
10126 path.stripFilename();
10127 if (!RTDirExists(path.c_str()))
10128 {
10129 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10130 if (RT_FAILURE(vrc))
10131 {
10132 return setError(E_FAIL,
10133 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10134 path.c_str(),
10135 vrc);
10136 }
10137 }
10138
10139 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10140 path = Utf8Str(mData->m_strConfigFileFull);
10141 RTFILE f = NIL_RTFILE;
10142 vrc = RTFileOpen(&f, path.c_str(),
10143 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10144 if (RT_FAILURE(vrc))
10145 return setError(E_FAIL,
10146 tr("Could not create the settings file '%s' (%Rrc)"),
10147 path.c_str(),
10148 vrc);
10149 RTFileClose(f);
10150 }
10151
10152 return rc;
10153}
10154
10155/**
10156 * Saves and commits machine data, user data and hardware data.
10157 *
10158 * Note that on failure, the data remains uncommitted.
10159 *
10160 * @a aFlags may combine the following flags:
10161 *
10162 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10163 * Used when saving settings after an operation that makes them 100%
10164 * correspond to the settings from the current snapshot.
10165 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10166 * #isReallyModified() returns false. This is necessary for cases when we
10167 * change machine data directly, not through the backup()/commit() mechanism.
10168 * - SaveS_Force: settings will be saved without doing a deep compare of the
10169 * settings structures. This is used when this is called because snapshots
10170 * have changed to avoid the overhead of the deep compare.
10171 *
10172 * @note Must be called from under this object's write lock. Locks children for
10173 * writing.
10174 *
10175 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10176 * initialized to false and that will be set to true by this function if
10177 * the caller must invoke VirtualBox::saveSettings() because the global
10178 * settings have changed. This will happen if a machine rename has been
10179 * saved and the global machine and media registries will therefore need
10180 * updating.
10181 */
10182HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10183 int aFlags /*= 0*/)
10184{
10185 LogFlowThisFuncEnter();
10186
10187 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10188
10189 /* make sure child objects are unable to modify the settings while we are
10190 * saving them */
10191 ensureNoStateDependencies();
10192
10193 AssertReturn(!isSnapshotMachine(),
10194 E_FAIL);
10195
10196 HRESULT rc = S_OK;
10197 bool fNeedsWrite = false;
10198
10199 /* First, prepare to save settings. It will care about renaming the
10200 * settings directory and file if the machine name was changed and about
10201 * creating a new settings file if this is a new machine. */
10202 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10203 if (FAILED(rc)) return rc;
10204
10205 // keep a pointer to the current settings structures
10206 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10207 settings::MachineConfigFile *pNewConfig = NULL;
10208
10209 try
10210 {
10211 // make a fresh one to have everyone write stuff into
10212 pNewConfig = new settings::MachineConfigFile(NULL);
10213 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10214
10215 // now go and copy all the settings data from COM to the settings structures
10216 // (this calles saveSettings() on all the COM objects in the machine)
10217 copyMachineDataToSettings(*pNewConfig);
10218
10219 if (aFlags & SaveS_ResetCurStateModified)
10220 {
10221 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10222 mData->mCurrentStateModified = FALSE;
10223 fNeedsWrite = true; // always, no need to compare
10224 }
10225 else if (aFlags & SaveS_Force)
10226 {
10227 fNeedsWrite = true; // always, no need to compare
10228 }
10229 else
10230 {
10231 if (!mData->mCurrentStateModified)
10232 {
10233 // do a deep compare of the settings that we just saved with the settings
10234 // previously stored in the config file; this invokes MachineConfigFile::operator==
10235 // which does a deep compare of all the settings, which is expensive but less expensive
10236 // than writing out XML in vain
10237 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10238
10239 // could still be modified if any settings changed
10240 mData->mCurrentStateModified = fAnySettingsChanged;
10241
10242 fNeedsWrite = fAnySettingsChanged;
10243 }
10244 else
10245 fNeedsWrite = true;
10246 }
10247
10248 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10249
10250 if (fNeedsWrite)
10251 // now spit it all out!
10252 pNewConfig->write(mData->m_strConfigFileFull);
10253
10254 mData->pMachineConfigFile = pNewConfig;
10255 delete pOldConfig;
10256 commit();
10257
10258 // after saving settings, we are no longer different from the XML on disk
10259 mData->flModifications = 0;
10260 }
10261 catch (HRESULT err)
10262 {
10263 // we assume that error info is set by the thrower
10264 rc = err;
10265
10266 // restore old config
10267 delete pNewConfig;
10268 mData->pMachineConfigFile = pOldConfig;
10269 }
10270 catch (...)
10271 {
10272 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10273 }
10274
10275 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10276 {
10277 /* Fire the data change event, even on failure (since we've already
10278 * committed all data). This is done only for SessionMachines because
10279 * mutable Machine instances are always not registered (i.e. private
10280 * to the client process that creates them) and thus don't need to
10281 * inform callbacks. */
10282 if (isSessionMachine())
10283 mParent->onMachineDataChange(mData->mUuid);
10284 }
10285
10286 LogFlowThisFunc(("rc=%08X\n", rc));
10287 LogFlowThisFuncLeave();
10288 return rc;
10289}
10290
10291/**
10292 * Implementation for saving the machine settings into the given
10293 * settings::MachineConfigFile instance. This copies machine extradata
10294 * from the previous machine config file in the instance data, if any.
10295 *
10296 * This gets called from two locations:
10297 *
10298 * -- Machine::saveSettings(), during the regular XML writing;
10299 *
10300 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10301 * exported to OVF and we write the VirtualBox proprietary XML
10302 * into a <vbox:Machine> tag.
10303 *
10304 * This routine fills all the fields in there, including snapshots, *except*
10305 * for the following:
10306 *
10307 * -- fCurrentStateModified. There is some special logic associated with that.
10308 *
10309 * The caller can then call MachineConfigFile::write() or do something else
10310 * with it.
10311 *
10312 * Caller must hold the machine lock!
10313 *
10314 * This throws XML errors and HRESULT, so the caller must have a catch block!
10315 */
10316void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10317{
10318 // deep copy extradata
10319 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10320
10321 config.uuid = mData->mUuid;
10322
10323 // copy name, description, OS type, teleport, UTC etc.
10324 config.machineUserData = mUserData->s;
10325
10326 // Encode the Icon Override data from Machine and store on config userdata.
10327 com::SafeArray<BYTE> iconByte;
10328 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10329 ssize_t cbData = iconByte.size();
10330 if (cbData > 0)
10331 {
10332 ssize_t cchOut = RTBase64EncodedLength(cbData);
10333 Utf8Str strIconData;
10334 strIconData.reserve(cchOut+1);
10335 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10336 strIconData.mutableRaw(), strIconData.capacity(),
10337 NULL);
10338 if (RT_FAILURE(vrc))
10339 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10340 strIconData.jolt();
10341 config.machineUserData.ovIcon = strIconData;
10342 }
10343 else
10344 config.machineUserData.ovIcon.setNull();
10345
10346 if ( mData->mMachineState == MachineState_Saved
10347 || mData->mMachineState == MachineState_Restoring
10348 // when deleting a snapshot we may or may not have a saved state in the current state,
10349 // so let's not assert here please
10350 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10351 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10352 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10353 && (!mSSData->strStateFilePath.isEmpty())
10354 )
10355 )
10356 {
10357 Assert(!mSSData->strStateFilePath.isEmpty());
10358 /* try to make the file name relative to the settings file dir */
10359 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10360 }
10361 else
10362 {
10363 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10364 config.strStateFile.setNull();
10365 }
10366
10367 if (mData->mCurrentSnapshot)
10368 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10369 else
10370 config.uuidCurrentSnapshot.clear();
10371
10372 config.timeLastStateChange = mData->mLastStateChange;
10373 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10374 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10375
10376 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10377 if (FAILED(rc)) throw rc;
10378
10379 rc = saveStorageControllers(config.storageMachine);
10380 if (FAILED(rc)) throw rc;
10381
10382 // save machine's media registry if this is VirtualBox 4.0 or later
10383 if (config.canHaveOwnMediaRegistry())
10384 {
10385 // determine machine folder
10386 Utf8Str strMachineFolder = getSettingsFileFull();
10387 strMachineFolder.stripFilename();
10388 mParent->saveMediaRegistry(config.mediaRegistry,
10389 getId(), // only media with registry ID == machine UUID
10390 strMachineFolder);
10391 // this throws HRESULT
10392 }
10393
10394 // save snapshots
10395 rc = saveAllSnapshots(config);
10396 if (FAILED(rc)) throw rc;
10397}
10398
10399/**
10400 * Saves all snapshots of the machine into the given machine config file. Called
10401 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10402 * @param config
10403 * @return
10404 */
10405HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10406{
10407 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10408
10409 HRESULT rc = S_OK;
10410
10411 try
10412 {
10413 config.llFirstSnapshot.clear();
10414
10415 if (mData->mFirstSnapshot)
10416 {
10417 settings::Snapshot snapNew;
10418 config.llFirstSnapshot.push_back(snapNew);
10419
10420 // get reference to the fresh copy of the snapshot on the list and
10421 // work on that copy directly to avoid excessive copying later
10422 settings::Snapshot &snap = config.llFirstSnapshot.front();
10423
10424 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10425 if (FAILED(rc)) throw rc;
10426 }
10427
10428// if (mType == IsSessionMachine)
10429// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10430
10431 }
10432 catch (HRESULT err)
10433 {
10434 /* we assume that error info is set by the thrower */
10435 rc = err;
10436 }
10437 catch (...)
10438 {
10439 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10440 }
10441
10442 return rc;
10443}
10444
10445/**
10446 * Saves the VM hardware configuration. It is assumed that the
10447 * given node is empty.
10448 *
10449 * @param data Reference to the settings object for the hardware config.
10450 * @param pDbg Pointer to the settings object for the debugging config
10451 * which happens to live in mHWData.
10452 * @param pAutostart Pointer to the settings object for the autostart config
10453 * which happens to live in mHWData.
10454 */
10455HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10456 settings::Autostart *pAutostart)
10457{
10458 HRESULT rc = S_OK;
10459
10460 try
10461 {
10462 /* The hardware version attribute (optional).
10463 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10464 if ( mHWData->mHWVersion == "1"
10465 && mSSData->strStateFilePath.isEmpty()
10466 )
10467 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. */
10468
10469 data.strVersion = mHWData->mHWVersion;
10470 data.uuid = mHWData->mHardwareUUID;
10471
10472 // CPU
10473 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10474 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10475 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10476 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10477 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10478 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10479 data.fPAE = !!mHWData->mPAEEnabled;
10480 data.enmLongMode = mHWData->mLongMode;
10481 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10482
10483 /* Standard and Extended CPUID leafs. */
10484 data.llCpuIdLeafs.clear();
10485 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10486 {
10487 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10488 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10489 }
10490 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10491 {
10492 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10493 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10494 }
10495
10496 data.cCPUs = mHWData->mCPUCount;
10497 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10498 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10499
10500 data.llCpus.clear();
10501 if (data.fCpuHotPlug)
10502 {
10503 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10504 {
10505 if (mHWData->mCPUAttached[idx])
10506 {
10507 settings::Cpu cpu;
10508 cpu.ulId = idx;
10509 data.llCpus.push_back(cpu);
10510 }
10511 }
10512 }
10513
10514 // memory
10515 data.ulMemorySizeMB = mHWData->mMemorySize;
10516 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10517
10518 // firmware
10519 data.firmwareType = mHWData->mFirmwareType;
10520
10521 // HID
10522 data.pointingHIDType = mHWData->mPointingHIDType;
10523 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10524
10525 // chipset
10526 data.chipsetType = mHWData->mChipsetType;
10527
10528 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10529 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10530
10531 // HPET
10532 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10533
10534 // boot order
10535 data.mapBootOrder.clear();
10536 for (size_t i = 0;
10537 i < RT_ELEMENTS(mHWData->mBootOrder);
10538 ++i)
10539 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10540
10541 // display
10542 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10543 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10544 data.cMonitors = mHWData->mMonitorCount;
10545 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10546 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10547 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10548 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10549 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10550 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10551 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10552 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10553 {
10554 if (mHWData->maVideoCaptureScreens[i])
10555 ASMBitSet(&data.u64VideoCaptureScreens, i);
10556 else
10557 ASMBitClear(&data.u64VideoCaptureScreens, i);
10558 }
10559 /* store relative video capture file if possible */
10560 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10561
10562 /* VRDEServer settings (optional) */
10563 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10564 if (FAILED(rc)) throw rc;
10565
10566 /* BIOS (required) */
10567 rc = mBIOSSettings->saveSettings(data.biosSettings);
10568 if (FAILED(rc)) throw rc;
10569
10570 /* USB Controller (required) */
10571 for (USBControllerList::const_iterator it = mUSBControllers->begin();
10572 it != mUSBControllers->end();
10573 ++it)
10574 {
10575 ComObjPtr<USBController> ctrl = *it;
10576 settings::USBController settingsCtrl;
10577
10578 settingsCtrl.strName = ctrl->getName();
10579 settingsCtrl.enmType = ctrl->getControllerType();
10580
10581 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10582 }
10583
10584 /* USB device filters (required) */
10585 rc = mUSBDeviceFilters->saveSettings(data.usbSettings);
10586 if (FAILED(rc)) throw rc;
10587
10588 /* Network adapters (required) */
10589 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10590 data.llNetworkAdapters.clear();
10591 /* Write out only the nominal number of network adapters for this
10592 * chipset type. Since Machine::commit() hasn't been called there
10593 * may be extra NIC settings in the vector. */
10594 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10595 {
10596 settings::NetworkAdapter nic;
10597 nic.ulSlot = slot;
10598 /* paranoia check... must not be NULL, but must not crash either. */
10599 if (mNetworkAdapters[slot])
10600 {
10601 rc = mNetworkAdapters[slot]->saveSettings(nic);
10602 if (FAILED(rc)) throw rc;
10603
10604 data.llNetworkAdapters.push_back(nic);
10605 }
10606 }
10607
10608 /* Serial ports */
10609 data.llSerialPorts.clear();
10610 for (ULONG slot = 0;
10611 slot < RT_ELEMENTS(mSerialPorts);
10612 ++slot)
10613 {
10614 settings::SerialPort s;
10615 s.ulSlot = slot;
10616 rc = mSerialPorts[slot]->saveSettings(s);
10617 if (FAILED(rc)) return rc;
10618
10619 data.llSerialPorts.push_back(s);
10620 }
10621
10622 /* Parallel ports */
10623 data.llParallelPorts.clear();
10624 for (ULONG slot = 0;
10625 slot < RT_ELEMENTS(mParallelPorts);
10626 ++slot)
10627 {
10628 settings::ParallelPort p;
10629 p.ulSlot = slot;
10630 rc = mParallelPorts[slot]->saveSettings(p);
10631 if (FAILED(rc)) return rc;
10632
10633 data.llParallelPorts.push_back(p);
10634 }
10635
10636 /* Audio adapter */
10637 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10638 if (FAILED(rc)) return rc;
10639
10640 /* Shared folders */
10641 data.llSharedFolders.clear();
10642 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10643 it != mHWData->mSharedFolders.end();
10644 ++it)
10645 {
10646 SharedFolder *pSF = *it;
10647 AutoCaller sfCaller(pSF);
10648 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10649 settings::SharedFolder sf;
10650 sf.strName = pSF->getName();
10651 sf.strHostPath = pSF->getHostPath();
10652 sf.fWritable = !!pSF->isWritable();
10653 sf.fAutoMount = !!pSF->isAutoMounted();
10654
10655 data.llSharedFolders.push_back(sf);
10656 }
10657
10658 // clipboard
10659 data.clipboardMode = mHWData->mClipboardMode;
10660
10661 // drag'n'drop
10662 data.dragAndDropMode = mHWData->mDragAndDropMode;
10663
10664 /* Guest */
10665 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10666
10667 // IO settings
10668 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10669 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10670
10671 /* BandwidthControl (required) */
10672 rc = mBandwidthControl->saveSettings(data.ioSettings);
10673 if (FAILED(rc)) throw rc;
10674
10675 /* Host PCI devices */
10676 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10677 it != mHWData->mPCIDeviceAssignments.end();
10678 ++it)
10679 {
10680 ComObjPtr<PCIDeviceAttachment> pda = *it;
10681 settings::HostPCIDeviceAttachment hpda;
10682
10683 rc = pda->saveSettings(hpda);
10684 if (FAILED(rc)) throw rc;
10685
10686 data.pciAttachments.push_back(hpda);
10687 }
10688
10689
10690 // guest properties
10691 data.llGuestProperties.clear();
10692#ifdef VBOX_WITH_GUEST_PROPS
10693 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10694 it != mHWData->mGuestProperties.end();
10695 ++it)
10696 {
10697 HWData::GuestProperty property = it->second;
10698
10699 /* Remove transient guest properties at shutdown unless we
10700 * are saving state */
10701 if ( ( mData->mMachineState == MachineState_PoweredOff
10702 || mData->mMachineState == MachineState_Aborted
10703 || mData->mMachineState == MachineState_Teleported)
10704 && ( property.mFlags & guestProp::TRANSIENT
10705 || property.mFlags & guestProp::TRANSRESET))
10706 continue;
10707 settings::GuestProperty prop;
10708 prop.strName = it->first;
10709 prop.strValue = property.strValue;
10710 prop.timestamp = property.mTimestamp;
10711 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10712 guestProp::writeFlags(property.mFlags, szFlags);
10713 prop.strFlags = szFlags;
10714
10715 data.llGuestProperties.push_back(prop);
10716 }
10717
10718 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10719 /* I presume this doesn't require a backup(). */
10720 mData->mGuestPropertiesModified = FALSE;
10721#endif /* VBOX_WITH_GUEST_PROPS defined */
10722
10723 *pDbg = mHWData->mDebugging;
10724 *pAutostart = mHWData->mAutostart;
10725
10726 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10727 }
10728 catch(std::bad_alloc &)
10729 {
10730 return E_OUTOFMEMORY;
10731 }
10732
10733 AssertComRC(rc);
10734 return rc;
10735}
10736
10737/**
10738 * Saves the storage controller configuration.
10739 *
10740 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10741 */
10742HRESULT Machine::saveStorageControllers(settings::Storage &data)
10743{
10744 data.llStorageControllers.clear();
10745
10746 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10747 it != mStorageControllers->end();
10748 ++it)
10749 {
10750 HRESULT rc;
10751 ComObjPtr<StorageController> pCtl = *it;
10752
10753 settings::StorageController ctl;
10754 ctl.strName = pCtl->getName();
10755 ctl.controllerType = pCtl->getControllerType();
10756 ctl.storageBus = pCtl->getStorageBus();
10757 ctl.ulInstance = pCtl->getInstance();
10758 ctl.fBootable = pCtl->getBootable();
10759
10760 /* Save the port count. */
10761 ULONG portCount;
10762 rc = pCtl->COMGETTER(PortCount)(&portCount);
10763 ComAssertComRCRet(rc, rc);
10764 ctl.ulPortCount = portCount;
10765
10766 /* Save fUseHostIOCache */
10767 BOOL fUseHostIOCache;
10768 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10769 ComAssertComRCRet(rc, rc);
10770 ctl.fUseHostIOCache = !!fUseHostIOCache;
10771
10772 /* Save IDE emulation settings. */
10773 if (ctl.controllerType == StorageControllerType_IntelAhci)
10774 {
10775 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10776 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10777 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10778 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10779 )
10780 ComAssertComRCRet(rc, rc);
10781 }
10782
10783 /* save the devices now. */
10784 rc = saveStorageDevices(pCtl, ctl);
10785 ComAssertComRCRet(rc, rc);
10786
10787 data.llStorageControllers.push_back(ctl);
10788 }
10789
10790 return S_OK;
10791}
10792
10793/**
10794 * Saves the hard disk configuration.
10795 */
10796HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10797 settings::StorageController &data)
10798{
10799 MediaData::AttachmentList atts;
10800
10801 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10802 if (FAILED(rc)) return rc;
10803
10804 data.llAttachedDevices.clear();
10805 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10806 it != atts.end();
10807 ++it)
10808 {
10809 settings::AttachedDevice dev;
10810
10811 MediumAttachment *pAttach = *it;
10812 Medium *pMedium = pAttach->getMedium();
10813
10814 dev.deviceType = pAttach->getType();
10815 dev.lPort = pAttach->getPort();
10816 dev.lDevice = pAttach->getDevice();
10817 if (pMedium)
10818 {
10819 if (pMedium->isHostDrive())
10820 dev.strHostDriveSrc = pMedium->getLocationFull();
10821 else
10822 dev.uuid = pMedium->getId();
10823 dev.fPassThrough = pAttach->getPassthrough();
10824 dev.fTempEject = pAttach->getTempEject();
10825 dev.fNonRotational = pAttach->getNonRotational();
10826 dev.fDiscard = pAttach->getDiscard();
10827 }
10828
10829 dev.strBwGroup = pAttach->getBandwidthGroup();
10830
10831 data.llAttachedDevices.push_back(dev);
10832 }
10833
10834 return S_OK;
10835}
10836
10837/**
10838 * Saves machine state settings as defined by aFlags
10839 * (SaveSTS_* values).
10840 *
10841 * @param aFlags Combination of SaveSTS_* flags.
10842 *
10843 * @note Locks objects for writing.
10844 */
10845HRESULT Machine::saveStateSettings(int aFlags)
10846{
10847 if (aFlags == 0)
10848 return S_OK;
10849
10850 AutoCaller autoCaller(this);
10851 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10852
10853 /* This object's write lock is also necessary to serialize file access
10854 * (prevent concurrent reads and writes) */
10855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10856
10857 HRESULT rc = S_OK;
10858
10859 Assert(mData->pMachineConfigFile);
10860
10861 try
10862 {
10863 if (aFlags & SaveSTS_CurStateModified)
10864 mData->pMachineConfigFile->fCurrentStateModified = true;
10865
10866 if (aFlags & SaveSTS_StateFilePath)
10867 {
10868 if (!mSSData->strStateFilePath.isEmpty())
10869 /* try to make the file name relative to the settings file dir */
10870 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10871 else
10872 mData->pMachineConfigFile->strStateFile.setNull();
10873 }
10874
10875 if (aFlags & SaveSTS_StateTimeStamp)
10876 {
10877 Assert( mData->mMachineState != MachineState_Aborted
10878 || mSSData->strStateFilePath.isEmpty());
10879
10880 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10881
10882 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10883//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10884 }
10885
10886 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10887 }
10888 catch (...)
10889 {
10890 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10891 }
10892
10893 return rc;
10894}
10895
10896/**
10897 * Ensures that the given medium is added to a media registry. If this machine
10898 * was created with 4.0 or later, then the machine registry is used. Otherwise
10899 * the global VirtualBox media registry is used.
10900 *
10901 * Caller must NOT hold machine lock, media tree or any medium locks!
10902 *
10903 * @param pMedium
10904 */
10905void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10906{
10907 /* Paranoia checks: do not hold machine or media tree locks. */
10908 AssertReturnVoid(!isWriteLockOnCurrentThread());
10909 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10910
10911 ComObjPtr<Medium> pBase;
10912 {
10913 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10914 pBase = pMedium->getBase();
10915 }
10916
10917 /* Paranoia checks: do not hold medium locks. */
10918 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10919 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10920
10921 // decide which medium registry to use now that the medium is attached:
10922 Guid uuid;
10923 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10924 // machine XML is VirtualBox 4.0 or higher:
10925 uuid = getId(); // machine UUID
10926 else
10927 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10928
10929 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10930 mParent->markRegistryModified(uuid);
10931
10932 /* For more complex hard disk structures it can happen that the base
10933 * medium isn't yet associated with any medium registry. Do that now. */
10934 if (pMedium != pBase)
10935 {
10936 if (pBase->addRegistry(uuid, true /* fRecurse */))
10937 mParent->markRegistryModified(uuid);
10938 }
10939}
10940
10941/**
10942 * Creates differencing hard disks for all normal hard disks attached to this
10943 * machine and a new set of attachments to refer to created disks.
10944 *
10945 * Used when taking a snapshot or when deleting the current state. Gets called
10946 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10947 *
10948 * This method assumes that mMediaData contains the original hard disk attachments
10949 * it needs to create diffs for. On success, these attachments will be replaced
10950 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10951 * called to delete created diffs which will also rollback mMediaData and restore
10952 * whatever was backed up before calling this method.
10953 *
10954 * Attachments with non-normal hard disks are left as is.
10955 *
10956 * If @a aOnline is @c false then the original hard disks that require implicit
10957 * diffs will be locked for reading. Otherwise it is assumed that they are
10958 * already locked for writing (when the VM was started). Note that in the latter
10959 * case it is responsibility of the caller to lock the newly created diffs for
10960 * writing if this method succeeds.
10961 *
10962 * @param aProgress Progress object to run (must contain at least as
10963 * many operations left as the number of hard disks
10964 * attached).
10965 * @param aOnline Whether the VM was online prior to this operation.
10966 *
10967 * @note The progress object is not marked as completed, neither on success nor
10968 * on failure. This is a responsibility of the caller.
10969 *
10970 * @note Locks this object and the media tree for writing.
10971 */
10972HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10973 ULONG aWeight,
10974 bool aOnline)
10975{
10976 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10977
10978 AutoCaller autoCaller(this);
10979 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10980
10981 AutoMultiWriteLock2 alock(this->lockHandle(),
10982 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10983
10984 /* must be in a protective state because we release the lock below */
10985 AssertReturn( mData->mMachineState == MachineState_Saving
10986 || mData->mMachineState == MachineState_LiveSnapshotting
10987 || mData->mMachineState == MachineState_RestoringSnapshot
10988 || mData->mMachineState == MachineState_DeletingSnapshot
10989 , E_FAIL);
10990
10991 HRESULT rc = S_OK;
10992
10993 // use appropriate locked media map (online or offline)
10994 MediumLockListMap lockedMediaOffline;
10995 MediumLockListMap *lockedMediaMap;
10996 if (aOnline)
10997 lockedMediaMap = &mData->mSession.mLockedMedia;
10998 else
10999 lockedMediaMap = &lockedMediaOffline;
11000
11001 try
11002 {
11003 if (!aOnline)
11004 {
11005 /* lock all attached hard disks early to detect "in use"
11006 * situations before creating actual diffs */
11007 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11008 it != mMediaData->mAttachments.end();
11009 ++it)
11010 {
11011 MediumAttachment* pAtt = *it;
11012 if (pAtt->getType() == DeviceType_HardDisk)
11013 {
11014 Medium* pMedium = pAtt->getMedium();
11015 Assert(pMedium);
11016
11017 MediumLockList *pMediumLockList(new MediumLockList());
11018 alock.release();
11019 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11020 false /* fMediumLockWrite */,
11021 NULL,
11022 *pMediumLockList);
11023 alock.acquire();
11024 if (FAILED(rc))
11025 {
11026 delete pMediumLockList;
11027 throw rc;
11028 }
11029 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11030 if (FAILED(rc))
11031 {
11032 throw setError(rc,
11033 tr("Collecting locking information for all attached media failed"));
11034 }
11035 }
11036 }
11037
11038 /* Now lock all media. If this fails, nothing is locked. */
11039 alock.release();
11040 rc = lockedMediaMap->Lock();
11041 alock.acquire();
11042 if (FAILED(rc))
11043 {
11044 throw setError(rc,
11045 tr("Locking of attached media failed"));
11046 }
11047 }
11048
11049 /* remember the current list (note that we don't use backup() since
11050 * mMediaData may be already backed up) */
11051 MediaData::AttachmentList atts = mMediaData->mAttachments;
11052
11053 /* start from scratch */
11054 mMediaData->mAttachments.clear();
11055
11056 /* go through remembered attachments and create diffs for normal hard
11057 * disks and attach them */
11058 for (MediaData::AttachmentList::const_iterator it = atts.begin();
11059 it != atts.end();
11060 ++it)
11061 {
11062 MediumAttachment* pAtt = *it;
11063
11064 DeviceType_T devType = pAtt->getType();
11065 Medium* pMedium = pAtt->getMedium();
11066
11067 if ( devType != DeviceType_HardDisk
11068 || pMedium == NULL
11069 || pMedium->getType() != MediumType_Normal)
11070 {
11071 /* copy the attachment as is */
11072
11073 /** @todo the progress object created in Console::TakeSnaphot
11074 * only expects operations for hard disks. Later other
11075 * device types need to show up in the progress as well. */
11076 if (devType == DeviceType_HardDisk)
11077 {
11078 if (pMedium == NULL)
11079 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11080 aWeight); // weight
11081 else
11082 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11083 pMedium->getBase()->getName().c_str()).raw(),
11084 aWeight); // weight
11085 }
11086
11087 mMediaData->mAttachments.push_back(pAtt);
11088 continue;
11089 }
11090
11091 /* need a diff */
11092 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11093 pMedium->getBase()->getName().c_str()).raw(),
11094 aWeight); // weight
11095
11096 Utf8Str strFullSnapshotFolder;
11097 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11098
11099 ComObjPtr<Medium> diff;
11100 diff.createObject();
11101 // store the diff in the same registry as the parent
11102 // (this cannot fail here because we can't create implicit diffs for
11103 // unregistered images)
11104 Guid uuidRegistryParent;
11105 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
11106 Assert(fInRegistry); NOREF(fInRegistry);
11107 rc = diff->init(mParent,
11108 pMedium->getPreferredDiffFormat(),
11109 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11110 uuidRegistryParent);
11111 if (FAILED(rc)) throw rc;
11112
11113 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11114 * the push_back? Looks like we're going to release medium with the
11115 * wrong kind of lock (general issue with if we fail anywhere at all)
11116 * and an orphaned VDI in the snapshots folder. */
11117
11118 /* update the appropriate lock list */
11119 MediumLockList *pMediumLockList;
11120 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11121 AssertComRCThrowRC(rc);
11122 if (aOnline)
11123 {
11124 alock.release();
11125 /* The currently attached medium will be read-only, change
11126 * the lock type to read. */
11127 rc = pMediumLockList->Update(pMedium, false);
11128 alock.acquire();
11129 AssertComRCThrowRC(rc);
11130 }
11131
11132 /* release the locks before the potentially lengthy operation */
11133 alock.release();
11134 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
11135 pMediumLockList,
11136 NULL /* aProgress */,
11137 true /* aWait */);
11138 alock.acquire();
11139 if (FAILED(rc)) throw rc;
11140
11141 /* actual lock list update is done in Medium::commitMedia */
11142
11143 rc = diff->addBackReference(mData->mUuid);
11144 AssertComRCThrowRC(rc);
11145
11146 /* add a new attachment */
11147 ComObjPtr<MediumAttachment> attachment;
11148 attachment.createObject();
11149 rc = attachment->init(this,
11150 diff,
11151 pAtt->getControllerName(),
11152 pAtt->getPort(),
11153 pAtt->getDevice(),
11154 DeviceType_HardDisk,
11155 true /* aImplicit */,
11156 false /* aPassthrough */,
11157 false /* aTempEject */,
11158 pAtt->getNonRotational(),
11159 pAtt->getDiscard(),
11160 pAtt->getBandwidthGroup());
11161 if (FAILED(rc)) throw rc;
11162
11163 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11164 AssertComRCThrowRC(rc);
11165 mMediaData->mAttachments.push_back(attachment);
11166 }
11167 }
11168 catch (HRESULT aRC) { rc = aRC; }
11169
11170 /* unlock all hard disks we locked when there is no VM */
11171 if (!aOnline)
11172 {
11173 ErrorInfoKeeper eik;
11174
11175 HRESULT rc1 = lockedMediaMap->Clear();
11176 AssertComRC(rc1);
11177 }
11178
11179 return rc;
11180}
11181
11182/**
11183 * Deletes implicit differencing hard disks created either by
11184 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11185 *
11186 * Note that to delete hard disks created by #AttachDevice() this method is
11187 * called from #fixupMedia() when the changes are rolled back.
11188 *
11189 * @note Locks this object and the media tree for writing.
11190 */
11191HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11192{
11193 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11194
11195 AutoCaller autoCaller(this);
11196 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11197
11198 AutoMultiWriteLock2 alock(this->lockHandle(),
11199 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11200
11201 /* We absolutely must have backed up state. */
11202 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11203
11204 /* Check if there are any implicitly created diff images. */
11205 bool fImplicitDiffs = false;
11206 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11207 it != mMediaData->mAttachments.end();
11208 ++it)
11209 {
11210 const ComObjPtr<MediumAttachment> &pAtt = *it;
11211 if (pAtt->isImplicit())
11212 {
11213 fImplicitDiffs = true;
11214 break;
11215 }
11216 }
11217 /* If there is nothing to do, leave early. This saves lots of image locking
11218 * effort. It also avoids a MachineStateChanged event without real reason.
11219 * This is important e.g. when loading a VM config, because there should be
11220 * no events. Otherwise API clients can become thoroughly confused for
11221 * inaccessible VMs (the code for loading VM configs uses this method for
11222 * cleanup if the config makes no sense), as they take such events as an
11223 * indication that the VM is alive, and they would force the VM config to
11224 * be reread, leading to an endless loop. */
11225 if (!fImplicitDiffs)
11226 return S_OK;
11227
11228 HRESULT rc = S_OK;
11229 MachineState_T oldState = mData->mMachineState;
11230
11231 /* will release the lock before the potentially lengthy operation,
11232 * so protect with the special state (unless already protected) */
11233 if ( oldState != MachineState_Saving
11234 && oldState != MachineState_LiveSnapshotting
11235 && oldState != MachineState_RestoringSnapshot
11236 && oldState != MachineState_DeletingSnapshot
11237 && oldState != MachineState_DeletingSnapshotOnline
11238 && oldState != MachineState_DeletingSnapshotPaused
11239 )
11240 setMachineState(MachineState_SettingUp);
11241
11242 // use appropriate locked media map (online or offline)
11243 MediumLockListMap lockedMediaOffline;
11244 MediumLockListMap *lockedMediaMap;
11245 if (aOnline)
11246 lockedMediaMap = &mData->mSession.mLockedMedia;
11247 else
11248 lockedMediaMap = &lockedMediaOffline;
11249
11250 try
11251 {
11252 if (!aOnline)
11253 {
11254 /* lock all attached hard disks early to detect "in use"
11255 * situations before deleting actual diffs */
11256 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11257 it != mMediaData->mAttachments.end();
11258 ++it)
11259 {
11260 MediumAttachment* pAtt = *it;
11261 if (pAtt->getType() == DeviceType_HardDisk)
11262 {
11263 Medium* pMedium = pAtt->getMedium();
11264 Assert(pMedium);
11265
11266 MediumLockList *pMediumLockList(new MediumLockList());
11267 alock.release();
11268 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11269 false /* fMediumLockWrite */,
11270 NULL,
11271 *pMediumLockList);
11272 alock.acquire();
11273
11274 if (FAILED(rc))
11275 {
11276 delete pMediumLockList;
11277 throw rc;
11278 }
11279
11280 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11281 if (FAILED(rc))
11282 throw rc;
11283 }
11284 }
11285
11286 if (FAILED(rc))
11287 throw rc;
11288 } // end of offline
11289
11290 /* Lock lists are now up to date and include implicitly created media */
11291
11292 /* Go through remembered attachments and delete all implicitly created
11293 * diffs and fix up the attachment information */
11294 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11295 MediaData::AttachmentList implicitAtts;
11296 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11297 it != mMediaData->mAttachments.end();
11298 ++it)
11299 {
11300 ComObjPtr<MediumAttachment> pAtt = *it;
11301 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11302 if (pMedium.isNull())
11303 continue;
11304
11305 // Implicit attachments go on the list for deletion and back references are removed.
11306 if (pAtt->isImplicit())
11307 {
11308 /* Deassociate and mark for deletion */
11309 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11310 rc = pMedium->removeBackReference(mData->mUuid);
11311 if (FAILED(rc))
11312 throw rc;
11313 implicitAtts.push_back(pAtt);
11314 continue;
11315 }
11316
11317 /* Was this medium attached before? */
11318 if (!findAttachment(oldAtts, pMedium))
11319 {
11320 /* no: de-associate */
11321 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11322 rc = pMedium->removeBackReference(mData->mUuid);
11323 if (FAILED(rc))
11324 throw rc;
11325 continue;
11326 }
11327 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11328 }
11329
11330 /* If there are implicit attachments to delete, throw away the lock
11331 * map contents (which will unlock all media) since the medium
11332 * attachments will be rolled back. Below we need to completely
11333 * recreate the lock map anyway since it is infinitely complex to
11334 * do this incrementally (would need reconstructing each attachment
11335 * change, which would be extremely hairy). */
11336 if (implicitAtts.size() != 0)
11337 {
11338 ErrorInfoKeeper eik;
11339
11340 HRESULT rc1 = lockedMediaMap->Clear();
11341 AssertComRC(rc1);
11342 }
11343
11344 /* rollback hard disk changes */
11345 mMediaData.rollback();
11346
11347 MultiResult mrc(S_OK);
11348
11349 // Delete unused implicit diffs.
11350 if (implicitAtts.size() != 0)
11351 {
11352 alock.release();
11353
11354 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11355 it != implicitAtts.end();
11356 ++it)
11357 {
11358 // Remove medium associated with this attachment.
11359 ComObjPtr<MediumAttachment> pAtt = *it;
11360 Assert(pAtt);
11361 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11362 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11363 Assert(pMedium);
11364
11365 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11366 // continue on delete failure, just collect error messages
11367 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11368 mrc = rc;
11369 }
11370
11371 alock.acquire();
11372
11373 /* if there is a VM recreate media lock map as mentioned above,
11374 * otherwise it is a waste of time and we leave things unlocked */
11375 if (aOnline)
11376 {
11377 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11378 /* must never be NULL, but better safe than sorry */
11379 if (!pMachine.isNull())
11380 {
11381 alock.release();
11382 rc = mData->mSession.mMachine->lockMedia();
11383 alock.acquire();
11384 if (FAILED(rc))
11385 throw rc;
11386 }
11387 }
11388 }
11389 }
11390 catch (HRESULT aRC) {rc = aRC;}
11391
11392 if (mData->mMachineState == MachineState_SettingUp)
11393 setMachineState(oldState);
11394
11395 /* unlock all hard disks we locked when there is no VM */
11396 if (!aOnline)
11397 {
11398 ErrorInfoKeeper eik;
11399
11400 HRESULT rc1 = lockedMediaMap->Clear();
11401 AssertComRC(rc1);
11402 }
11403
11404 return rc;
11405}
11406
11407
11408/**
11409 * Looks through the given list of media attachments for one with the given parameters
11410 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11411 * can be searched as well if needed.
11412 *
11413 * @param list
11414 * @param aControllerName
11415 * @param aControllerPort
11416 * @param aDevice
11417 * @return
11418 */
11419MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11420 IN_BSTR aControllerName,
11421 LONG aControllerPort,
11422 LONG aDevice)
11423{
11424 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11425 it != ll.end();
11426 ++it)
11427 {
11428 MediumAttachment *pAttach = *it;
11429 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11430 return pAttach;
11431 }
11432
11433 return NULL;
11434}
11435
11436/**
11437 * Looks through the given list of media attachments for one with the given parameters
11438 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11439 * can be searched as well if needed.
11440 *
11441 * @param list
11442 * @param aControllerName
11443 * @param aControllerPort
11444 * @param aDevice
11445 * @return
11446 */
11447MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11448 ComObjPtr<Medium> pMedium)
11449{
11450 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11451 it != ll.end();
11452 ++it)
11453 {
11454 MediumAttachment *pAttach = *it;
11455 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11456 if (pMediumThis == pMedium)
11457 return pAttach;
11458 }
11459
11460 return NULL;
11461}
11462
11463/**
11464 * Looks through the given list of media attachments for one with the given parameters
11465 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11466 * can be searched as well if needed.
11467 *
11468 * @param list
11469 * @param aControllerName
11470 * @param aControllerPort
11471 * @param aDevice
11472 * @return
11473 */
11474MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11475 Guid &id)
11476{
11477 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11478 it != ll.end();
11479 ++it)
11480 {
11481 MediumAttachment *pAttach = *it;
11482 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11483 if (pMediumThis->getId() == id)
11484 return pAttach;
11485 }
11486
11487 return NULL;
11488}
11489
11490/**
11491 * Main implementation for Machine::DetachDevice. This also gets called
11492 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11493 *
11494 * @param pAttach Medium attachment to detach.
11495 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11496 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11497 * @return
11498 */
11499HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11500 AutoWriteLock &writeLock,
11501 Snapshot *pSnapshot)
11502{
11503 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11504 DeviceType_T mediumType = pAttach->getType();
11505
11506 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11507
11508 if (pAttach->isImplicit())
11509 {
11510 /* attempt to implicitly delete the implicitly created diff */
11511
11512 /// @todo move the implicit flag from MediumAttachment to Medium
11513 /// and forbid any hard disk operation when it is implicit. Or maybe
11514 /// a special media state for it to make it even more simple.
11515
11516 Assert(mMediaData.isBackedUp());
11517
11518 /* will release the lock before the potentially lengthy operation, so
11519 * protect with the special state */
11520 MachineState_T oldState = mData->mMachineState;
11521 setMachineState(MachineState_SettingUp);
11522
11523 writeLock.release();
11524
11525 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11526 true /*aWait*/);
11527
11528 writeLock.acquire();
11529
11530 setMachineState(oldState);
11531
11532 if (FAILED(rc)) return rc;
11533 }
11534
11535 setModified(IsModified_Storage);
11536 mMediaData.backup();
11537 mMediaData->mAttachments.remove(pAttach);
11538
11539 if (!oldmedium.isNull())
11540 {
11541 // if this is from a snapshot, do not defer detachment to commitMedia()
11542 if (pSnapshot)
11543 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11544 // else if non-hard disk media, do not defer detachment to commitMedia() either
11545 else if (mediumType != DeviceType_HardDisk)
11546 oldmedium->removeBackReference(mData->mUuid);
11547 }
11548
11549 return S_OK;
11550}
11551
11552/**
11553 * Goes thru all media of the given list and
11554 *
11555 * 1) calls detachDevice() on each of them for this machine and
11556 * 2) adds all Medium objects found in the process to the given list,
11557 * depending on cleanupMode.
11558 *
11559 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11560 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11561 * media to the list.
11562 *
11563 * This gets called from Machine::Unregister, both for the actual Machine and
11564 * the SnapshotMachine objects that might be found in the snapshots.
11565 *
11566 * Requires caller and locking. The machine lock must be passed in because it
11567 * will be passed on to detachDevice which needs it for temporary unlocking.
11568 *
11569 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11570 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11571 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11572 * otherwise no media get added.
11573 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11574 * @return
11575 */
11576HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11577 Snapshot *pSnapshot,
11578 CleanupMode_T cleanupMode,
11579 MediaList &llMedia)
11580{
11581 Assert(isWriteLockOnCurrentThread());
11582
11583 HRESULT rc;
11584
11585 // make a temporary list because detachDevice invalidates iterators into
11586 // mMediaData->mAttachments
11587 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11588
11589 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11590 it != llAttachments2.end();
11591 ++it)
11592 {
11593 ComObjPtr<MediumAttachment> &pAttach = *it;
11594 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11595
11596 if (!pMedium.isNull())
11597 {
11598 AutoCaller mac(pMedium);
11599 if (FAILED(mac.rc())) return mac.rc();
11600 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11601 DeviceType_T devType = pMedium->getDeviceType();
11602 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11603 && devType == DeviceType_HardDisk)
11604 || (cleanupMode == CleanupMode_Full)
11605 )
11606 {
11607 llMedia.push_back(pMedium);
11608 ComObjPtr<Medium> pParent = pMedium->getParent();
11609 /*
11610 * Search for medias which are not attached to any machine, but
11611 * in the chain to an attached disk. Mediums are only consided
11612 * if they are:
11613 * - have only one child
11614 * - no references to any machines
11615 * - are of normal medium type
11616 */
11617 while (!pParent.isNull())
11618 {
11619 AutoCaller mac1(pParent);
11620 if (FAILED(mac1.rc())) return mac1.rc();
11621 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11622 if (pParent->getChildren().size() == 1)
11623 {
11624 if ( pParent->getMachineBackRefCount() == 0
11625 && pParent->getType() == MediumType_Normal
11626 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11627 llMedia.push_back(pParent);
11628 }
11629 else
11630 break;
11631 pParent = pParent->getParent();
11632 }
11633 }
11634 }
11635
11636 // real machine: then we need to use the proper method
11637 rc = detachDevice(pAttach, writeLock, pSnapshot);
11638
11639 if (FAILED(rc))
11640 return rc;
11641 }
11642
11643 return S_OK;
11644}
11645
11646/**
11647 * Perform deferred hard disk detachments.
11648 *
11649 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11650 * backed up).
11651 *
11652 * If @a aOnline is @c true then this method will also unlock the old hard disks
11653 * for which the new implicit diffs were created and will lock these new diffs for
11654 * writing.
11655 *
11656 * @param aOnline Whether the VM was online prior to this operation.
11657 *
11658 * @note Locks this object for writing!
11659 */
11660void Machine::commitMedia(bool aOnline /*= false*/)
11661{
11662 AutoCaller autoCaller(this);
11663 AssertComRCReturnVoid(autoCaller.rc());
11664
11665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11666
11667 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11668
11669 HRESULT rc = S_OK;
11670
11671 /* no attach/detach operations -- nothing to do */
11672 if (!mMediaData.isBackedUp())
11673 return;
11674
11675 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11676 bool fMediaNeedsLocking = false;
11677
11678 /* enumerate new attachments */
11679 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11680 it != mMediaData->mAttachments.end();
11681 ++it)
11682 {
11683 MediumAttachment *pAttach = *it;
11684
11685 pAttach->commit();
11686
11687 Medium* pMedium = pAttach->getMedium();
11688 bool fImplicit = pAttach->isImplicit();
11689
11690 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11691 (pMedium) ? pMedium->getName().c_str() : "NULL",
11692 fImplicit));
11693
11694 /** @todo convert all this Machine-based voodoo to MediumAttachment
11695 * based commit logic. */
11696 if (fImplicit)
11697 {
11698 /* convert implicit attachment to normal */
11699 pAttach->setImplicit(false);
11700
11701 if ( aOnline
11702 && pMedium
11703 && pAttach->getType() == DeviceType_HardDisk
11704 )
11705 {
11706 ComObjPtr<Medium> parent = pMedium->getParent();
11707 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11708
11709 /* update the appropriate lock list */
11710 MediumLockList *pMediumLockList;
11711 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11712 AssertComRC(rc);
11713 if (pMediumLockList)
11714 {
11715 /* unlock if there's a need to change the locking */
11716 if (!fMediaNeedsLocking)
11717 {
11718 rc = mData->mSession.mLockedMedia.Unlock();
11719 AssertComRC(rc);
11720 fMediaNeedsLocking = true;
11721 }
11722 rc = pMediumLockList->Update(parent, false);
11723 AssertComRC(rc);
11724 rc = pMediumLockList->Append(pMedium, true);
11725 AssertComRC(rc);
11726 }
11727 }
11728
11729 continue;
11730 }
11731
11732 if (pMedium)
11733 {
11734 /* was this medium attached before? */
11735 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11736 oldIt != oldAtts.end();
11737 ++oldIt)
11738 {
11739 MediumAttachment *pOldAttach = *oldIt;
11740 if (pOldAttach->getMedium() == pMedium)
11741 {
11742 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11743
11744 /* yes: remove from old to avoid de-association */
11745 oldAtts.erase(oldIt);
11746 break;
11747 }
11748 }
11749 }
11750 }
11751
11752 /* enumerate remaining old attachments and de-associate from the
11753 * current machine state */
11754 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11755 it != oldAtts.end();
11756 ++it)
11757 {
11758 MediumAttachment *pAttach = *it;
11759 Medium* pMedium = pAttach->getMedium();
11760
11761 /* Detach only hard disks, since DVD/floppy media is detached
11762 * instantly in MountMedium. */
11763 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11764 {
11765 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11766
11767 /* now de-associate from the current machine state */
11768 rc = pMedium->removeBackReference(mData->mUuid);
11769 AssertComRC(rc);
11770
11771 if (aOnline)
11772 {
11773 /* unlock since medium is not used anymore */
11774 MediumLockList *pMediumLockList;
11775 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11776 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11777 {
11778 /* this happens for online snapshots, there the attachment
11779 * is changing, but only to a diff image created under
11780 * the old one, so there is no separate lock list */
11781 Assert(!pMediumLockList);
11782 }
11783 else
11784 {
11785 AssertComRC(rc);
11786 if (pMediumLockList)
11787 {
11788 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11789 AssertComRC(rc);
11790 }
11791 }
11792 }
11793 }
11794 }
11795
11796 /* take media locks again so that the locking state is consistent */
11797 if (fMediaNeedsLocking)
11798 {
11799 Assert(aOnline);
11800 rc = mData->mSession.mLockedMedia.Lock();
11801 AssertComRC(rc);
11802 }
11803
11804 /* commit the hard disk changes */
11805 mMediaData.commit();
11806
11807 if (isSessionMachine())
11808 {
11809 /*
11810 * Update the parent machine to point to the new owner.
11811 * This is necessary because the stored parent will point to the
11812 * session machine otherwise and cause crashes or errors later
11813 * when the session machine gets invalid.
11814 */
11815 /** @todo Change the MediumAttachment class to behave like any other
11816 * class in this regard by creating peer MediumAttachment
11817 * objects for session machines and share the data with the peer
11818 * machine.
11819 */
11820 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11821 it != mMediaData->mAttachments.end();
11822 ++it)
11823 {
11824 (*it)->updateParentMachine(mPeer);
11825 }
11826
11827 /* attach new data to the primary machine and reshare it */
11828 mPeer->mMediaData.attach(mMediaData);
11829 }
11830
11831 return;
11832}
11833
11834/**
11835 * Perform deferred deletion of implicitly created diffs.
11836 *
11837 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11838 * backed up).
11839 *
11840 * @note Locks this object for writing!
11841 */
11842void Machine::rollbackMedia()
11843{
11844 AutoCaller autoCaller(this);
11845 AssertComRCReturnVoid(autoCaller.rc());
11846
11847 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11848 LogFlowThisFunc(("Entering rollbackMedia\n"));
11849
11850 HRESULT rc = S_OK;
11851
11852 /* no attach/detach operations -- nothing to do */
11853 if (!mMediaData.isBackedUp())
11854 return;
11855
11856 /* enumerate new attachments */
11857 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11858 it != mMediaData->mAttachments.end();
11859 ++it)
11860 {
11861 MediumAttachment *pAttach = *it;
11862 /* Fix up the backrefs for DVD/floppy media. */
11863 if (pAttach->getType() != DeviceType_HardDisk)
11864 {
11865 Medium* pMedium = pAttach->getMedium();
11866 if (pMedium)
11867 {
11868 rc = pMedium->removeBackReference(mData->mUuid);
11869 AssertComRC(rc);
11870 }
11871 }
11872
11873 (*it)->rollback();
11874
11875 pAttach = *it;
11876 /* Fix up the backrefs for DVD/floppy media. */
11877 if (pAttach->getType() != DeviceType_HardDisk)
11878 {
11879 Medium* pMedium = pAttach->getMedium();
11880 if (pMedium)
11881 {
11882 rc = pMedium->addBackReference(mData->mUuid);
11883 AssertComRC(rc);
11884 }
11885 }
11886 }
11887
11888 /** @todo convert all this Machine-based voodoo to MediumAttachment
11889 * based rollback logic. */
11890 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11891
11892 return;
11893}
11894
11895/**
11896 * Returns true if the settings file is located in the directory named exactly
11897 * as the machine; this means, among other things, that the machine directory
11898 * should be auto-renamed.
11899 *
11900 * @param aSettingsDir if not NULL, the full machine settings file directory
11901 * name will be assigned there.
11902 *
11903 * @note Doesn't lock anything.
11904 * @note Not thread safe (must be called from this object's lock).
11905 */
11906bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11907{
11908 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11909 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11910 if (aSettingsDir)
11911 *aSettingsDir = strMachineDirName;
11912 strMachineDirName.stripPath(); // vmname
11913 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11914 strConfigFileOnly.stripPath() // vmname.vbox
11915 .stripExt(); // vmname
11916 /** @todo hack, make somehow use of ComposeMachineFilename */
11917 if (mUserData->s.fDirectoryIncludesUUID)
11918 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11919
11920 AssertReturn(!strMachineDirName.isEmpty(), false);
11921 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11922
11923 return strMachineDirName == strConfigFileOnly;
11924}
11925
11926/**
11927 * Discards all changes to machine settings.
11928 *
11929 * @param aNotify Whether to notify the direct session about changes or not.
11930 *
11931 * @note Locks objects for writing!
11932 */
11933void Machine::rollback(bool aNotify)
11934{
11935 AutoCaller autoCaller(this);
11936 AssertComRCReturn(autoCaller.rc(), (void)0);
11937
11938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11939
11940 if (!mStorageControllers.isNull())
11941 {
11942 if (mStorageControllers.isBackedUp())
11943 {
11944 /* unitialize all new devices (absent in the backed up list). */
11945 StorageControllerList::const_iterator it = mStorageControllers->begin();
11946 StorageControllerList *backedList = mStorageControllers.backedUpData();
11947 while (it != mStorageControllers->end())
11948 {
11949 if ( std::find(backedList->begin(), backedList->end(), *it)
11950 == backedList->end()
11951 )
11952 {
11953 (*it)->uninit();
11954 }
11955 ++it;
11956 }
11957
11958 /* restore the list */
11959 mStorageControllers.rollback();
11960 }
11961
11962 /* rollback any changes to devices after restoring the list */
11963 if (mData->flModifications & IsModified_Storage)
11964 {
11965 StorageControllerList::const_iterator it = mStorageControllers->begin();
11966 while (it != mStorageControllers->end())
11967 {
11968 (*it)->rollback();
11969 ++it;
11970 }
11971 }
11972 }
11973
11974 if (!mUSBControllers.isNull())
11975 {
11976 if (mUSBControllers.isBackedUp())
11977 {
11978 /* unitialize all new devices (absent in the backed up list). */
11979 USBControllerList::const_iterator it = mUSBControllers->begin();
11980 USBControllerList *backedList = mUSBControllers.backedUpData();
11981 while (it != mUSBControllers->end())
11982 {
11983 if ( std::find(backedList->begin(), backedList->end(), *it)
11984 == backedList->end()
11985 )
11986 {
11987 (*it)->uninit();
11988 }
11989 ++it;
11990 }
11991
11992 /* restore the list */
11993 mUSBControllers.rollback();
11994 }
11995
11996 /* rollback any changes to devices after restoring the list */
11997 if (mData->flModifications & IsModified_USB)
11998 {
11999 USBControllerList::const_iterator it = mUSBControllers->begin();
12000 while (it != mUSBControllers->end())
12001 {
12002 (*it)->rollback();
12003 ++it;
12004 }
12005 }
12006 }
12007
12008 mUserData.rollback();
12009
12010 mHWData.rollback();
12011
12012 if (mData->flModifications & IsModified_Storage)
12013 rollbackMedia();
12014
12015 if (mBIOSSettings)
12016 mBIOSSettings->rollback();
12017
12018 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12019 mVRDEServer->rollback();
12020
12021 if (mAudioAdapter)
12022 mAudioAdapter->rollback();
12023
12024 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12025 mUSBDeviceFilters->rollback();
12026
12027 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12028 mBandwidthControl->rollback();
12029
12030 if (!mHWData.isNull())
12031 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12032 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12033 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12034 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12035
12036 if (mData->flModifications & IsModified_NetworkAdapters)
12037 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12038 if ( mNetworkAdapters[slot]
12039 && mNetworkAdapters[slot]->isModified())
12040 {
12041 mNetworkAdapters[slot]->rollback();
12042 networkAdapters[slot] = mNetworkAdapters[slot];
12043 }
12044
12045 if (mData->flModifications & IsModified_SerialPorts)
12046 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12047 if ( mSerialPorts[slot]
12048 && mSerialPorts[slot]->isModified())
12049 {
12050 mSerialPorts[slot]->rollback();
12051 serialPorts[slot] = mSerialPorts[slot];
12052 }
12053
12054 if (mData->flModifications & IsModified_ParallelPorts)
12055 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12056 if ( mParallelPorts[slot]
12057 && mParallelPorts[slot]->isModified())
12058 {
12059 mParallelPorts[slot]->rollback();
12060 parallelPorts[slot] = mParallelPorts[slot];
12061 }
12062
12063 if (aNotify)
12064 {
12065 /* inform the direct session about changes */
12066
12067 ComObjPtr<Machine> that = this;
12068 uint32_t flModifications = mData->flModifications;
12069 alock.release();
12070
12071 if (flModifications & IsModified_SharedFolders)
12072 that->onSharedFolderChange();
12073
12074 if (flModifications & IsModified_VRDEServer)
12075 that->onVRDEServerChange(/* aRestart */ TRUE);
12076 if (flModifications & IsModified_USB)
12077 that->onUSBControllerChange();
12078
12079 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
12080 if (networkAdapters[slot])
12081 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
12082 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
12083 if (serialPorts[slot])
12084 that->onSerialPortChange(serialPorts[slot]);
12085 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
12086 if (parallelPorts[slot])
12087 that->onParallelPortChange(parallelPorts[slot]);
12088
12089 if (flModifications & IsModified_Storage)
12090 that->onStorageControllerChange();
12091
12092#if 0
12093 if (flModifications & IsModified_BandwidthControl)
12094 that->onBandwidthControlChange();
12095#endif
12096 }
12097}
12098
12099/**
12100 * Commits all the changes to machine settings.
12101 *
12102 * Note that this operation is supposed to never fail.
12103 *
12104 * @note Locks this object and children for writing.
12105 */
12106void Machine::commit()
12107{
12108 AutoCaller autoCaller(this);
12109 AssertComRCReturnVoid(autoCaller.rc());
12110
12111 AutoCaller peerCaller(mPeer);
12112 AssertComRCReturnVoid(peerCaller.rc());
12113
12114 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12115
12116 /*
12117 * use safe commit to ensure Snapshot machines (that share mUserData)
12118 * will still refer to a valid memory location
12119 */
12120 mUserData.commitCopy();
12121
12122 mHWData.commit();
12123
12124 if (mMediaData.isBackedUp())
12125 commitMedia(Global::IsOnline(mData->mMachineState));
12126
12127 mBIOSSettings->commit();
12128 mVRDEServer->commit();
12129 mAudioAdapter->commit();
12130 mUSBDeviceFilters->commit();
12131 mBandwidthControl->commit();
12132
12133 /* Since mNetworkAdapters is a list which might have been changed (resized)
12134 * without using the Backupable<> template we need to handle the copying
12135 * of the list entries manually, including the creation of peers for the
12136 * new objects. */
12137 bool commitNetworkAdapters = false;
12138 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12139 if (mPeer)
12140 {
12141 /* commit everything, even the ones which will go away */
12142 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12143 mNetworkAdapters[slot]->commit();
12144 /* copy over the new entries, creating a peer and uninit the original */
12145 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12146 for (size_t slot = 0; slot < newSize; slot++)
12147 {
12148 /* look if this adapter has a peer device */
12149 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
12150 if (!peer)
12151 {
12152 /* no peer means the adapter is a newly created one;
12153 * create a peer owning data this data share it with */
12154 peer.createObject();
12155 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12156 }
12157 mPeer->mNetworkAdapters[slot] = peer;
12158 }
12159 /* uninit any no longer needed network adapters */
12160 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
12161 mNetworkAdapters[slot]->uninit();
12162 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
12163 {
12164 if (mPeer->mNetworkAdapters[slot])
12165 mPeer->mNetworkAdapters[slot]->uninit();
12166 }
12167 /* Keep the original network adapter count until this point, so that
12168 * discarding a chipset type change will not lose settings. */
12169 mNetworkAdapters.resize(newSize);
12170 mPeer->mNetworkAdapters.resize(newSize);
12171 }
12172 else
12173 {
12174 /* we have no peer (our parent is the newly created machine);
12175 * just commit changes to the network adapters */
12176 commitNetworkAdapters = true;
12177 }
12178 if (commitNetworkAdapters)
12179 {
12180 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12181 mNetworkAdapters[slot]->commit();
12182 }
12183
12184 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12185 mSerialPorts[slot]->commit();
12186 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12187 mParallelPorts[slot]->commit();
12188
12189 bool commitStorageControllers = false;
12190
12191 if (mStorageControllers.isBackedUp())
12192 {
12193 mStorageControllers.commit();
12194
12195 if (mPeer)
12196 {
12197 /* Commit all changes to new controllers (this will reshare data with
12198 * peers for those who have peers) */
12199 StorageControllerList *newList = new StorageControllerList();
12200 StorageControllerList::const_iterator it = mStorageControllers->begin();
12201 while (it != mStorageControllers->end())
12202 {
12203 (*it)->commit();
12204
12205 /* look if this controller has a peer device */
12206 ComObjPtr<StorageController> peer = (*it)->getPeer();
12207 if (!peer)
12208 {
12209 /* no peer means the device is a newly created one;
12210 * create a peer owning data this device share it with */
12211 peer.createObject();
12212 peer->init(mPeer, *it, true /* aReshare */);
12213 }
12214 else
12215 {
12216 /* remove peer from the old list */
12217 mPeer->mStorageControllers->remove(peer);
12218 }
12219 /* and add it to the new list */
12220 newList->push_back(peer);
12221
12222 ++it;
12223 }
12224
12225 /* uninit old peer's controllers that are left */
12226 it = mPeer->mStorageControllers->begin();
12227 while (it != mPeer->mStorageControllers->end())
12228 {
12229 (*it)->uninit();
12230 ++it;
12231 }
12232
12233 /* attach new list of controllers to our peer */
12234 mPeer->mStorageControllers.attach(newList);
12235 }
12236 else
12237 {
12238 /* we have no peer (our parent is the newly created machine);
12239 * just commit changes to devices */
12240 commitStorageControllers = true;
12241 }
12242 }
12243 else
12244 {
12245 /* the list of controllers itself is not changed,
12246 * just commit changes to controllers themselves */
12247 commitStorageControllers = true;
12248 }
12249
12250 if (commitStorageControllers)
12251 {
12252 StorageControllerList::const_iterator it = mStorageControllers->begin();
12253 while (it != mStorageControllers->end())
12254 {
12255 (*it)->commit();
12256 ++it;
12257 }
12258 }
12259
12260 bool commitUSBControllers = false;
12261
12262 if (mUSBControllers.isBackedUp())
12263 {
12264 mUSBControllers.commit();
12265
12266 if (mPeer)
12267 {
12268 /* Commit all changes to new controllers (this will reshare data with
12269 * peers for those who have peers) */
12270 USBControllerList *newList = new USBControllerList();
12271 USBControllerList::const_iterator it = mUSBControllers->begin();
12272 while (it != mUSBControllers->end())
12273 {
12274 (*it)->commit();
12275
12276 /* look if this controller has a peer device */
12277 ComObjPtr<USBController> peer = (*it)->getPeer();
12278 if (!peer)
12279 {
12280 /* no peer means the device is a newly created one;
12281 * create a peer owning data this device share it with */
12282 peer.createObject();
12283 peer->init(mPeer, *it, true /* aReshare */);
12284 }
12285 else
12286 {
12287 /* remove peer from the old list */
12288 mPeer->mUSBControllers->remove(peer);
12289 }
12290 /* and add it to the new list */
12291 newList->push_back(peer);
12292
12293 ++it;
12294 }
12295
12296 /* uninit old peer's controllers that are left */
12297 it = mPeer->mUSBControllers->begin();
12298 while (it != mPeer->mUSBControllers->end())
12299 {
12300 (*it)->uninit();
12301 ++it;
12302 }
12303
12304 /* attach new list of controllers to our peer */
12305 mPeer->mUSBControllers.attach(newList);
12306 }
12307 else
12308 {
12309 /* we have no peer (our parent is the newly created machine);
12310 * just commit changes to devices */
12311 commitUSBControllers = true;
12312 }
12313 }
12314 else
12315 {
12316 /* the list of controllers itself is not changed,
12317 * just commit changes to controllers themselves */
12318 commitUSBControllers = true;
12319 }
12320
12321 if (commitUSBControllers)
12322 {
12323 USBControllerList::const_iterator it = mUSBControllers->begin();
12324 while (it != mUSBControllers->end())
12325 {
12326 (*it)->commit();
12327 ++it;
12328 }
12329 }
12330
12331 if (isSessionMachine())
12332 {
12333 /* attach new data to the primary machine and reshare it */
12334 mPeer->mUserData.attach(mUserData);
12335 mPeer->mHWData.attach(mHWData);
12336 /* mMediaData is reshared by fixupMedia */
12337 // mPeer->mMediaData.attach(mMediaData);
12338 Assert(mPeer->mMediaData.data() == mMediaData.data());
12339 }
12340}
12341
12342/**
12343 * Copies all the hardware data from the given machine.
12344 *
12345 * Currently, only called when the VM is being restored from a snapshot. In
12346 * particular, this implies that the VM is not running during this method's
12347 * call.
12348 *
12349 * @note This method must be called from under this object's lock.
12350 *
12351 * @note This method doesn't call #commit(), so all data remains backed up and
12352 * unsaved.
12353 */
12354void Machine::copyFrom(Machine *aThat)
12355{
12356 AssertReturnVoid(!isSnapshotMachine());
12357 AssertReturnVoid(aThat->isSnapshotMachine());
12358
12359 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12360
12361 mHWData.assignCopy(aThat->mHWData);
12362
12363 // create copies of all shared folders (mHWData after attaching a copy
12364 // contains just references to original objects)
12365 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12366 it != mHWData->mSharedFolders.end();
12367 ++it)
12368 {
12369 ComObjPtr<SharedFolder> folder;
12370 folder.createObject();
12371 HRESULT rc = folder->initCopy(getMachine(), *it);
12372 AssertComRC(rc);
12373 *it = folder;
12374 }
12375
12376 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12377 mVRDEServer->copyFrom(aThat->mVRDEServer);
12378 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12379 mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters);
12380 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12381
12382 /* create private copies of all controllers */
12383 mStorageControllers.backup();
12384 mStorageControllers->clear();
12385 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12386 it != aThat->mStorageControllers->end();
12387 ++it)
12388 {
12389 ComObjPtr<StorageController> ctrl;
12390 ctrl.createObject();
12391 ctrl->initCopy(this, *it);
12392 mStorageControllers->push_back(ctrl);
12393 }
12394
12395 /* create private copies of all USB controllers */
12396 mUSBControllers.backup();
12397 mUSBControllers->clear();
12398 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12399 it != aThat->mUSBControllers->end();
12400 ++it)
12401 {
12402 ComObjPtr<USBController> ctrl;
12403 ctrl.createObject();
12404 ctrl->initCopy(this, *it);
12405 mUSBControllers->push_back(ctrl);
12406 }
12407
12408 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12409 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12410 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12411 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12412 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12413 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12414 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12415}
12416
12417/**
12418 * Returns whether the given storage controller is hotplug capable.
12419 *
12420 * @returns true if the controller supports hotplugging
12421 * false otherwise.
12422 * @param enmCtrlType The controller type to check for.
12423 */
12424bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12425{
12426 switch (enmCtrlType)
12427 {
12428 case StorageControllerType_IntelAhci:
12429 return true;
12430 case StorageControllerType_LsiLogic:
12431 case StorageControllerType_LsiLogicSas:
12432 case StorageControllerType_BusLogic:
12433 case StorageControllerType_PIIX3:
12434 case StorageControllerType_PIIX4:
12435 case StorageControllerType_ICH6:
12436 case StorageControllerType_I82078:
12437 default:
12438 return false;
12439 }
12440}
12441
12442#ifdef VBOX_WITH_RESOURCE_USAGE_API
12443
12444void Machine::getDiskList(MediaList &list)
12445{
12446 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12447 it != mMediaData->mAttachments.end();
12448 ++it)
12449 {
12450 MediumAttachment* pAttach = *it;
12451 /* just in case */
12452 AssertStmt(pAttach, continue);
12453
12454 AutoCaller localAutoCallerA(pAttach);
12455 if (FAILED(localAutoCallerA.rc())) continue;
12456
12457 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12458
12459 if (pAttach->getType() == DeviceType_HardDisk)
12460 list.push_back(pAttach->getMedium());
12461 }
12462}
12463
12464void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12465{
12466 AssertReturnVoid(isWriteLockOnCurrentThread());
12467 AssertPtrReturnVoid(aCollector);
12468
12469 pm::CollectorHAL *hal = aCollector->getHAL();
12470 /* Create sub metrics */
12471 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12472 "Percentage of processor time spent in user mode by the VM process.");
12473 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12474 "Percentage of processor time spent in kernel mode by the VM process.");
12475 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12476 "Size of resident portion of VM process in memory.");
12477 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12478 "Actual size of all VM disks combined.");
12479 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12480 "Network receive rate.");
12481 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12482 "Network transmit rate.");
12483 /* Create and register base metrics */
12484 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12485 cpuLoadUser, cpuLoadKernel);
12486 aCollector->registerBaseMetric(cpuLoad);
12487 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12488 ramUsageUsed);
12489 aCollector->registerBaseMetric(ramUsage);
12490 MediaList disks;
12491 getDiskList(disks);
12492 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12493 diskUsageUsed);
12494 aCollector->registerBaseMetric(diskUsage);
12495
12496 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12497 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12498 new pm::AggregateAvg()));
12499 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12500 new pm::AggregateMin()));
12501 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12502 new pm::AggregateMax()));
12503 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12504 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12505 new pm::AggregateAvg()));
12506 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12507 new pm::AggregateMin()));
12508 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12509 new pm::AggregateMax()));
12510
12511 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12512 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12513 new pm::AggregateAvg()));
12514 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12515 new pm::AggregateMin()));
12516 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12517 new pm::AggregateMax()));
12518
12519 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12520 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12521 new pm::AggregateAvg()));
12522 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12523 new pm::AggregateMin()));
12524 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12525 new pm::AggregateMax()));
12526
12527
12528 /* Guest metrics collector */
12529 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12530 aCollector->registerGuest(mCollectorGuest);
12531 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12532 this, __PRETTY_FUNCTION__, mCollectorGuest));
12533
12534 /* Create sub metrics */
12535 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12536 "Percentage of processor time spent in user mode as seen by the guest.");
12537 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12538 "Percentage of processor time spent in kernel mode as seen by the guest.");
12539 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12540 "Percentage of processor time spent idling as seen by the guest.");
12541
12542 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12543 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12544 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12545 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12546 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12547 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12548
12549 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12550
12551 /* Create and register base metrics */
12552 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12553 machineNetRx, machineNetTx);
12554 aCollector->registerBaseMetric(machineNetRate);
12555
12556 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12557 guestLoadUser, guestLoadKernel, guestLoadIdle);
12558 aCollector->registerBaseMetric(guestCpuLoad);
12559
12560 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12561 guestMemTotal, guestMemFree,
12562 guestMemBalloon, guestMemShared,
12563 guestMemCache, guestPagedTotal);
12564 aCollector->registerBaseMetric(guestCpuMem);
12565
12566 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12567 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12568 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12569 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12570
12571 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12572 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12573 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12574 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12575
12576 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12577 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12578 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12579 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12580
12581 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12582 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12583 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12584 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12585
12586 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12587 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12588 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12589 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12590
12591 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12592 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12593 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12594 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12595
12596 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12597 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12598 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12599 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12600
12601 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12602 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12603 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12604 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12605
12606 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12607 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12608 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12609 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12610
12611 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12613 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12614 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12615
12616 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12617 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12618 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12619 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12620}
12621
12622void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12623{
12624 AssertReturnVoid(isWriteLockOnCurrentThread());
12625
12626 if (aCollector)
12627 {
12628 aCollector->unregisterMetricsFor(aMachine);
12629 aCollector->unregisterBaseMetricsFor(aMachine);
12630 }
12631}
12632
12633#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12634
12635
12636////////////////////////////////////////////////////////////////////////////////
12637
12638DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12639
12640HRESULT SessionMachine::FinalConstruct()
12641{
12642 LogFlowThisFunc(("\n"));
12643
12644 mClientToken = NULL;
12645
12646 return BaseFinalConstruct();
12647}
12648
12649void SessionMachine::FinalRelease()
12650{
12651 LogFlowThisFunc(("\n"));
12652
12653 Assert(!mClientToken);
12654 /* paranoia, should not hang around any more */
12655 if (mClientToken)
12656 {
12657 delete mClientToken;
12658 mClientToken = NULL;
12659 }
12660
12661 uninit(Uninit::Unexpected);
12662
12663 BaseFinalRelease();
12664}
12665
12666/**
12667 * @note Must be called only by Machine::LockMachine() from its own write lock.
12668 */
12669HRESULT SessionMachine::init(Machine *aMachine)
12670{
12671 LogFlowThisFuncEnter();
12672 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12673
12674 AssertReturn(aMachine, E_INVALIDARG);
12675
12676 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12677
12678 /* Enclose the state transition NotReady->InInit->Ready */
12679 AutoInitSpan autoInitSpan(this);
12680 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12681
12682 HRESULT rc = S_OK;
12683
12684 /* create the machine client token */
12685 try
12686 {
12687 mClientToken = new ClientToken(aMachine);
12688 if (!mClientToken->isReady())
12689 {
12690 delete mClientToken;
12691 mClientToken = NULL;
12692 rc = E_FAIL;
12693 }
12694 }
12695 catch (std::bad_alloc &)
12696 {
12697 rc = E_OUTOFMEMORY;
12698 }
12699 if (FAILED(rc))
12700 return rc;
12701
12702 /* memorize the peer Machine */
12703 unconst(mPeer) = aMachine;
12704 /* share the parent pointer */
12705 unconst(mParent) = aMachine->mParent;
12706
12707 /* take the pointers to data to share */
12708 mData.share(aMachine->mData);
12709 mSSData.share(aMachine->mSSData);
12710
12711 mUserData.share(aMachine->mUserData);
12712 mHWData.share(aMachine->mHWData);
12713 mMediaData.share(aMachine->mMediaData);
12714
12715 mStorageControllers.allocate();
12716 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12717 it != aMachine->mStorageControllers->end();
12718 ++it)
12719 {
12720 ComObjPtr<StorageController> ctl;
12721 ctl.createObject();
12722 ctl->init(this, *it);
12723 mStorageControllers->push_back(ctl);
12724 }
12725
12726 mUSBControllers.allocate();
12727 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12728 it != aMachine->mUSBControllers->end();
12729 ++it)
12730 {
12731 ComObjPtr<USBController> ctl;
12732 ctl.createObject();
12733 ctl->init(this, *it);
12734 mUSBControllers->push_back(ctl);
12735 }
12736
12737 unconst(mBIOSSettings).createObject();
12738 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12739 /* create another VRDEServer object that will be mutable */
12740 unconst(mVRDEServer).createObject();
12741 mVRDEServer->init(this, aMachine->mVRDEServer);
12742 /* create another audio adapter object that will be mutable */
12743 unconst(mAudioAdapter).createObject();
12744 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12745 /* create a list of serial ports that will be mutable */
12746 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12747 {
12748 unconst(mSerialPorts[slot]).createObject();
12749 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12750 }
12751 /* create a list of parallel ports that will be mutable */
12752 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12753 {
12754 unconst(mParallelPorts[slot]).createObject();
12755 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12756 }
12757
12758 /* create another USB device filters object that will be mutable */
12759 unconst(mUSBDeviceFilters).createObject();
12760 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12761
12762 /* create a list of network adapters that will be mutable */
12763 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12764 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12765 {
12766 unconst(mNetworkAdapters[slot]).createObject();
12767 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12768 }
12769
12770 /* create another bandwidth control object that will be mutable */
12771 unconst(mBandwidthControl).createObject();
12772 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12773
12774 /* default is to delete saved state on Saved -> PoweredOff transition */
12775 mRemoveSavedState = true;
12776
12777 /* Confirm a successful initialization when it's the case */
12778 autoInitSpan.setSucceeded();
12779
12780 LogFlowThisFuncLeave();
12781 return rc;
12782}
12783
12784/**
12785 * Uninitializes this session object. If the reason is other than
12786 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12787 *
12788 * @param aReason uninitialization reason
12789 *
12790 * @note Locks mParent + this object for writing.
12791 */
12792void SessionMachine::uninit(Uninit::Reason aReason)
12793{
12794 LogFlowThisFuncEnter();
12795 LogFlowThisFunc(("reason=%d\n", aReason));
12796
12797 /*
12798 * Strongly reference ourselves to prevent this object deletion after
12799 * mData->mSession.mMachine.setNull() below (which can release the last
12800 * reference and call the destructor). Important: this must be done before
12801 * accessing any members (and before AutoUninitSpan that does it as well).
12802 * This self reference will be released as the very last step on return.
12803 */
12804 ComObjPtr<SessionMachine> selfRef = this;
12805
12806 /* Enclose the state transition Ready->InUninit->NotReady */
12807 AutoUninitSpan autoUninitSpan(this);
12808 if (autoUninitSpan.uninitDone())
12809 {
12810 LogFlowThisFunc(("Already uninitialized\n"));
12811 LogFlowThisFuncLeave();
12812 return;
12813 }
12814
12815 if (autoUninitSpan.initFailed())
12816 {
12817 /* We've been called by init() because it's failed. It's not really
12818 * necessary (nor it's safe) to perform the regular uninit sequence
12819 * below, the following is enough.
12820 */
12821 LogFlowThisFunc(("Initialization failed.\n"));
12822 /* destroy the machine client token */
12823 if (mClientToken)
12824 {
12825 delete mClientToken;
12826 mClientToken = NULL;
12827 }
12828 uninitDataAndChildObjects();
12829 mData.free();
12830 unconst(mParent) = NULL;
12831 unconst(mPeer) = NULL;
12832 LogFlowThisFuncLeave();
12833 return;
12834 }
12835
12836 MachineState_T lastState;
12837 {
12838 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12839 lastState = mData->mMachineState;
12840 }
12841 NOREF(lastState);
12842
12843#ifdef VBOX_WITH_USB
12844 // release all captured USB devices, but do this before requesting the locks below
12845 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12846 {
12847 /* Console::captureUSBDevices() is called in the VM process only after
12848 * setting the machine state to Starting or Restoring.
12849 * Console::detachAllUSBDevices() will be called upon successful
12850 * termination. So, we need to release USB devices only if there was
12851 * an abnormal termination of a running VM.
12852 *
12853 * This is identical to SessionMachine::DetachAllUSBDevices except
12854 * for the aAbnormal argument. */
12855 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
12856 AssertComRC(rc);
12857 NOREF(rc);
12858
12859 USBProxyService *service = mParent->host()->usbProxyService();
12860 if (service)
12861 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12862 }
12863#endif /* VBOX_WITH_USB */
12864
12865 // we need to lock this object in uninit() because the lock is shared
12866 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12867 // and others need mParent lock, and USB needs host lock.
12868 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12869
12870#ifdef VBOX_WITH_RESOURCE_USAGE_API
12871 /*
12872 * It is safe to call Machine::unregisterMetrics() here because
12873 * PerformanceCollector::samplerCallback no longer accesses guest methods
12874 * holding the lock.
12875 */
12876 unregisterMetrics(mParent->performanceCollector(), mPeer);
12877 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12878 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12879 this, __PRETTY_FUNCTION__, mCollectorGuest));
12880 if (mCollectorGuest)
12881 {
12882 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12883 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12884 mCollectorGuest = NULL;
12885 }
12886#endif
12887
12888 if (aReason == Uninit::Abnormal)
12889 {
12890 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12891 Global::IsOnlineOrTransient(lastState)));
12892
12893 /* reset the state to Aborted */
12894 if (mData->mMachineState != MachineState_Aborted)
12895 setMachineState(MachineState_Aborted);
12896 }
12897
12898 // any machine settings modified?
12899 if (mData->flModifications)
12900 {
12901 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12902 rollback(false /* aNotify */);
12903 }
12904
12905 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12906 || !mConsoleTaskData.mSnapshot);
12907 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12908 {
12909 LogWarningThisFunc(("canceling failed save state request!\n"));
12910 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12911 }
12912 else if (!mConsoleTaskData.mSnapshot.isNull())
12913 {
12914 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12915
12916 /* delete all differencing hard disks created (this will also attach
12917 * their parents back by rolling back mMediaData) */
12918 rollbackMedia();
12919
12920 // delete the saved state file (it might have been already created)
12921 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12922 // think it's still in use
12923 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12924 mConsoleTaskData.mSnapshot->uninit();
12925 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12926 }
12927
12928 if (!mData->mSession.mType.isEmpty())
12929 {
12930 /* mType is not null when this machine's process has been started by
12931 * Machine::LaunchVMProcess(), therefore it is our child. We
12932 * need to queue the PID to reap the process (and avoid zombies on
12933 * Linux). */
12934 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12935 mParent->addProcessToReap(mData->mSession.mPID);
12936 }
12937
12938 mData->mSession.mPID = NIL_RTPROCESS;
12939
12940 if (aReason == Uninit::Unexpected)
12941 {
12942 /* Uninitialization didn't come from #checkForDeath(), so tell the
12943 * client watcher thread to update the set of machines that have open
12944 * sessions. */
12945 mParent->updateClientWatcher();
12946 }
12947
12948 /* uninitialize all remote controls */
12949 if (mData->mSession.mRemoteControls.size())
12950 {
12951 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12952 mData->mSession.mRemoteControls.size()));
12953
12954 Data::Session::RemoteControlList::iterator it =
12955 mData->mSession.mRemoteControls.begin();
12956 while (it != mData->mSession.mRemoteControls.end())
12957 {
12958 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12959 HRESULT rc = (*it)->Uninitialize();
12960 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12961 if (FAILED(rc))
12962 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12963 ++it;
12964 }
12965 mData->mSession.mRemoteControls.clear();
12966 }
12967
12968 /*
12969 * An expected uninitialization can come only from #checkForDeath().
12970 * Otherwise it means that something's gone really wrong (for example,
12971 * the Session implementation has released the VirtualBox reference
12972 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12973 * etc). However, it's also possible, that the client releases the IPC
12974 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12975 * but the VirtualBox release event comes first to the server process.
12976 * This case is practically possible, so we should not assert on an
12977 * unexpected uninit, just log a warning.
12978 */
12979
12980 if ((aReason == Uninit::Unexpected))
12981 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12982
12983 if (aReason != Uninit::Normal)
12984 {
12985 mData->mSession.mDirectControl.setNull();
12986 }
12987 else
12988 {
12989 /* this must be null here (see #OnSessionEnd()) */
12990 Assert(mData->mSession.mDirectControl.isNull());
12991 Assert(mData->mSession.mState == SessionState_Unlocking);
12992 Assert(!mData->mSession.mProgress.isNull());
12993 }
12994 if (mData->mSession.mProgress)
12995 {
12996 if (aReason == Uninit::Normal)
12997 mData->mSession.mProgress->notifyComplete(S_OK);
12998 else
12999 mData->mSession.mProgress->notifyComplete(E_FAIL,
13000 COM_IIDOF(ISession),
13001 getComponentName(),
13002 tr("The VM session was aborted"));
13003 mData->mSession.mProgress.setNull();
13004 }
13005
13006 /* remove the association between the peer machine and this session machine */
13007 Assert( (SessionMachine*)mData->mSession.mMachine == this
13008 || aReason == Uninit::Unexpected);
13009
13010 /* reset the rest of session data */
13011 mData->mSession.mMachine.setNull();
13012 mData->mSession.mState = SessionState_Unlocked;
13013 mData->mSession.mType.setNull();
13014
13015 /* destroy the machine client token before leaving the exclusive lock */
13016 if (mClientToken)
13017 {
13018 delete mClientToken;
13019 mClientToken = NULL;
13020 }
13021
13022 /* fire an event */
13023 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13024
13025 uninitDataAndChildObjects();
13026
13027 /* free the essential data structure last */
13028 mData.free();
13029
13030 /* release the exclusive lock before setting the below two to NULL */
13031 multilock.release();
13032
13033 RTThreadSleep(500);
13034 mParent->AddRef();
13035 LONG c = mParent->Release();
13036 LogFlowThisFunc(("vbox ref=%d\n", c)); NOREF(c);
13037 unconst(mParent) = NULL;
13038 unconst(mPeer) = NULL;
13039
13040 LogFlowThisFuncLeave();
13041}
13042
13043// util::Lockable interface
13044////////////////////////////////////////////////////////////////////////////////
13045
13046/**
13047 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13048 * with the primary Machine instance (mPeer).
13049 */
13050RWLockHandle *SessionMachine::lockHandle() const
13051{
13052 AssertReturn(mPeer != NULL, NULL);
13053 return mPeer->lockHandle();
13054}
13055
13056// IInternalMachineControl methods
13057////////////////////////////////////////////////////////////////////////////////
13058
13059/**
13060 * Passes collected guest statistics to performance collector object
13061 */
13062STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13063 ULONG aCpuKernel, ULONG aCpuIdle,
13064 ULONG aMemTotal, ULONG aMemFree,
13065 ULONG aMemBalloon, ULONG aMemShared,
13066 ULONG aMemCache, ULONG aPageTotal,
13067 ULONG aAllocVMM, ULONG aFreeVMM,
13068 ULONG aBalloonedVMM, ULONG aSharedVMM,
13069 ULONG aVmNetRx, ULONG aVmNetTx)
13070{
13071#ifdef VBOX_WITH_RESOURCE_USAGE_API
13072 if (mCollectorGuest)
13073 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13074 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13075 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13076 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13077
13078 return S_OK;
13079#else
13080 NOREF(aValidStats);
13081 NOREF(aCpuUser);
13082 NOREF(aCpuKernel);
13083 NOREF(aCpuIdle);
13084 NOREF(aMemTotal);
13085 NOREF(aMemFree);
13086 NOREF(aMemBalloon);
13087 NOREF(aMemShared);
13088 NOREF(aMemCache);
13089 NOREF(aPageTotal);
13090 NOREF(aAllocVMM);
13091 NOREF(aFreeVMM);
13092 NOREF(aBalloonedVMM);
13093 NOREF(aSharedVMM);
13094 NOREF(aVmNetRx);
13095 NOREF(aVmNetTx);
13096 return E_NOTIMPL;
13097#endif
13098}
13099
13100/**
13101 * @note Locks this object for writing.
13102 */
13103STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
13104{
13105 AutoCaller autoCaller(this);
13106 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13107
13108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13109
13110 mRemoveSavedState = aRemove;
13111
13112 return S_OK;
13113}
13114
13115/**
13116 * @note Locks the same as #setMachineState() does.
13117 */
13118STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
13119{
13120 return setMachineState(aMachineState);
13121}
13122
13123/**
13124 * @note Locks this object for writing.
13125 */
13126STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
13127{
13128 LogFlowThisFunc(("aProgress=%p\n", aProgress));
13129 AutoCaller autoCaller(this);
13130 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13131
13132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13133
13134 if (mData->mSession.mState != SessionState_Locked)
13135 return VBOX_E_INVALID_OBJECT_STATE;
13136
13137 if (!mData->mSession.mProgress.isNull())
13138 mData->mSession.mProgress->setOtherProgressObject(aProgress);
13139
13140 LogFlowThisFunc(("returns S_OK.\n"));
13141 return S_OK;
13142}
13143
13144/**
13145 * @note Locks this object for writing.
13146 */
13147STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
13148{
13149 AutoCaller autoCaller(this);
13150 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13151
13152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13153
13154 if (mData->mSession.mState != SessionState_Locked)
13155 return VBOX_E_INVALID_OBJECT_STATE;
13156
13157 /* Finalize the LaunchVMProcess progress object. */
13158 if (mData->mSession.mProgress)
13159 {
13160 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
13161 mData->mSession.mProgress.setNull();
13162 }
13163
13164 if (SUCCEEDED((HRESULT)iResult))
13165 {
13166#ifdef VBOX_WITH_RESOURCE_USAGE_API
13167 /* The VM has been powered up successfully, so it makes sense
13168 * now to offer the performance metrics for a running machine
13169 * object. Doing it earlier wouldn't be safe. */
13170 registerMetrics(mParent->performanceCollector(), mPeer,
13171 mData->mSession.mPID);
13172#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13173 }
13174
13175 return S_OK;
13176}
13177
13178/**
13179 * @note Locks this object for writing.
13180 */
13181STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13182{
13183 LogFlowThisFuncEnter();
13184
13185 CheckComArgOutPointerValid(aProgress);
13186
13187 AutoCaller autoCaller(this);
13188 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13189
13190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13191
13192 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13193 E_FAIL);
13194
13195 /* create a progress object to track operation completion */
13196 ComObjPtr<Progress> pProgress;
13197 pProgress.createObject();
13198 pProgress->init(getVirtualBox(),
13199 static_cast<IMachine *>(this) /* aInitiator */,
13200 Bstr(tr("Stopping the virtual machine")).raw(),
13201 FALSE /* aCancelable */);
13202
13203 /* fill in the console task data */
13204 mConsoleTaskData.mLastState = mData->mMachineState;
13205 mConsoleTaskData.mProgress = pProgress;
13206
13207 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13208 setMachineState(MachineState_Stopping);
13209
13210 pProgress.queryInterfaceTo(aProgress);
13211
13212 return S_OK;
13213}
13214
13215/**
13216 * @note Locks this object for writing.
13217 */
13218STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13219{
13220 LogFlowThisFuncEnter();
13221
13222 AutoCaller autoCaller(this);
13223 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13224
13225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13226
13227 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13228 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13229 && mConsoleTaskData.mLastState != MachineState_Null,
13230 E_FAIL);
13231
13232 /*
13233 * On failure, set the state to the state we had when BeginPoweringDown()
13234 * was called (this is expected by Console::PowerDown() and the associated
13235 * task). On success the VM process already changed the state to
13236 * MachineState_PoweredOff, so no need to do anything.
13237 */
13238 if (FAILED(iResult))
13239 setMachineState(mConsoleTaskData.mLastState);
13240
13241 /* notify the progress object about operation completion */
13242 Assert(mConsoleTaskData.mProgress);
13243 if (SUCCEEDED(iResult))
13244 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13245 else
13246 {
13247 Utf8Str strErrMsg(aErrMsg);
13248 if (strErrMsg.length())
13249 mConsoleTaskData.mProgress->notifyComplete(iResult,
13250 COM_IIDOF(ISession),
13251 getComponentName(),
13252 strErrMsg.c_str());
13253 else
13254 mConsoleTaskData.mProgress->notifyComplete(iResult);
13255 }
13256
13257 /* clear out the temporary saved state data */
13258 mConsoleTaskData.mLastState = MachineState_Null;
13259 mConsoleTaskData.mProgress.setNull();
13260
13261 LogFlowThisFuncLeave();
13262 return S_OK;
13263}
13264
13265
13266/**
13267 * Goes through the USB filters of the given machine to see if the given
13268 * device matches any filter or not.
13269 *
13270 * @note Locks the same as USBController::hasMatchingFilter() does.
13271 */
13272STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13273 BOOL *aMatched,
13274 ULONG *aMaskedIfs)
13275{
13276 LogFlowThisFunc(("\n"));
13277
13278 CheckComArgNotNull(aUSBDevice);
13279 CheckComArgOutPointerValid(aMatched);
13280
13281 AutoCaller autoCaller(this);
13282 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13283
13284#ifdef VBOX_WITH_USB
13285 *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13286#else
13287 NOREF(aUSBDevice);
13288 NOREF(aMaskedIfs);
13289 *aMatched = FALSE;
13290#endif
13291
13292 return S_OK;
13293}
13294
13295/**
13296 * @note Locks the same as Host::captureUSBDevice() does.
13297 */
13298STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13299{
13300 LogFlowThisFunc(("\n"));
13301
13302 AutoCaller autoCaller(this);
13303 AssertComRCReturnRC(autoCaller.rc());
13304
13305#ifdef VBOX_WITH_USB
13306 /* if captureDeviceForVM() fails, it must have set extended error info */
13307 clearError();
13308 MultiResult rc = mParent->host()->checkUSBProxyService();
13309 if (FAILED(rc)) return rc;
13310
13311 USBProxyService *service = mParent->host()->usbProxyService();
13312 AssertReturn(service, E_FAIL);
13313 return service->captureDeviceForVM(this, Guid(aId).ref());
13314#else
13315 NOREF(aId);
13316 return E_NOTIMPL;
13317#endif
13318}
13319
13320/**
13321 * @note Locks the same as Host::detachUSBDevice() does.
13322 */
13323STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13324{
13325 LogFlowThisFunc(("\n"));
13326
13327 AutoCaller autoCaller(this);
13328 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13329
13330#ifdef VBOX_WITH_USB
13331 USBProxyService *service = mParent->host()->usbProxyService();
13332 AssertReturn(service, E_FAIL);
13333 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13334#else
13335 NOREF(aId);
13336 NOREF(aDone);
13337 return E_NOTIMPL;
13338#endif
13339}
13340
13341/**
13342 * Inserts all machine filters to the USB proxy service and then calls
13343 * Host::autoCaptureUSBDevices().
13344 *
13345 * Called by Console from the VM process upon VM startup.
13346 *
13347 * @note Locks what called methods lock.
13348 */
13349STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13350{
13351 LogFlowThisFunc(("\n"));
13352
13353 AutoCaller autoCaller(this);
13354 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13355
13356#ifdef VBOX_WITH_USB
13357 HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);
13358 AssertComRC(rc);
13359 NOREF(rc);
13360
13361 USBProxyService *service = mParent->host()->usbProxyService();
13362 AssertReturn(service, E_FAIL);
13363 return service->autoCaptureDevicesForVM(this);
13364#else
13365 return S_OK;
13366#endif
13367}
13368
13369/**
13370 * Removes all machine filters from the USB proxy service and then calls
13371 * Host::detachAllUSBDevices().
13372 *
13373 * Called by Console from the VM process upon normal VM termination or by
13374 * SessionMachine::uninit() upon abnormal VM termination (from under the
13375 * Machine/SessionMachine lock).
13376 *
13377 * @note Locks what called methods lock.
13378 */
13379STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13380{
13381 LogFlowThisFunc(("\n"));
13382
13383 AutoCaller autoCaller(this);
13384 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13385
13386#ifdef VBOX_WITH_USB
13387 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
13388 AssertComRC(rc);
13389 NOREF(rc);
13390
13391 USBProxyService *service = mParent->host()->usbProxyService();
13392 AssertReturn(service, E_FAIL);
13393 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13394#else
13395 NOREF(aDone);
13396 return S_OK;
13397#endif
13398}
13399
13400/**
13401 * @note Locks this object for writing.
13402 */
13403STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13404 IProgress **aProgress)
13405{
13406 LogFlowThisFuncEnter();
13407
13408 AssertReturn(aSession, E_INVALIDARG);
13409 AssertReturn(aProgress, E_INVALIDARG);
13410
13411 AutoCaller autoCaller(this);
13412
13413 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13414 /*
13415 * We don't assert below because it might happen that a non-direct session
13416 * informs us it is closed right after we've been uninitialized -- it's ok.
13417 */
13418 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13419
13420 /* get IInternalSessionControl interface */
13421 ComPtr<IInternalSessionControl> control(aSession);
13422
13423 ComAssertRet(!control.isNull(), E_INVALIDARG);
13424
13425 /* Creating a Progress object requires the VirtualBox lock, and
13426 * thus locking it here is required by the lock order rules. */
13427 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13428
13429 if (control == mData->mSession.mDirectControl)
13430 {
13431 ComAssertRet(aProgress, E_POINTER);
13432
13433 /* The direct session is being normally closed by the client process
13434 * ----------------------------------------------------------------- */
13435
13436 /* go to the closing state (essential for all open*Session() calls and
13437 * for #checkForDeath()) */
13438 Assert(mData->mSession.mState == SessionState_Locked);
13439 mData->mSession.mState = SessionState_Unlocking;
13440
13441 /* set direct control to NULL to release the remote instance */
13442 mData->mSession.mDirectControl.setNull();
13443 LogFlowThisFunc(("Direct control is set to NULL\n"));
13444
13445 if (mData->mSession.mProgress)
13446 {
13447 /* finalize the progress, someone might wait if a frontend
13448 * closes the session before powering on the VM. */
13449 mData->mSession.mProgress->notifyComplete(E_FAIL,
13450 COM_IIDOF(ISession),
13451 getComponentName(),
13452 tr("The VM session was closed before any attempt to power it on"));
13453 mData->mSession.mProgress.setNull();
13454 }
13455
13456 /* Create the progress object the client will use to wait until
13457 * #checkForDeath() is called to uninitialize this session object after
13458 * it releases the IPC semaphore.
13459 * Note! Because we're "reusing" mProgress here, this must be a proxy
13460 * object just like for LaunchVMProcess. */
13461 Assert(mData->mSession.mProgress.isNull());
13462 ComObjPtr<ProgressProxy> progress;
13463 progress.createObject();
13464 ComPtr<IUnknown> pPeer(mPeer);
13465 progress->init(mParent, pPeer,
13466 Bstr(tr("Closing session")).raw(),
13467 FALSE /* aCancelable */);
13468 progress.queryInterfaceTo(aProgress);
13469 mData->mSession.mProgress = progress;
13470 }
13471 else
13472 {
13473 /* the remote session is being normally closed */
13474 Data::Session::RemoteControlList::iterator it =
13475 mData->mSession.mRemoteControls.begin();
13476 while (it != mData->mSession.mRemoteControls.end())
13477 {
13478 if (control == *it)
13479 break;
13480 ++it;
13481 }
13482 BOOL found = it != mData->mSession.mRemoteControls.end();
13483 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13484 E_INVALIDARG);
13485 // This MUST be erase(it), not remove(*it) as the latter triggers a
13486 // very nasty use after free due to the place where the value "lives".
13487 mData->mSession.mRemoteControls.erase(it);
13488 }
13489
13490 /* signal the client watcher thread, because the client is going away */
13491 mParent->updateClientWatcher();
13492
13493 LogFlowThisFuncLeave();
13494 return S_OK;
13495}
13496
13497/**
13498 * @note Locks this object for writing.
13499 */
13500STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13501{
13502 LogFlowThisFuncEnter();
13503
13504 CheckComArgOutPointerValid(aProgress);
13505 CheckComArgOutPointerValid(aStateFilePath);
13506
13507 AutoCaller autoCaller(this);
13508 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13509
13510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13511
13512 AssertReturn( mData->mMachineState == MachineState_Paused
13513 && mConsoleTaskData.mLastState == MachineState_Null
13514 && mConsoleTaskData.strStateFilePath.isEmpty(),
13515 E_FAIL);
13516
13517 /* create a progress object to track operation completion */
13518 ComObjPtr<Progress> pProgress;
13519 pProgress.createObject();
13520 pProgress->init(getVirtualBox(),
13521 static_cast<IMachine *>(this) /* aInitiator */,
13522 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13523 FALSE /* aCancelable */);
13524
13525 Utf8Str strStateFilePath;
13526 /* stateFilePath is null when the machine is not running */
13527 if (mData->mMachineState == MachineState_Paused)
13528 composeSavedStateFilename(strStateFilePath);
13529
13530 /* fill in the console task data */
13531 mConsoleTaskData.mLastState = mData->mMachineState;
13532 mConsoleTaskData.strStateFilePath = strStateFilePath;
13533 mConsoleTaskData.mProgress = pProgress;
13534
13535 /* set the state to Saving (this is expected by Console::SaveState()) */
13536 setMachineState(MachineState_Saving);
13537
13538 strStateFilePath.cloneTo(aStateFilePath);
13539 pProgress.queryInterfaceTo(aProgress);
13540
13541 return S_OK;
13542}
13543
13544/**
13545 * @note Locks mParent + this object for writing.
13546 */
13547STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13548{
13549 LogFlowThisFunc(("\n"));
13550
13551 AutoCaller autoCaller(this);
13552 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13553
13554 /* endSavingState() need mParent lock */
13555 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13556
13557 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13558 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13559 && mConsoleTaskData.mLastState != MachineState_Null
13560 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13561 E_FAIL);
13562
13563 /*
13564 * On failure, set the state to the state we had when BeginSavingState()
13565 * was called (this is expected by Console::SaveState() and the associated
13566 * task). On success the VM process already changed the state to
13567 * MachineState_Saved, so no need to do anything.
13568 */
13569 if (FAILED(iResult))
13570 setMachineState(mConsoleTaskData.mLastState);
13571
13572 return endSavingState(iResult, aErrMsg);
13573}
13574
13575/**
13576 * @note Locks this object for writing.
13577 */
13578STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13579{
13580 LogFlowThisFunc(("\n"));
13581
13582 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13583
13584 AutoCaller autoCaller(this);
13585 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13586
13587 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13588
13589 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13590 || mData->mMachineState == MachineState_Teleported
13591 || mData->mMachineState == MachineState_Aborted
13592 , E_FAIL); /** @todo setError. */
13593
13594 Utf8Str stateFilePathFull = aSavedStateFile;
13595 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13596 if (RT_FAILURE(vrc))
13597 return setError(VBOX_E_FILE_ERROR,
13598 tr("Invalid saved state file path '%ls' (%Rrc)"),
13599 aSavedStateFile,
13600 vrc);
13601
13602 mSSData->strStateFilePath = stateFilePathFull;
13603
13604 /* The below setMachineState() will detect the state transition and will
13605 * update the settings file */
13606
13607 return setMachineState(MachineState_Saved);
13608}
13609
13610STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13611 ComSafeArrayOut(BSTR, aValues),
13612 ComSafeArrayOut(LONG64, aTimestamps),
13613 ComSafeArrayOut(BSTR, aFlags))
13614{
13615 LogFlowThisFunc(("\n"));
13616
13617#ifdef VBOX_WITH_GUEST_PROPS
13618 using namespace guestProp;
13619
13620 AutoCaller autoCaller(this);
13621 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13622
13623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13624
13625 CheckComArgOutSafeArrayPointerValid(aNames);
13626 CheckComArgOutSafeArrayPointerValid(aValues);
13627 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13628 CheckComArgOutSafeArrayPointerValid(aFlags);
13629
13630 size_t cEntries = mHWData->mGuestProperties.size();
13631 com::SafeArray<BSTR> names(cEntries);
13632 com::SafeArray<BSTR> values(cEntries);
13633 com::SafeArray<LONG64> timestamps(cEntries);
13634 com::SafeArray<BSTR> flags(cEntries);
13635 unsigned i = 0;
13636 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13637 it != mHWData->mGuestProperties.end();
13638 ++it)
13639 {
13640 char szFlags[MAX_FLAGS_LEN + 1];
13641 it->first.cloneTo(&names[i]);
13642 it->second.strValue.cloneTo(&values[i]);
13643 timestamps[i] = it->second.mTimestamp;
13644 /* If it is NULL, keep it NULL. */
13645 if (it->second.mFlags)
13646 {
13647 writeFlags(it->second.mFlags, szFlags);
13648 Bstr(szFlags).cloneTo(&flags[i]);
13649 }
13650 else
13651 flags[i] = NULL;
13652 ++i;
13653 }
13654 names.detachTo(ComSafeArrayOutArg(aNames));
13655 values.detachTo(ComSafeArrayOutArg(aValues));
13656 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13657 flags.detachTo(ComSafeArrayOutArg(aFlags));
13658 return S_OK;
13659#else
13660 ReturnComNotImplemented();
13661#endif
13662}
13663
13664STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13665 IN_BSTR aValue,
13666 LONG64 aTimestamp,
13667 IN_BSTR aFlags)
13668{
13669 LogFlowThisFunc(("\n"));
13670
13671#ifdef VBOX_WITH_GUEST_PROPS
13672 using namespace guestProp;
13673
13674 CheckComArgStrNotEmptyOrNull(aName);
13675 CheckComArgNotNull(aValue);
13676 CheckComArgNotNull(aFlags);
13677
13678 try
13679 {
13680 /*
13681 * Convert input up front.
13682 */
13683 Utf8Str utf8Name(aName);
13684 uint32_t fFlags = NILFLAG;
13685 if (aFlags)
13686 {
13687 Utf8Str utf8Flags(aFlags);
13688 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13689 AssertRCReturn(vrc, E_INVALIDARG);
13690 }
13691
13692 /*
13693 * Now grab the object lock, validate the state and do the update.
13694 */
13695 AutoCaller autoCaller(this);
13696 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13697
13698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13699
13700 switch (mData->mMachineState)
13701 {
13702 case MachineState_Paused:
13703 case MachineState_Running:
13704 case MachineState_Teleporting:
13705 case MachineState_TeleportingPausedVM:
13706 case MachineState_LiveSnapshotting:
13707 case MachineState_DeletingSnapshotOnline:
13708 case MachineState_DeletingSnapshotPaused:
13709 case MachineState_Saving:
13710 case MachineState_Stopping:
13711 break;
13712
13713 default:
13714 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13715 VBOX_E_INVALID_VM_STATE);
13716 }
13717
13718 setModified(IsModified_MachineData);
13719 mHWData.backup();
13720
13721 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13722 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13723 if (it != mHWData->mGuestProperties.end())
13724 {
13725 if (!fDelete)
13726 {
13727 it->second.strValue = aValue;
13728 it->second.mTimestamp = aTimestamp;
13729 it->second.mFlags = fFlags;
13730 }
13731 else
13732 mHWData->mGuestProperties.erase(it);
13733
13734 mData->mGuestPropertiesModified = TRUE;
13735 }
13736 else if (!fDelete)
13737 {
13738 HWData::GuestProperty prop;
13739 prop.strValue = aValue;
13740 prop.mTimestamp = aTimestamp;
13741 prop.mFlags = fFlags;
13742
13743 mHWData->mGuestProperties[utf8Name] = prop;
13744 mData->mGuestPropertiesModified = TRUE;
13745 }
13746
13747 /*
13748 * Send a callback notification if appropriate
13749 */
13750 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13751 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13752 RTSTR_MAX,
13753 utf8Name.c_str(),
13754 RTSTR_MAX, NULL)
13755 )
13756 {
13757 alock.release();
13758
13759 mParent->onGuestPropertyChange(mData->mUuid,
13760 aName,
13761 aValue,
13762 aFlags);
13763 }
13764 }
13765 catch (...)
13766 {
13767 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13768 }
13769 return S_OK;
13770#else
13771 ReturnComNotImplemented();
13772#endif
13773}
13774
13775STDMETHODIMP SessionMachine::LockMedia()
13776{
13777 AutoCaller autoCaller(this);
13778 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13779
13780 AutoMultiWriteLock2 alock(this->lockHandle(),
13781 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13782
13783 AssertReturn( mData->mMachineState == MachineState_Starting
13784 || mData->mMachineState == MachineState_Restoring
13785 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13786
13787 clearError();
13788 alock.release();
13789 return lockMedia();
13790}
13791
13792STDMETHODIMP SessionMachine::UnlockMedia()
13793{
13794 unlockMedia();
13795 return S_OK;
13796}
13797
13798STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13799 IMediumAttachment **aNewAttachment)
13800{
13801 CheckComArgNotNull(aAttachment);
13802 CheckComArgOutPointerValid(aNewAttachment);
13803
13804 AutoCaller autoCaller(this);
13805 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13806
13807 // request the host lock first, since might be calling Host methods for getting host drives;
13808 // next, protect the media tree all the while we're in here, as well as our member variables
13809 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13810 this->lockHandle(),
13811 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13812
13813 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13814
13815 Bstr ctrlName;
13816 LONG lPort;
13817 LONG lDevice;
13818 bool fTempEject;
13819 {
13820 AutoCaller autoAttachCaller(this);
13821 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13822
13823 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13824
13825 /* Need to query the details first, as the IMediumAttachment reference
13826 * might be to the original settings, which we are going to change. */
13827 ctrlName = pAttach->getControllerName();
13828 lPort = pAttach->getPort();
13829 lDevice = pAttach->getDevice();
13830 fTempEject = pAttach->getTempEject();
13831 }
13832
13833 if (!fTempEject)
13834 {
13835 /* Remember previously mounted medium. The medium before taking the
13836 * backup is not necessarily the same thing. */
13837 ComObjPtr<Medium> oldmedium;
13838 oldmedium = pAttach->getMedium();
13839
13840 setModified(IsModified_Storage);
13841 mMediaData.backup();
13842
13843 // The backup operation makes the pAttach reference point to the
13844 // old settings. Re-get the correct reference.
13845 pAttach = findAttachment(mMediaData->mAttachments,
13846 ctrlName.raw(),
13847 lPort,
13848 lDevice);
13849
13850 {
13851 AutoCaller autoAttachCaller(this);
13852 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13853
13854 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13855 if (!oldmedium.isNull())
13856 oldmedium->removeBackReference(mData->mUuid);
13857
13858 pAttach->updateMedium(NULL);
13859 pAttach->updateEjected();
13860 }
13861
13862 setModified(IsModified_Storage);
13863 }
13864 else
13865 {
13866 {
13867 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13868 pAttach->updateEjected();
13869 }
13870 }
13871
13872 pAttach.queryInterfaceTo(aNewAttachment);
13873
13874 return S_OK;
13875}
13876
13877// public methods only for internal purposes
13878/////////////////////////////////////////////////////////////////////////////
13879
13880/**
13881 * Called from the client watcher thread to check for expected or unexpected
13882 * death of the client process that has a direct session to this machine.
13883 *
13884 * On Win32 and on OS/2, this method is called only when we've got the
13885 * mutex (i.e. the client has either died or terminated normally) so it always
13886 * returns @c true (the client is terminated, the session machine is
13887 * uninitialized).
13888 *
13889 * On other platforms, the method returns @c true if the client process has
13890 * terminated normally or abnormally and the session machine was uninitialized,
13891 * and @c false if the client process is still alive.
13892 *
13893 * @note Locks this object for writing.
13894 */
13895bool SessionMachine::checkForDeath()
13896{
13897 Uninit::Reason reason;
13898 bool terminated = false;
13899
13900 /* Enclose autoCaller with a block because calling uninit() from under it
13901 * will deadlock. */
13902 {
13903 AutoCaller autoCaller(this);
13904 if (!autoCaller.isOk())
13905 {
13906 /* return true if not ready, to cause the client watcher to exclude
13907 * the corresponding session from watching */
13908 LogFlowThisFunc(("Already uninitialized!\n"));
13909 return true;
13910 }
13911
13912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13913
13914 /* Determine the reason of death: if the session state is Closing here,
13915 * everything is fine. Otherwise it means that the client did not call
13916 * OnSessionEnd() before it released the IPC semaphore. This may happen
13917 * either because the client process has abnormally terminated, or
13918 * because it simply forgot to call ISession::Close() before exiting. We
13919 * threat the latter also as an abnormal termination (see
13920 * Session::uninit() for details). */
13921 reason = mData->mSession.mState == SessionState_Unlocking ?
13922 Uninit::Normal :
13923 Uninit::Abnormal;
13924
13925 if (mClientToken)
13926 terminated = mClientToken->release();
13927 } /* AutoCaller block */
13928
13929 if (terminated)
13930 uninit(reason);
13931
13932 return terminated;
13933}
13934
13935void SessionMachine::getTokenId(Utf8Str &strTokenId)
13936{
13937 LogFlowThisFunc(("\n"));
13938
13939 strTokenId.setNull();
13940
13941 AutoCaller autoCaller(this);
13942 AssertComRCReturnVoid(autoCaller.rc());
13943
13944 Assert(mClientToken);
13945 if (mClientToken)
13946 mClientToken->getId(strTokenId);
13947}
13948
13949Machine::ClientToken *SessionMachine::getClientToken()
13950{
13951 LogFlowThisFunc(("\n"));
13952
13953 AutoCaller autoCaller(this);
13954 AssertComRCReturn(autoCaller.rc(), NULL);
13955
13956 return mClientToken;
13957}
13958
13959
13960/**
13961 * @note Locks this object for reading.
13962 */
13963HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13964{
13965 LogFlowThisFunc(("\n"));
13966
13967 AutoCaller autoCaller(this);
13968 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13969
13970 ComPtr<IInternalSessionControl> directControl;
13971 {
13972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13973 directControl = mData->mSession.mDirectControl;
13974 }
13975
13976 /* ignore notifications sent after #OnSessionEnd() is called */
13977 if (!directControl)
13978 return S_OK;
13979
13980 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13981}
13982
13983/**
13984 * @note Locks this object for reading.
13985 */
13986HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13987 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13988{
13989 LogFlowThisFunc(("\n"));
13990
13991 AutoCaller autoCaller(this);
13992 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13993
13994 ComPtr<IInternalSessionControl> directControl;
13995 {
13996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13997 directControl = mData->mSession.mDirectControl;
13998 }
13999
14000 /* ignore notifications sent after #OnSessionEnd() is called */
14001 if (!directControl)
14002 return S_OK;
14003 /*
14004 * instead acting like callback we ask IVirtualBox deliver corresponding event
14005 */
14006
14007 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14008 return S_OK;
14009}
14010
14011/**
14012 * @note Locks this object for reading.
14013 */
14014HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
14015{
14016 LogFlowThisFunc(("\n"));
14017
14018 AutoCaller autoCaller(this);
14019 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14020
14021 ComPtr<IInternalSessionControl> directControl;
14022 {
14023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14024 directControl = mData->mSession.mDirectControl;
14025 }
14026
14027 /* ignore notifications sent after #OnSessionEnd() is called */
14028 if (!directControl)
14029 return S_OK;
14030
14031 return directControl->OnSerialPortChange(serialPort);
14032}
14033
14034/**
14035 * @note Locks this object for reading.
14036 */
14037HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
14038{
14039 LogFlowThisFunc(("\n"));
14040
14041 AutoCaller autoCaller(this);
14042 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14043
14044 ComPtr<IInternalSessionControl> directControl;
14045 {
14046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14047 directControl = mData->mSession.mDirectControl;
14048 }
14049
14050 /* ignore notifications sent after #OnSessionEnd() is called */
14051 if (!directControl)
14052 return S_OK;
14053
14054 return directControl->OnParallelPortChange(parallelPort);
14055}
14056
14057/**
14058 * @note Locks this object for reading.
14059 */
14060HRESULT SessionMachine::onStorageControllerChange()
14061{
14062 LogFlowThisFunc(("\n"));
14063
14064 AutoCaller autoCaller(this);
14065 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14066
14067 ComPtr<IInternalSessionControl> directControl;
14068 {
14069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14070 directControl = mData->mSession.mDirectControl;
14071 }
14072
14073 /* ignore notifications sent after #OnSessionEnd() is called */
14074 if (!directControl)
14075 return S_OK;
14076
14077 return directControl->OnStorageControllerChange();
14078}
14079
14080/**
14081 * @note Locks this object for reading.
14082 */
14083HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14084{
14085 LogFlowThisFunc(("\n"));
14086
14087 AutoCaller autoCaller(this);
14088 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14089
14090 ComPtr<IInternalSessionControl> directControl;
14091 {
14092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14093 directControl = mData->mSession.mDirectControl;
14094 }
14095
14096 /* ignore notifications sent after #OnSessionEnd() is called */
14097 if (!directControl)
14098 return S_OK;
14099
14100 return directControl->OnMediumChange(aAttachment, aForce);
14101}
14102
14103/**
14104 * @note Locks this object for reading.
14105 */
14106HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
14107{
14108 LogFlowThisFunc(("\n"));
14109
14110 AutoCaller autoCaller(this);
14111 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14112
14113 ComPtr<IInternalSessionControl> directControl;
14114 {
14115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14116 directControl = mData->mSession.mDirectControl;
14117 }
14118
14119 /* ignore notifications sent after #OnSessionEnd() is called */
14120 if (!directControl)
14121 return S_OK;
14122
14123 return directControl->OnCPUChange(aCPU, aRemove);
14124}
14125
14126HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
14127{
14128 LogFlowThisFunc(("\n"));
14129
14130 AutoCaller autoCaller(this);
14131 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14132
14133 ComPtr<IInternalSessionControl> directControl;
14134 {
14135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14136 directControl = mData->mSession.mDirectControl;
14137 }
14138
14139 /* ignore notifications sent after #OnSessionEnd() is called */
14140 if (!directControl)
14141 return S_OK;
14142
14143 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14144}
14145
14146/**
14147 * @note Locks this object for reading.
14148 */
14149HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
14150{
14151 LogFlowThisFunc(("\n"));
14152
14153 AutoCaller autoCaller(this);
14154 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14155
14156 ComPtr<IInternalSessionControl> directControl;
14157 {
14158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14159 directControl = mData->mSession.mDirectControl;
14160 }
14161
14162 /* ignore notifications sent after #OnSessionEnd() is called */
14163 if (!directControl)
14164 return S_OK;
14165
14166 return directControl->OnVRDEServerChange(aRestart);
14167}
14168
14169/**
14170 * @note Locks this object for reading.
14171 */
14172HRESULT SessionMachine::onVideoCaptureChange()
14173{
14174 LogFlowThisFunc(("\n"));
14175
14176 AutoCaller autoCaller(this);
14177 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14178
14179 ComPtr<IInternalSessionControl> directControl;
14180 {
14181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14182 directControl = mData->mSession.mDirectControl;
14183 }
14184
14185 /* ignore notifications sent after #OnSessionEnd() is called */
14186 if (!directControl)
14187 return S_OK;
14188
14189 return directControl->OnVideoCaptureChange();
14190}
14191
14192/**
14193 * @note Locks this object for reading.
14194 */
14195HRESULT SessionMachine::onUSBControllerChange()
14196{
14197 LogFlowThisFunc(("\n"));
14198
14199 AutoCaller autoCaller(this);
14200 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14201
14202 ComPtr<IInternalSessionControl> directControl;
14203 {
14204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14205 directControl = mData->mSession.mDirectControl;
14206 }
14207
14208 /* ignore notifications sent after #OnSessionEnd() is called */
14209 if (!directControl)
14210 return S_OK;
14211
14212 return directControl->OnUSBControllerChange();
14213}
14214
14215/**
14216 * @note Locks this object for reading.
14217 */
14218HRESULT SessionMachine::onSharedFolderChange()
14219{
14220 LogFlowThisFunc(("\n"));
14221
14222 AutoCaller autoCaller(this);
14223 AssertComRCReturnRC(autoCaller.rc());
14224
14225 ComPtr<IInternalSessionControl> directControl;
14226 {
14227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14228 directControl = mData->mSession.mDirectControl;
14229 }
14230
14231 /* ignore notifications sent after #OnSessionEnd() is called */
14232 if (!directControl)
14233 return S_OK;
14234
14235 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14236}
14237
14238/**
14239 * @note Locks this object for reading.
14240 */
14241HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14242{
14243 LogFlowThisFunc(("\n"));
14244
14245 AutoCaller autoCaller(this);
14246 AssertComRCReturnRC(autoCaller.rc());
14247
14248 ComPtr<IInternalSessionControl> directControl;
14249 {
14250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14251 directControl = mData->mSession.mDirectControl;
14252 }
14253
14254 /* ignore notifications sent after #OnSessionEnd() is called */
14255 if (!directControl)
14256 return S_OK;
14257
14258 return directControl->OnClipboardModeChange(aClipboardMode);
14259}
14260
14261/**
14262 * @note Locks this object for reading.
14263 */
14264HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14265{
14266 LogFlowThisFunc(("\n"));
14267
14268 AutoCaller autoCaller(this);
14269 AssertComRCReturnRC(autoCaller.rc());
14270
14271 ComPtr<IInternalSessionControl> directControl;
14272 {
14273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14274 directControl = mData->mSession.mDirectControl;
14275 }
14276
14277 /* ignore notifications sent after #OnSessionEnd() is called */
14278 if (!directControl)
14279 return S_OK;
14280
14281 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14282}
14283
14284/**
14285 * @note Locks this object for reading.
14286 */
14287HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14288{
14289 LogFlowThisFunc(("\n"));
14290
14291 AutoCaller autoCaller(this);
14292 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14293
14294 ComPtr<IInternalSessionControl> directControl;
14295 {
14296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14297 directControl = mData->mSession.mDirectControl;
14298 }
14299
14300 /* ignore notifications sent after #OnSessionEnd() is called */
14301 if (!directControl)
14302 return S_OK;
14303
14304 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14305}
14306
14307/**
14308 * @note Locks this object for reading.
14309 */
14310HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14311{
14312 LogFlowThisFunc(("\n"));
14313
14314 AutoCaller autoCaller(this);
14315 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14316
14317 ComPtr<IInternalSessionControl> directControl;
14318 {
14319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14320 directControl = mData->mSession.mDirectControl;
14321 }
14322
14323 /* ignore notifications sent after #OnSessionEnd() is called */
14324 if (!directControl)
14325 return S_OK;
14326
14327 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14328}
14329
14330/**
14331 * Returns @c true if this machine's USB controller reports it has a matching
14332 * filter for the given USB device and @c false otherwise.
14333 *
14334 * @note locks this object for reading.
14335 */
14336bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14337{
14338 AutoCaller autoCaller(this);
14339 /* silently return if not ready -- this method may be called after the
14340 * direct machine session has been called */
14341 if (!autoCaller.isOk())
14342 return false;
14343
14344#ifdef VBOX_WITH_USB
14345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14346
14347 switch (mData->mMachineState)
14348 {
14349 case MachineState_Starting:
14350 case MachineState_Restoring:
14351 case MachineState_TeleportingIn:
14352 case MachineState_Paused:
14353 case MachineState_Running:
14354 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14355 * elsewhere... */
14356 alock.release();
14357 return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs);
14358 default: break;
14359 }
14360#else
14361 NOREF(aDevice);
14362 NOREF(aMaskedIfs);
14363#endif
14364 return false;
14365}
14366
14367/**
14368 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14369 */
14370HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14371 IVirtualBoxErrorInfo *aError,
14372 ULONG aMaskedIfs)
14373{
14374 LogFlowThisFunc(("\n"));
14375
14376 AutoCaller autoCaller(this);
14377
14378 /* This notification may happen after the machine object has been
14379 * uninitialized (the session was closed), so don't assert. */
14380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14381
14382 ComPtr<IInternalSessionControl> directControl;
14383 {
14384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14385 directControl = mData->mSession.mDirectControl;
14386 }
14387
14388 /* fail on notifications sent after #OnSessionEnd() is called, it is
14389 * expected by the caller */
14390 if (!directControl)
14391 return E_FAIL;
14392
14393 /* No locks should be held at this point. */
14394 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14395 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14396
14397 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14398}
14399
14400/**
14401 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14402 */
14403HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14404 IVirtualBoxErrorInfo *aError)
14405{
14406 LogFlowThisFunc(("\n"));
14407
14408 AutoCaller autoCaller(this);
14409
14410 /* This notification may happen after the machine object has been
14411 * uninitialized (the session was closed), so don't assert. */
14412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14413
14414 ComPtr<IInternalSessionControl> directControl;
14415 {
14416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14417 directControl = mData->mSession.mDirectControl;
14418 }
14419
14420 /* fail on notifications sent after #OnSessionEnd() is called, it is
14421 * expected by the caller */
14422 if (!directControl)
14423 return E_FAIL;
14424
14425 /* No locks should be held at this point. */
14426 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14427 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14428
14429 return directControl->OnUSBDeviceDetach(aId, aError);
14430}
14431
14432// protected methods
14433/////////////////////////////////////////////////////////////////////////////
14434
14435/**
14436 * Helper method to finalize saving the state.
14437 *
14438 * @note Must be called from under this object's lock.
14439 *
14440 * @param aRc S_OK if the snapshot has been taken successfully
14441 * @param aErrMsg human readable error message for failure
14442 *
14443 * @note Locks mParent + this objects for writing.
14444 */
14445HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14446{
14447 LogFlowThisFuncEnter();
14448
14449 AutoCaller autoCaller(this);
14450 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14451
14452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14453
14454 HRESULT rc = S_OK;
14455
14456 if (SUCCEEDED(aRc))
14457 {
14458 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14459
14460 /* save all VM settings */
14461 rc = saveSettings(NULL);
14462 // no need to check whether VirtualBox.xml needs saving also since
14463 // we can't have a name change pending at this point
14464 }
14465 else
14466 {
14467 // delete the saved state file (it might have been already created);
14468 // we need not check whether this is shared with a snapshot here because
14469 // we certainly created this saved state file here anew
14470 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14471 }
14472
14473 /* notify the progress object about operation completion */
14474 Assert(mConsoleTaskData.mProgress);
14475 if (SUCCEEDED(aRc))
14476 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14477 else
14478 {
14479 if (aErrMsg.length())
14480 mConsoleTaskData.mProgress->notifyComplete(aRc,
14481 COM_IIDOF(ISession),
14482 getComponentName(),
14483 aErrMsg.c_str());
14484 else
14485 mConsoleTaskData.mProgress->notifyComplete(aRc);
14486 }
14487
14488 /* clear out the temporary saved state data */
14489 mConsoleTaskData.mLastState = MachineState_Null;
14490 mConsoleTaskData.strStateFilePath.setNull();
14491 mConsoleTaskData.mProgress.setNull();
14492
14493 LogFlowThisFuncLeave();
14494 return rc;
14495}
14496
14497/**
14498 * Deletes the given file if it is no longer in use by either the current machine state
14499 * (if the machine is "saved") or any of the machine's snapshots.
14500 *
14501 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14502 * but is different for each SnapshotMachine. When calling this, the order of calling this
14503 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14504 * is therefore critical. I know, it's all rather messy.
14505 *
14506 * @param strStateFile
14507 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14508 */
14509void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14510 Snapshot *pSnapshotToIgnore)
14511{
14512 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14513 if ( (strStateFile.isNotEmpty())
14514 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14515 )
14516 // ... and it must also not be shared with other snapshots
14517 if ( !mData->mFirstSnapshot
14518 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14519 // this checks the SnapshotMachine's state file paths
14520 )
14521 RTFileDelete(strStateFile.c_str());
14522}
14523
14524/**
14525 * Locks the attached media.
14526 *
14527 * All attached hard disks are locked for writing and DVD/floppy are locked for
14528 * reading. Parents of attached hard disks (if any) are locked for reading.
14529 *
14530 * This method also performs accessibility check of all media it locks: if some
14531 * media is inaccessible, the method will return a failure and a bunch of
14532 * extended error info objects per each inaccessible medium.
14533 *
14534 * Note that this method is atomic: if it returns a success, all media are
14535 * locked as described above; on failure no media is locked at all (all
14536 * succeeded individual locks will be undone).
14537 *
14538 * The caller is responsible for doing the necessary state sanity checks.
14539 *
14540 * The locks made by this method must be undone by calling #unlockMedia() when
14541 * no more needed.
14542 */
14543HRESULT SessionMachine::lockMedia()
14544{
14545 AutoCaller autoCaller(this);
14546 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14547
14548 AutoMultiWriteLock2 alock(this->lockHandle(),
14549 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14550
14551 /* bail out if trying to lock things with already set up locking */
14552 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14553
14554 MultiResult mrc(S_OK);
14555
14556 /* Collect locking information for all medium objects attached to the VM. */
14557 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14558 it != mMediaData->mAttachments.end();
14559 ++it)
14560 {
14561 MediumAttachment* pAtt = *it;
14562 DeviceType_T devType = pAtt->getType();
14563 Medium *pMedium = pAtt->getMedium();
14564
14565 MediumLockList *pMediumLockList(new MediumLockList());
14566 // There can be attachments without a medium (floppy/dvd), and thus
14567 // it's impossible to create a medium lock list. It still makes sense
14568 // to have the empty medium lock list in the map in case a medium is
14569 // attached later.
14570 if (pMedium != NULL)
14571 {
14572 MediumType_T mediumType = pMedium->getType();
14573 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14574 || mediumType == MediumType_Shareable;
14575 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14576
14577 alock.release();
14578 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14579 !fIsReadOnlyLock /* fMediumLockWrite */,
14580 NULL,
14581 *pMediumLockList);
14582 alock.acquire();
14583 if (FAILED(mrc))
14584 {
14585 delete pMediumLockList;
14586 mData->mSession.mLockedMedia.Clear();
14587 break;
14588 }
14589 }
14590
14591 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14592 if (FAILED(rc))
14593 {
14594 mData->mSession.mLockedMedia.Clear();
14595 mrc = setError(rc,
14596 tr("Collecting locking information for all attached media failed"));
14597 break;
14598 }
14599 }
14600
14601 if (SUCCEEDED(mrc))
14602 {
14603 /* Now lock all media. If this fails, nothing is locked. */
14604 alock.release();
14605 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14606 alock.acquire();
14607 if (FAILED(rc))
14608 {
14609 mrc = setError(rc,
14610 tr("Locking of attached media failed"));
14611 }
14612 }
14613
14614 return mrc;
14615}
14616
14617/**
14618 * Undoes the locks made by by #lockMedia().
14619 */
14620void SessionMachine::unlockMedia()
14621{
14622 AutoCaller autoCaller(this);
14623 AssertComRCReturnVoid(autoCaller.rc());
14624
14625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14626
14627 /* we may be holding important error info on the current thread;
14628 * preserve it */
14629 ErrorInfoKeeper eik;
14630
14631 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14632 AssertComRC(rc);
14633}
14634
14635/**
14636 * Helper to change the machine state (reimplementation).
14637 *
14638 * @note Locks this object for writing.
14639 * @note This method must not call saveSettings or SaveSettings, otherwise
14640 * it can cause crashes in random places due to unexpectedly committing
14641 * the current settings. The caller is responsible for that. The call
14642 * to saveStateSettings is fine, because this method does not commit.
14643 */
14644HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14645{
14646 LogFlowThisFuncEnter();
14647 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14648
14649 AutoCaller autoCaller(this);
14650 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14651
14652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14653
14654 MachineState_T oldMachineState = mData->mMachineState;
14655
14656 AssertMsgReturn(oldMachineState != aMachineState,
14657 ("oldMachineState=%s, aMachineState=%s\n",
14658 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14659 E_FAIL);
14660
14661 HRESULT rc = S_OK;
14662
14663 int stsFlags = 0;
14664 bool deleteSavedState = false;
14665
14666 /* detect some state transitions */
14667
14668 if ( ( oldMachineState == MachineState_Saved
14669 && aMachineState == MachineState_Restoring)
14670 || ( ( oldMachineState == MachineState_PoweredOff
14671 || oldMachineState == MachineState_Teleported
14672 || oldMachineState == MachineState_Aborted
14673 )
14674 && ( aMachineState == MachineState_TeleportingIn
14675 || aMachineState == MachineState_Starting
14676 )
14677 )
14678 )
14679 {
14680 /* The EMT thread is about to start */
14681
14682 /* Nothing to do here for now... */
14683
14684 /// @todo NEWMEDIA don't let mDVDDrive and other children
14685 /// change anything when in the Starting/Restoring state
14686 }
14687 else if ( ( oldMachineState == MachineState_Running
14688 || oldMachineState == MachineState_Paused
14689 || oldMachineState == MachineState_Teleporting
14690 || oldMachineState == MachineState_LiveSnapshotting
14691 || oldMachineState == MachineState_Stuck
14692 || oldMachineState == MachineState_Starting
14693 || oldMachineState == MachineState_Stopping
14694 || oldMachineState == MachineState_Saving
14695 || oldMachineState == MachineState_Restoring
14696 || oldMachineState == MachineState_TeleportingPausedVM
14697 || oldMachineState == MachineState_TeleportingIn
14698 )
14699 && ( aMachineState == MachineState_PoweredOff
14700 || aMachineState == MachineState_Saved
14701 || aMachineState == MachineState_Teleported
14702 || aMachineState == MachineState_Aborted
14703 )
14704 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14705 * snapshot */
14706 && ( mConsoleTaskData.mSnapshot.isNull()
14707 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14708 )
14709 )
14710 {
14711 /* The EMT thread has just stopped, unlock attached media. Note that as
14712 * opposed to locking that is done from Console, we do unlocking here
14713 * because the VM process may have aborted before having a chance to
14714 * properly unlock all media it locked. */
14715
14716 unlockMedia();
14717 }
14718
14719 if (oldMachineState == MachineState_Restoring)
14720 {
14721 if (aMachineState != MachineState_Saved)
14722 {
14723 /*
14724 * delete the saved state file once the machine has finished
14725 * restoring from it (note that Console sets the state from
14726 * Restoring to Saved if the VM couldn't restore successfully,
14727 * to give the user an ability to fix an error and retry --
14728 * we keep the saved state file in this case)
14729 */
14730 deleteSavedState = true;
14731 }
14732 }
14733 else if ( oldMachineState == MachineState_Saved
14734 && ( aMachineState == MachineState_PoweredOff
14735 || aMachineState == MachineState_Aborted
14736 || aMachineState == MachineState_Teleported
14737 )
14738 )
14739 {
14740 /*
14741 * delete the saved state after Console::ForgetSavedState() is called
14742 * or if the VM process (owning a direct VM session) crashed while the
14743 * VM was Saved
14744 */
14745
14746 /// @todo (dmik)
14747 // Not sure that deleting the saved state file just because of the
14748 // client death before it attempted to restore the VM is a good
14749 // thing. But when it crashes we need to go to the Aborted state
14750 // which cannot have the saved state file associated... The only
14751 // way to fix this is to make the Aborted condition not a VM state
14752 // but a bool flag: i.e., when a crash occurs, set it to true and
14753 // change the state to PoweredOff or Saved depending on the
14754 // saved state presence.
14755
14756 deleteSavedState = true;
14757 mData->mCurrentStateModified = TRUE;
14758 stsFlags |= SaveSTS_CurStateModified;
14759 }
14760
14761 if ( aMachineState == MachineState_Starting
14762 || aMachineState == MachineState_Restoring
14763 || aMachineState == MachineState_TeleportingIn
14764 )
14765 {
14766 /* set the current state modified flag to indicate that the current
14767 * state is no more identical to the state in the
14768 * current snapshot */
14769 if (!mData->mCurrentSnapshot.isNull())
14770 {
14771 mData->mCurrentStateModified = TRUE;
14772 stsFlags |= SaveSTS_CurStateModified;
14773 }
14774 }
14775
14776 if (deleteSavedState)
14777 {
14778 if (mRemoveSavedState)
14779 {
14780 Assert(!mSSData->strStateFilePath.isEmpty());
14781
14782 // it is safe to delete the saved state file if ...
14783 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14784 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14785 // ... none of the snapshots share the saved state file
14786 )
14787 RTFileDelete(mSSData->strStateFilePath.c_str());
14788 }
14789
14790 mSSData->strStateFilePath.setNull();
14791 stsFlags |= SaveSTS_StateFilePath;
14792 }
14793
14794 /* redirect to the underlying peer machine */
14795 mPeer->setMachineState(aMachineState);
14796
14797 if ( aMachineState == MachineState_PoweredOff
14798 || aMachineState == MachineState_Teleported
14799 || aMachineState == MachineState_Aborted
14800 || aMachineState == MachineState_Saved)
14801 {
14802 /* the machine has stopped execution
14803 * (or the saved state file was adopted) */
14804 stsFlags |= SaveSTS_StateTimeStamp;
14805 }
14806
14807 if ( ( oldMachineState == MachineState_PoweredOff
14808 || oldMachineState == MachineState_Aborted
14809 || oldMachineState == MachineState_Teleported
14810 )
14811 && aMachineState == MachineState_Saved)
14812 {
14813 /* the saved state file was adopted */
14814 Assert(!mSSData->strStateFilePath.isEmpty());
14815 stsFlags |= SaveSTS_StateFilePath;
14816 }
14817
14818#ifdef VBOX_WITH_GUEST_PROPS
14819 if ( aMachineState == MachineState_PoweredOff
14820 || aMachineState == MachineState_Aborted
14821 || aMachineState == MachineState_Teleported)
14822 {
14823 /* Make sure any transient guest properties get removed from the
14824 * property store on shutdown. */
14825
14826 HWData::GuestPropertyMap::const_iterator it;
14827 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14828 if (!fNeedsSaving)
14829 for (it = mHWData->mGuestProperties.begin();
14830 it != mHWData->mGuestProperties.end(); ++it)
14831 if ( (it->second.mFlags & guestProp::TRANSIENT)
14832 || (it->second.mFlags & guestProp::TRANSRESET))
14833 {
14834 fNeedsSaving = true;
14835 break;
14836 }
14837 if (fNeedsSaving)
14838 {
14839 mData->mCurrentStateModified = TRUE;
14840 stsFlags |= SaveSTS_CurStateModified;
14841 }
14842 }
14843#endif
14844
14845 rc = saveStateSettings(stsFlags);
14846
14847 if ( ( oldMachineState != MachineState_PoweredOff
14848 && oldMachineState != MachineState_Aborted
14849 && oldMachineState != MachineState_Teleported
14850 )
14851 && ( aMachineState == MachineState_PoweredOff
14852 || aMachineState == MachineState_Aborted
14853 || aMachineState == MachineState_Teleported
14854 )
14855 )
14856 {
14857 /* we've been shut down for any reason */
14858 /* no special action so far */
14859 }
14860
14861 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14862 LogFlowThisFuncLeave();
14863 return rc;
14864}
14865
14866/**
14867 * Sends the current machine state value to the VM process.
14868 *
14869 * @note Locks this object for reading, then calls a client process.
14870 */
14871HRESULT SessionMachine::updateMachineStateOnClient()
14872{
14873 AutoCaller autoCaller(this);
14874 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14875
14876 ComPtr<IInternalSessionControl> directControl;
14877 {
14878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14879 AssertReturn(!!mData, E_FAIL);
14880 directControl = mData->mSession.mDirectControl;
14881
14882 /* directControl may be already set to NULL here in #OnSessionEnd()
14883 * called too early by the direct session process while there is still
14884 * some operation (like deleting the snapshot) in progress. The client
14885 * process in this case is waiting inside Session::close() for the
14886 * "end session" process object to complete, while #uninit() called by
14887 * #checkForDeath() on the Watcher thread is waiting for the pending
14888 * operation to complete. For now, we accept this inconsistent behavior
14889 * and simply do nothing here. */
14890
14891 if (mData->mSession.mState == SessionState_Unlocking)
14892 return S_OK;
14893
14894 AssertReturn(!directControl.isNull(), E_FAIL);
14895 }
14896
14897 return directControl->UpdateMachineState(mData->mMachineState);
14898}
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