VirtualBox

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

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

VPX: plugged memory leak; introduced rate parameter; clear artefacts from previous frames after resize; cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 478.8 KB
Line 
1/* $Id: MachineImpl.cpp 45838 2013-04-30 13:54:20Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
165 mVRAMSize = 8;
166 mAccelerate3DEnabled = false;
167 mAccelerate2DVideoEnabled = false;
168 mMonitorCount = 1;
169 mVideoCaptureFile = "Test.webm";
170 mVideoCaptureWidth = 1024;
171 mVideoCaptureHeight = 768;
172 mVideoCaptureRate = 512;
173 mVideoCaptureEnabled = false;
174
175 mHWVirtExEnabled = true;
176 mHWVirtExNestedPagingEnabled = true;
177#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
178 mHWVirtExLargePagesEnabled = true;
179#else
180 /* Not supported on 32 bits hosts. */
181 mHWVirtExLargePagesEnabled = false;
182#endif
183 mHWVirtExVPIDEnabled = true;
184 mHWVirtExForceEnabled = false;
185#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
186 mHWVirtExExclusive = false;
187#else
188 mHWVirtExExclusive = true;
189#endif
190#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
191 mPAEEnabled = true;
192#else
193 mPAEEnabled = false;
194#endif
195 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
196 mSyntheticCpu = false;
197 mHPETEnabled = false;
198
199 /* default boot order: floppy - DVD - HDD */
200 mBootOrder[0] = DeviceType_Floppy;
201 mBootOrder[1] = DeviceType_DVD;
202 mBootOrder[2] = DeviceType_HardDisk;
203 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
204 mBootOrder[i] = DeviceType_Null;
205
206 mClipboardMode = ClipboardMode_Disabled;
207 mDragAndDropMode = DragAndDropMode_Disabled;
208 mGuestPropertyNotificationPatterns = "";
209
210 mFirmwareType = FirmwareType_BIOS;
211 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
212 mPointingHIDType = PointingHIDType_PS2Mouse;
213 mChipsetType = ChipsetType_PIIX3;
214 mEmulatedUSBWebcamEnabled = FALSE;
215 mEmulatedUSBCardReaderEnabled = FALSE;
216
217 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
218 mCPUAttached[i] = false;
219
220 mIOCacheEnabled = true;
221 mIOCacheSize = 5; /* 5MB */
222
223 /* Maximum CPU execution cap by default. */
224 mCpuExecutionCap = 100;
225}
226
227Machine::HWData::~HWData()
228{
229}
230
231/////////////////////////////////////////////////////////////////////////////
232// Machine::HDData structure
233/////////////////////////////////////////////////////////////////////////////
234
235Machine::MediaData::MediaData()
236{
237}
238
239Machine::MediaData::~MediaData()
240{
241}
242
243/////////////////////////////////////////////////////////////////////////////
244// Machine class
245/////////////////////////////////////////////////////////////////////////////
246
247// constructor / destructor
248/////////////////////////////////////////////////////////////////////////////
249
250Machine::Machine()
251 : mCollectorGuest(NULL),
252 mPeer(NULL),
253 mParent(NULL),
254 mSerialPorts(),
255 mParallelPorts(),
256 uRegistryNeedsSaving(0)
257{}
258
259Machine::~Machine()
260{}
261
262HRESULT Machine::FinalConstruct()
263{
264 LogFlowThisFunc(("\n"));
265 return BaseFinalConstruct();
266}
267
268void Machine::FinalRelease()
269{
270 LogFlowThisFunc(("\n"));
271 uninit();
272 BaseFinalRelease();
273}
274
275/**
276 * Initializes a new machine instance; this init() variant creates a new, empty machine.
277 * This gets called from VirtualBox::CreateMachine().
278 *
279 * @param aParent Associated parent object
280 * @param strConfigFile Local file system path to the VM settings file (can
281 * be relative to the VirtualBox config directory).
282 * @param strName name for the machine
283 * @param llGroups list of groups for the machine
284 * @param aOsType OS Type of this machine or NULL.
285 * @param aId UUID for the new machine.
286 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
287 *
288 * @return Success indicator. if not S_OK, the machine object is invalid
289 */
290HRESULT Machine::init(VirtualBox *aParent,
291 const Utf8Str &strConfigFile,
292 const Utf8Str &strName,
293 const StringsList &llGroups,
294 GuestOSType *aOsType,
295 const Guid &aId,
296 bool fForceOverwrite,
297 bool fDirectoryIncludesUUID)
298{
299 LogFlowThisFuncEnter();
300 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
301
302 /* Enclose the state transition NotReady->InInit->Ready */
303 AutoInitSpan autoInitSpan(this);
304 AssertReturn(autoInitSpan.isOk(), E_FAIL);
305
306 HRESULT rc = initImpl(aParent, strConfigFile);
307 if (FAILED(rc)) return rc;
308
309 rc = tryCreateMachineConfigFile(fForceOverwrite);
310 if (FAILED(rc)) return rc;
311
312 if (SUCCEEDED(rc))
313 {
314 // create an empty machine config
315 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
316
317 rc = initDataAndChildObjects();
318 }
319
320 if (SUCCEEDED(rc))
321 {
322 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
323 mData->mAccessible = TRUE;
324
325 unconst(mData->mUuid) = aId;
326
327 mUserData->s.strName = strName;
328
329 mUserData->s.llGroups = llGroups;
330
331 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
332 // the "name sync" flag determines whether the machine directory gets renamed along
333 // with the machine file; say so if the settings file name is the same as the
334 // settings file parent directory (machine directory)
335 mUserData->s.fNameSync = isInOwnDir();
336
337 // initialize the default snapshots folder
338 rc = COMSETTER(SnapshotFolder)(NULL);
339 AssertComRC(rc);
340
341 if (aOsType)
342 {
343 /* Store OS type */
344 mUserData->s.strOsType = aOsType->id();
345
346 /* Apply BIOS defaults */
347 mBIOSSettings->applyDefaults(aOsType);
348
349 /* Apply network adapters defaults */
350 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
351 mNetworkAdapters[slot]->applyDefaults(aOsType);
352
353 /* Apply serial port defaults */
354 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
355 mSerialPorts[slot]->applyDefaults(aOsType);
356
357 /* Let the OS type select 64-bit ness. */
358 mHWData->mLongMode = aOsType->is64Bit()
359 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
360 }
361
362 /* At this point the changing of the current state modification
363 * flag is allowed. */
364 allowStateModification();
365
366 /* commit all changes made during the initialization */
367 commit();
368 }
369
370 /* Confirm a successful initialization when it's the case */
371 if (SUCCEEDED(rc))
372 {
373 if (mData->mAccessible)
374 autoInitSpan.setSucceeded();
375 else
376 autoInitSpan.setLimited();
377 }
378
379 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
380 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
381 mData->mRegistered,
382 mData->mAccessible,
383 rc));
384
385 LogFlowThisFuncLeave();
386
387 return rc;
388}
389
390/**
391 * Initializes a new instance with data from machine XML (formerly Init_Registered).
392 * Gets called in two modes:
393 *
394 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
395 * UUID is specified and we mark the machine as "registered";
396 *
397 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
398 * and the machine remains unregistered until RegisterMachine() is called.
399 *
400 * @param aParent Associated parent object
401 * @param aConfigFile Local file system path to the VM settings file (can
402 * be relative to the VirtualBox config directory).
403 * @param aId UUID of the machine or NULL (see above).
404 *
405 * @return Success indicator. if not S_OK, the machine object is invalid
406 */
407HRESULT Machine::initFromSettings(VirtualBox *aParent,
408 const Utf8Str &strConfigFile,
409 const Guid *aId)
410{
411 LogFlowThisFuncEnter();
412 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
413
414 /* Enclose the state transition NotReady->InInit->Ready */
415 AutoInitSpan autoInitSpan(this);
416 AssertReturn(autoInitSpan.isOk(), E_FAIL);
417
418 HRESULT rc = initImpl(aParent, strConfigFile);
419 if (FAILED(rc)) return rc;
420
421 if (aId)
422 {
423 // loading a registered VM:
424 unconst(mData->mUuid) = *aId;
425 mData->mRegistered = TRUE;
426 // now load the settings from XML:
427 rc = registeredInit();
428 // this calls initDataAndChildObjects() and loadSettings()
429 }
430 else
431 {
432 // opening an unregistered VM (VirtualBox::OpenMachine()):
433 rc = initDataAndChildObjects();
434
435 if (SUCCEEDED(rc))
436 {
437 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
438 mData->mAccessible = TRUE;
439
440 try
441 {
442 // load and parse machine XML; this will throw on XML or logic errors
443 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
444
445 // reject VM UUID duplicates, they can happen if someone
446 // tries to register an already known VM config again
447 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
448 true /* fPermitInaccessible */,
449 false /* aDoSetError */,
450 NULL) != VBOX_E_OBJECT_NOT_FOUND)
451 {
452 throw setError(E_FAIL,
453 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
454 mData->m_strConfigFile.c_str());
455 }
456
457 // use UUID from machine config
458 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
459
460 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
461 NULL /* puuidRegistry */);
462 if (FAILED(rc)) throw rc;
463
464 /* At this point the changing of the current state modification
465 * flag is allowed. */
466 allowStateModification();
467
468 commit();
469 }
470 catch (HRESULT err)
471 {
472 /* we assume that error info is set by the thrower */
473 rc = err;
474 }
475 catch (...)
476 {
477 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
478 }
479 }
480 }
481
482 /* Confirm a successful initialization when it's the case */
483 if (SUCCEEDED(rc))
484 {
485 if (mData->mAccessible)
486 autoInitSpan.setSucceeded();
487 else
488 {
489 autoInitSpan.setLimited();
490
491 // uninit media from this machine's media registry, or else
492 // reloading the settings will fail
493 mParent->unregisterMachineMedia(getId());
494 }
495 }
496
497 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
498 "rc=%08X\n",
499 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
500 mData->mRegistered, mData->mAccessible, rc));
501
502 LogFlowThisFuncLeave();
503
504 return rc;
505}
506
507/**
508 * Initializes a new instance from a machine config that is already in memory
509 * (import OVF case). Since we are importing, the UUID in the machine
510 * config is ignored and we always generate a fresh one.
511 *
512 * @param strName Name for the new machine; this overrides what is specified in config and is used
513 * for the settings file as well.
514 * @param config Machine configuration loaded and parsed from XML.
515 *
516 * @return Success indicator. if not S_OK, the machine object is invalid
517 */
518HRESULT Machine::init(VirtualBox *aParent,
519 const Utf8Str &strName,
520 const settings::MachineConfigFile &config)
521{
522 LogFlowThisFuncEnter();
523
524 /* Enclose the state transition NotReady->InInit->Ready */
525 AutoInitSpan autoInitSpan(this);
526 AssertReturn(autoInitSpan.isOk(), E_FAIL);
527
528 Utf8Str strConfigFile;
529 aParent->getDefaultMachineFolder(strConfigFile);
530 strConfigFile.append(RTPATH_DELIMITER);
531 strConfigFile.append(strName);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(".vbox");
535
536 HRESULT rc = initImpl(aParent, strConfigFile);
537 if (FAILED(rc)) return rc;
538
539 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
540 if (FAILED(rc)) return rc;
541
542 rc = initDataAndChildObjects();
543
544 if (SUCCEEDED(rc))
545 {
546 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
547 mData->mAccessible = TRUE;
548
549 // create empty machine config for instance data
550 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
551
552 // generate fresh UUID, ignore machine config
553 unconst(mData->mUuid).create();
554
555 rc = loadMachineDataFromSettings(config,
556 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
557
558 // override VM name as well, it may be different
559 mUserData->s.strName = strName;
560
561 if (SUCCEEDED(rc))
562 {
563 /* At this point the changing of the current state modification
564 * flag is allowed. */
565 allowStateModification();
566
567 /* commit all changes made during the initialization */
568 commit();
569 }
570 }
571
572 /* Confirm a successful initialization when it's the case */
573 if (SUCCEEDED(rc))
574 {
575 if (mData->mAccessible)
576 autoInitSpan.setSucceeded();
577 else
578 {
579 autoInitSpan.setLimited();
580
581 // uninit media from this machine's media registry, or else
582 // reloading the settings will fail
583 mParent->unregisterMachineMedia(getId());
584 }
585 }
586
587 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
588 "rc=%08X\n",
589 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
590 mData->mRegistered, mData->mAccessible, rc));
591
592 LogFlowThisFuncLeave();
593
594 return rc;
595}
596
597/**
598 * Shared code between the various init() implementations.
599 * @param aParent
600 * @return
601 */
602HRESULT Machine::initImpl(VirtualBox *aParent,
603 const Utf8Str &strConfigFile)
604{
605 LogFlowThisFuncEnter();
606
607 AssertReturn(aParent, E_INVALIDARG);
608 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
609
610 HRESULT rc = S_OK;
611
612 /* share the parent weakly */
613 unconst(mParent) = aParent;
614
615 /* allocate the essential machine data structure (the rest will be
616 * allocated later by initDataAndChildObjects() */
617 mData.allocate();
618
619 /* memorize the config file name (as provided) */
620 mData->m_strConfigFile = strConfigFile;
621
622 /* get the full file name */
623 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
624 if (RT_FAILURE(vrc1))
625 return setError(VBOX_E_FILE_ERROR,
626 tr("Invalid machine settings file name '%s' (%Rrc)"),
627 strConfigFile.c_str(),
628 vrc1);
629
630 LogFlowThisFuncLeave();
631
632 return rc;
633}
634
635/**
636 * Tries to create a machine settings file in the path stored in the machine
637 * instance data. Used when a new machine is created to fail gracefully if
638 * the settings file could not be written (e.g. because machine dir is read-only).
639 * @return
640 */
641HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
642{
643 HRESULT rc = S_OK;
644
645 // when we create a new machine, we must be able to create the settings file
646 RTFILE f = NIL_RTFILE;
647 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
648 if ( RT_SUCCESS(vrc)
649 || vrc == VERR_SHARING_VIOLATION
650 )
651 {
652 if (RT_SUCCESS(vrc))
653 RTFileClose(f);
654 if (!fForceOverwrite)
655 rc = setError(VBOX_E_FILE_ERROR,
656 tr("Machine settings file '%s' already exists"),
657 mData->m_strConfigFileFull.c_str());
658 else
659 {
660 /* try to delete the config file, as otherwise the creation
661 * of a new settings file will fail. */
662 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
663 if (RT_FAILURE(vrc2))
664 rc = setError(VBOX_E_FILE_ERROR,
665 tr("Could not delete the existing settings file '%s' (%Rrc)"),
666 mData->m_strConfigFileFull.c_str(), vrc2);
667 }
668 }
669 else if ( vrc != VERR_FILE_NOT_FOUND
670 && vrc != VERR_PATH_NOT_FOUND
671 )
672 rc = setError(VBOX_E_FILE_ERROR,
673 tr("Invalid machine settings file name '%s' (%Rrc)"),
674 mData->m_strConfigFileFull.c_str(),
675 vrc);
676 return rc;
677}
678
679/**
680 * Initializes the registered machine by loading the settings file.
681 * This method is separated from #init() in order to make it possible to
682 * retry the operation after VirtualBox startup instead of refusing to
683 * startup the whole VirtualBox server in case if the settings file of some
684 * registered VM is invalid or inaccessible.
685 *
686 * @note Must be always called from this object's write lock
687 * (unless called from #init() that doesn't need any locking).
688 * @note Locks the mUSBController method for writing.
689 * @note Subclasses must not call this method.
690 */
691HRESULT Machine::registeredInit()
692{
693 AssertReturn(!isSessionMachine(), E_FAIL);
694 AssertReturn(!isSnapshotMachine(), E_FAIL);
695 AssertReturn(mData->mUuid.isValid(), E_FAIL);
696 AssertReturn(!mData->mAccessible, E_FAIL);
697
698 HRESULT rc = initDataAndChildObjects();
699
700 if (SUCCEEDED(rc))
701 {
702 /* Temporarily reset the registered flag in order to let setters
703 * potentially called from loadSettings() succeed (isMutable() used in
704 * all setters will return FALSE for a Machine instance if mRegistered
705 * is TRUE). */
706 mData->mRegistered = FALSE;
707
708 try
709 {
710 // load and parse machine XML; this will throw on XML or logic errors
711 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
712
713 if (mData->mUuid != mData->pMachineConfigFile->uuid)
714 throw setError(E_FAIL,
715 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
716 mData->pMachineConfigFile->uuid.raw(),
717 mData->m_strConfigFileFull.c_str(),
718 mData->mUuid.toString().c_str(),
719 mParent->settingsFilePath().c_str());
720
721 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
722 NULL /* const Guid *puuidRegistry */);
723 if (FAILED(rc)) throw rc;
724 }
725 catch (HRESULT err)
726 {
727 /* we assume that error info is set by the thrower */
728 rc = err;
729 }
730 catch (...)
731 {
732 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
733 }
734
735 /* Restore the registered flag (even on failure) */
736 mData->mRegistered = TRUE;
737 }
738
739 if (SUCCEEDED(rc))
740 {
741 /* Set mAccessible to TRUE only if we successfully locked and loaded
742 * the settings file */
743 mData->mAccessible = TRUE;
744
745 /* commit all changes made during loading the settings file */
746 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
747 /// @todo r=klaus for some reason the settings loading logic backs up
748 // the settings, and therefore a commit is needed. Should probably be changed.
749 }
750 else
751 {
752 /* If the machine is registered, then, instead of returning a
753 * failure, we mark it as inaccessible and set the result to
754 * success to give it a try later */
755
756 /* fetch the current error info */
757 mData->mAccessError = com::ErrorInfo();
758 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
759 mData->mUuid.raw(),
760 mData->mAccessError.getText().raw()));
761
762 /* rollback all changes */
763 rollback(false /* aNotify */);
764
765 // uninit media from this machine's media registry, or else
766 // reloading the settings will fail
767 mParent->unregisterMachineMedia(getId());
768
769 /* uninitialize the common part to make sure all data is reset to
770 * default (null) values */
771 uninitDataAndChildObjects();
772
773 rc = S_OK;
774 }
775
776 return rc;
777}
778
779/**
780 * Uninitializes the instance.
781 * Called either from FinalRelease() or by the parent when it gets destroyed.
782 *
783 * @note The caller of this method must make sure that this object
784 * a) doesn't have active callers on the current thread and b) is not locked
785 * by the current thread; otherwise uninit() will hang either a) due to
786 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
787 * a dead-lock caused by this thread waiting for all callers on the other
788 * threads are done but preventing them from doing so by holding a lock.
789 */
790void Machine::uninit()
791{
792 LogFlowThisFuncEnter();
793
794 Assert(!isWriteLockOnCurrentThread());
795
796 Assert(!uRegistryNeedsSaving);
797 if (uRegistryNeedsSaving)
798 {
799 AutoCaller autoCaller(this);
800 if (SUCCEEDED(autoCaller.rc()))
801 {
802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
803 saveSettings(NULL, Machine::SaveS_Force);
804 }
805 }
806
807 /* Enclose the state transition Ready->InUninit->NotReady */
808 AutoUninitSpan autoUninitSpan(this);
809 if (autoUninitSpan.uninitDone())
810 return;
811
812 Assert(!isSnapshotMachine());
813 Assert(!isSessionMachine());
814 Assert(!!mData);
815
816 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
817 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
818
819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
820
821 if (!mData->mSession.mMachine.isNull())
822 {
823 /* Theoretically, this can only happen if the VirtualBox server has been
824 * terminated while there were clients running that owned open direct
825 * sessions. Since in this case we are definitely called by
826 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
827 * won't happen on the client watcher thread (because it does
828 * VirtualBox::addCaller() for the duration of the
829 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
830 * cannot happen until the VirtualBox caller is released). This is
831 * important, because SessionMachine::uninit() cannot correctly operate
832 * after we return from this method (it expects the Machine instance is
833 * still valid). We'll call it ourselves below.
834 */
835 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
836 (SessionMachine*)mData->mSession.mMachine));
837
838 if (Global::IsOnlineOrTransient(mData->mMachineState))
839 {
840 LogWarningThisFunc(("Setting state to Aborted!\n"));
841 /* set machine state using SessionMachine reimplementation */
842 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
843 }
844
845 /*
846 * Uninitialize SessionMachine using public uninit() to indicate
847 * an unexpected uninitialization.
848 */
849 mData->mSession.mMachine->uninit();
850 /* SessionMachine::uninit() must set mSession.mMachine to null */
851 Assert(mData->mSession.mMachine.isNull());
852 }
853
854 // uninit media from this machine's media registry, if they're still there
855 Guid uuidMachine(getId());
856
857 /* the lock is no more necessary (SessionMachine is uninitialized) */
858 alock.release();
859
860 /* XXX This will fail with
861 * "cannot be closed because it is still attached to 1 virtual machines"
862 * because at this point we did not call uninitDataAndChildObjects() yet
863 * and therefore also removeBackReference() for all these mediums was not called! */
864
865 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
866 mParent->unregisterMachineMedia(uuidMachine);
867
868 // has machine been modified?
869 if (mData->flModifications)
870 {
871 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
872 rollback(false /* aNotify */);
873 }
874
875 if (mData->mAccessible)
876 uninitDataAndChildObjects();
877
878 /* free the essential data structure last */
879 mData.free();
880
881 LogFlowThisFuncLeave();
882}
883
884// IMachine properties
885/////////////////////////////////////////////////////////////////////////////
886
887STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
888{
889 CheckComArgOutPointerValid(aParent);
890
891 AutoLimitedCaller autoCaller(this);
892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
893
894 /* mParent is constant during life time, no need to lock */
895 ComObjPtr<VirtualBox> pVirtualBox(mParent);
896 pVirtualBox.queryInterfaceTo(aParent);
897
898 return S_OK;
899}
900
901STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
902{
903 CheckComArgOutPointerValid(aAccessible);
904
905 AutoLimitedCaller autoCaller(this);
906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
907
908 LogFlowThisFunc(("ENTER\n"));
909
910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
911
912 HRESULT rc = S_OK;
913
914 if (!mData->mAccessible)
915 {
916 /* try to initialize the VM once more if not accessible */
917
918 AutoReinitSpan autoReinitSpan(this);
919 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
920
921#ifdef DEBUG
922 LogFlowThisFunc(("Dumping media backreferences\n"));
923 mParent->dumpAllBackRefs();
924#endif
925
926 if (mData->pMachineConfigFile)
927 {
928 // reset the XML file to force loadSettings() (called from registeredInit())
929 // to parse it again; the file might have changed
930 delete mData->pMachineConfigFile;
931 mData->pMachineConfigFile = NULL;
932 }
933
934 rc = registeredInit();
935
936 if (SUCCEEDED(rc) && mData->mAccessible)
937 {
938 autoReinitSpan.setSucceeded();
939
940 /* make sure interesting parties will notice the accessibility
941 * state change */
942 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
943 mParent->onMachineDataChange(mData->mUuid);
944 }
945 }
946
947 if (SUCCEEDED(rc))
948 *aAccessible = mData->mAccessible;
949
950 LogFlowThisFuncLeave();
951
952 return rc;
953}
954
955STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
956{
957 CheckComArgOutPointerValid(aAccessError);
958
959 AutoLimitedCaller autoCaller(this);
960 if (FAILED(autoCaller.rc())) return autoCaller.rc();
961
962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
963
964 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
965 {
966 /* return shortly */
967 aAccessError = NULL;
968 return S_OK;
969 }
970
971 HRESULT rc = S_OK;
972
973 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
974 rc = errorInfo.createObject();
975 if (SUCCEEDED(rc))
976 {
977 errorInfo->init(mData->mAccessError.getResultCode(),
978 mData->mAccessError.getInterfaceID().ref(),
979 Utf8Str(mData->mAccessError.getComponent()).c_str(),
980 Utf8Str(mData->mAccessError.getText()));
981 rc = errorInfo.queryInterfaceTo(aAccessError);
982 }
983
984 return rc;
985}
986
987STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
988{
989 CheckComArgOutPointerValid(aName);
990
991 AutoCaller autoCaller(this);
992 if (FAILED(autoCaller.rc())) return autoCaller.rc();
993
994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
995
996 mUserData->s.strName.cloneTo(aName);
997
998 return S_OK;
999}
1000
1001STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1002{
1003 CheckComArgStrNotEmptyOrNull(aName);
1004
1005 AutoCaller autoCaller(this);
1006 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1007
1008 // prohibit setting a UUID only as the machine name, or else it can
1009 // never be found by findMachine()
1010 Guid test(aName);
1011
1012 if (test.isValid())
1013 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1014
1015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1016
1017 HRESULT rc = checkStateDependency(MutableStateDep);
1018 if (FAILED(rc)) return rc;
1019
1020 setModified(IsModified_MachineData);
1021 mUserData.backup();
1022 mUserData->s.strName = aName;
1023
1024 return S_OK;
1025}
1026
1027STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1028{
1029 CheckComArgOutPointerValid(aDescription);
1030
1031 AutoCaller autoCaller(this);
1032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1033
1034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 mUserData->s.strDescription.cloneTo(aDescription);
1037
1038 return S_OK;
1039}
1040
1041STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1042{
1043 AutoCaller autoCaller(this);
1044 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1045
1046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1047
1048 // this can be done in principle in any state as it doesn't affect the VM
1049 // significantly, but play safe by not messing around while complex
1050 // activities are going on
1051 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1052 if (FAILED(rc)) return rc;
1053
1054 setModified(IsModified_MachineData);
1055 mUserData.backup();
1056 mUserData->s.strDescription = aDescription;
1057
1058 return S_OK;
1059}
1060
1061STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1062{
1063 CheckComArgOutPointerValid(aId);
1064
1065 AutoLimitedCaller autoCaller(this);
1066 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1067
1068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1069
1070 mData->mUuid.toUtf16().cloneTo(aId);
1071
1072 return S_OK;
1073}
1074
1075STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1076{
1077 CheckComArgOutSafeArrayPointerValid(aGroups);
1078
1079 AutoCaller autoCaller(this);
1080 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1081
1082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1083 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1084 size_t i = 0;
1085 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1086 it != mUserData->s.llGroups.end();
1087 ++it, i++)
1088 {
1089 Bstr tmp = *it;
1090 tmp.cloneTo(&groups[i]);
1091 }
1092 groups.detachTo(ComSafeArrayOutArg(aGroups));
1093
1094 return S_OK;
1095}
1096
1097STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1098{
1099 AutoCaller autoCaller(this);
1100 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1101
1102 StringsList llGroups;
1103 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1104 if (FAILED(rc))
1105 return rc;
1106
1107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1108
1109 // changing machine groups is possible while the VM is offline
1110 rc = checkStateDependency(OfflineStateDep);
1111 if (FAILED(rc)) return rc;
1112
1113 setModified(IsModified_MachineData);
1114 mUserData.backup();
1115 mUserData->s.llGroups = llGroups;
1116
1117 return S_OK;
1118}
1119
1120STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1121{
1122 CheckComArgOutPointerValid(aOSTypeId);
1123
1124 AutoCaller autoCaller(this);
1125 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1126
1127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1128
1129 mUserData->s.strOsType.cloneTo(aOSTypeId);
1130
1131 return S_OK;
1132}
1133
1134STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1135{
1136 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1137
1138 AutoCaller autoCaller(this);
1139 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1140
1141 /* look up the object by Id to check it is valid */
1142 ComPtr<IGuestOSType> guestOSType;
1143 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1144 if (FAILED(rc)) return rc;
1145
1146 /* when setting, always use the "etalon" value for consistency -- lookup
1147 * by ID is case-insensitive and the input value may have different case */
1148 Bstr osTypeId;
1149 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1150 if (FAILED(rc)) return rc;
1151
1152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1153
1154 rc = checkStateDependency(MutableStateDep);
1155 if (FAILED(rc)) return rc;
1156
1157 setModified(IsModified_MachineData);
1158 mUserData.backup();
1159 mUserData->s.strOsType = osTypeId;
1160
1161 return S_OK;
1162}
1163
1164
1165STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1166{
1167 CheckComArgOutPointerValid(aFirmwareType);
1168
1169 AutoCaller autoCaller(this);
1170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1171
1172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1173
1174 *aFirmwareType = mHWData->mFirmwareType;
1175
1176 return S_OK;
1177}
1178
1179STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1180{
1181 AutoCaller autoCaller(this);
1182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1184
1185 HRESULT rc = checkStateDependency(MutableStateDep);
1186 if (FAILED(rc)) return rc;
1187
1188 setModified(IsModified_MachineData);
1189 mHWData.backup();
1190 mHWData->mFirmwareType = aFirmwareType;
1191
1192 return S_OK;
1193}
1194
1195STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1196{
1197 CheckComArgOutPointerValid(aKeyboardHIDType);
1198
1199 AutoCaller autoCaller(this);
1200 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1201
1202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1203
1204 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1205
1206 return S_OK;
1207}
1208
1209STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1210{
1211 AutoCaller autoCaller(this);
1212 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1214
1215 HRESULT rc = checkStateDependency(MutableStateDep);
1216 if (FAILED(rc)) return rc;
1217
1218 setModified(IsModified_MachineData);
1219 mHWData.backup();
1220 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1221
1222 return S_OK;
1223}
1224
1225STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1226{
1227 CheckComArgOutPointerValid(aPointingHIDType);
1228
1229 AutoCaller autoCaller(this);
1230 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1231
1232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1233
1234 *aPointingHIDType = mHWData->mPointingHIDType;
1235
1236 return S_OK;
1237}
1238
1239STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1240{
1241 AutoCaller autoCaller(this);
1242 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1244
1245 HRESULT rc = checkStateDependency(MutableStateDep);
1246 if (FAILED(rc)) return rc;
1247
1248 setModified(IsModified_MachineData);
1249 mHWData.backup();
1250 mHWData->mPointingHIDType = aPointingHIDType;
1251
1252 return S_OK;
1253}
1254
1255STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1256{
1257 CheckComArgOutPointerValid(aChipsetType);
1258
1259 AutoCaller autoCaller(this);
1260 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1261
1262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1263
1264 *aChipsetType = mHWData->mChipsetType;
1265
1266 return S_OK;
1267}
1268
1269STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1270{
1271 AutoCaller autoCaller(this);
1272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1274
1275 HRESULT rc = checkStateDependency(MutableStateDep);
1276 if (FAILED(rc)) return rc;
1277
1278 if (aChipsetType != mHWData->mChipsetType)
1279 {
1280 setModified(IsModified_MachineData);
1281 mHWData.backup();
1282 mHWData->mChipsetType = aChipsetType;
1283
1284 // Resize network adapter array, to be finalized on commit/rollback.
1285 // We must not throw away entries yet, otherwise settings are lost
1286 // without a way to roll back.
1287 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1288 uint32_t oldCount = mNetworkAdapters.size();
1289 if (newCount > oldCount)
1290 {
1291 mNetworkAdapters.resize(newCount);
1292 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1293 {
1294 unconst(mNetworkAdapters[slot]).createObject();
1295 mNetworkAdapters[slot]->init(this, slot);
1296 }
1297 }
1298 }
1299
1300 return S_OK;
1301}
1302
1303STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1304{
1305 CheckComArgOutPointerValid(aHWVersion);
1306
1307 AutoCaller autoCaller(this);
1308 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1309
1310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1311
1312 mHWData->mHWVersion.cloneTo(aHWVersion);
1313
1314 return S_OK;
1315}
1316
1317STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1318{
1319 /* check known version */
1320 Utf8Str hwVersion = aHWVersion;
1321 if ( hwVersion.compare("1") != 0
1322 && hwVersion.compare("2") != 0)
1323 return setError(E_INVALIDARG,
1324 tr("Invalid hardware version: %ls\n"), aHWVersion);
1325
1326 AutoCaller autoCaller(this);
1327 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1328
1329 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1330
1331 HRESULT rc = checkStateDependency(MutableStateDep);
1332 if (FAILED(rc)) return rc;
1333
1334 setModified(IsModified_MachineData);
1335 mHWData.backup();
1336 mHWData->mHWVersion = hwVersion;
1337
1338 return S_OK;
1339}
1340
1341STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1342{
1343 CheckComArgOutPointerValid(aUUID);
1344
1345 AutoCaller autoCaller(this);
1346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1347
1348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1349
1350 if (mHWData->mHardwareUUID.isValid())
1351 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1352 else
1353 mData->mUuid.toUtf16().cloneTo(aUUID);
1354
1355 return S_OK;
1356}
1357
1358STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1359{
1360 Guid hardwareUUID(aUUID);
1361 if (!hardwareUUID.isValid())
1362 return E_INVALIDARG;
1363
1364 AutoCaller autoCaller(this);
1365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1366
1367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1368
1369 HRESULT rc = checkStateDependency(MutableStateDep);
1370 if (FAILED(rc)) return rc;
1371
1372 setModified(IsModified_MachineData);
1373 mHWData.backup();
1374 if (hardwareUUID == mData->mUuid)
1375 mHWData->mHardwareUUID.clear();
1376 else
1377 mHWData->mHardwareUUID = hardwareUUID;
1378
1379 return S_OK;
1380}
1381
1382STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1383{
1384 CheckComArgOutPointerValid(memorySize);
1385
1386 AutoCaller autoCaller(this);
1387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1388
1389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1390
1391 *memorySize = mHWData->mMemorySize;
1392
1393 return S_OK;
1394}
1395
1396STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1397{
1398 /* check RAM limits */
1399 if ( memorySize < MM_RAM_MIN_IN_MB
1400 || memorySize > MM_RAM_MAX_IN_MB
1401 )
1402 return setError(E_INVALIDARG,
1403 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1404 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1405
1406 AutoCaller autoCaller(this);
1407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1408
1409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 HRESULT rc = checkStateDependency(MutableStateDep);
1412 if (FAILED(rc)) return rc;
1413
1414 setModified(IsModified_MachineData);
1415 mHWData.backup();
1416 mHWData->mMemorySize = memorySize;
1417
1418 return S_OK;
1419}
1420
1421STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1422{
1423 CheckComArgOutPointerValid(CPUCount);
1424
1425 AutoCaller autoCaller(this);
1426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1427
1428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1429
1430 *CPUCount = mHWData->mCPUCount;
1431
1432 return S_OK;
1433}
1434
1435STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1436{
1437 /* check CPU limits */
1438 if ( CPUCount < SchemaDefs::MinCPUCount
1439 || CPUCount > SchemaDefs::MaxCPUCount
1440 )
1441 return setError(E_INVALIDARG,
1442 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1443 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1444
1445 AutoCaller autoCaller(this);
1446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1447
1448 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1449
1450 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1451 if (mHWData->mCPUHotPlugEnabled)
1452 {
1453 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1454 {
1455 if (mHWData->mCPUAttached[idx])
1456 return setError(E_INVALIDARG,
1457 tr("There is still a CPU attached to socket %lu."
1458 "Detach the CPU before removing the socket"),
1459 CPUCount, idx+1);
1460 }
1461 }
1462
1463 HRESULT rc = checkStateDependency(MutableStateDep);
1464 if (FAILED(rc)) return rc;
1465
1466 setModified(IsModified_MachineData);
1467 mHWData.backup();
1468 mHWData->mCPUCount = CPUCount;
1469
1470 return S_OK;
1471}
1472
1473STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1474{
1475 CheckComArgOutPointerValid(aExecutionCap);
1476
1477 AutoCaller autoCaller(this);
1478 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1479
1480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1481
1482 *aExecutionCap = mHWData->mCpuExecutionCap;
1483
1484 return S_OK;
1485}
1486
1487STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1488{
1489 HRESULT rc = S_OK;
1490
1491 /* check throttle limits */
1492 if ( aExecutionCap < 1
1493 || aExecutionCap > 100
1494 )
1495 return setError(E_INVALIDARG,
1496 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1497 aExecutionCap, 1, 100);
1498
1499 AutoCaller autoCaller(this);
1500 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1501
1502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1503
1504 alock.release();
1505 rc = onCPUExecutionCapChange(aExecutionCap);
1506 alock.acquire();
1507 if (FAILED(rc)) return rc;
1508
1509 setModified(IsModified_MachineData);
1510 mHWData.backup();
1511 mHWData->mCpuExecutionCap = aExecutionCap;
1512
1513 /* Save settings if online - todo why is this required?? */
1514 if (Global::IsOnline(mData->mMachineState))
1515 saveSettings(NULL);
1516
1517 return S_OK;
1518}
1519
1520
1521STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1522{
1523 CheckComArgOutPointerValid(aEnabled);
1524
1525 AutoCaller autoCaller(this);
1526 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1527
1528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1529
1530 *aEnabled = mHWData->mCPUHotPlugEnabled;
1531
1532 return S_OK;
1533}
1534
1535STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1536{
1537 HRESULT rc = S_OK;
1538
1539 AutoCaller autoCaller(this);
1540 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1541
1542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1543
1544 rc = checkStateDependency(MutableStateDep);
1545 if (FAILED(rc)) return rc;
1546
1547 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1548 {
1549 if (aEnabled)
1550 {
1551 setModified(IsModified_MachineData);
1552 mHWData.backup();
1553
1554 /* Add the amount of CPUs currently attached */
1555 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1556 {
1557 mHWData->mCPUAttached[i] = true;
1558 }
1559 }
1560 else
1561 {
1562 /*
1563 * We can disable hotplug only if the amount of maximum CPUs is equal
1564 * to the amount of attached CPUs
1565 */
1566 unsigned cCpusAttached = 0;
1567 unsigned iHighestId = 0;
1568
1569 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1570 {
1571 if (mHWData->mCPUAttached[i])
1572 {
1573 cCpusAttached++;
1574 iHighestId = i;
1575 }
1576 }
1577
1578 if ( (cCpusAttached != mHWData->mCPUCount)
1579 || (iHighestId >= mHWData->mCPUCount))
1580 return setError(E_INVALIDARG,
1581 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1582
1583 setModified(IsModified_MachineData);
1584 mHWData.backup();
1585 }
1586 }
1587
1588 mHWData->mCPUHotPlugEnabled = aEnabled;
1589
1590 return rc;
1591}
1592
1593STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1594{
1595#ifdef VBOX_WITH_USB_CARDREADER
1596 CheckComArgOutPointerValid(aEnabled);
1597
1598 AutoCaller autoCaller(this);
1599 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1600
1601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1602
1603 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1604
1605 return S_OK;
1606#else
1607 NOREF(aEnabled);
1608 return E_NOTIMPL;
1609#endif
1610}
1611
1612STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1613{
1614#ifdef VBOX_WITH_USB_CARDREADER
1615 AutoCaller autoCaller(this);
1616 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1618
1619 HRESULT rc = checkStateDependency(MutableStateDep);
1620 if (FAILED(rc)) return rc;
1621
1622 setModified(IsModified_MachineData);
1623 mHWData.backup();
1624 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1625
1626 return S_OK;
1627#else
1628 NOREF(aEnabled);
1629 return E_NOTIMPL;
1630#endif
1631}
1632
1633STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1634{
1635#ifdef VBOX_WITH_USB_VIDEO
1636 CheckComArgOutPointerValid(aEnabled);
1637
1638 AutoCaller autoCaller(this);
1639 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1640
1641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1642
1643 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1644
1645 return S_OK;
1646#else
1647 NOREF(aEnabled);
1648 return E_NOTIMPL;
1649#endif
1650}
1651
1652STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1653{
1654#ifdef VBOX_WITH_USB_VIDEO
1655 AutoCaller autoCaller(this);
1656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1658
1659 HRESULT rc = checkStateDependency(MutableStateDep);
1660 if (FAILED(rc)) return rc;
1661
1662 setModified(IsModified_MachineData);
1663 mHWData.backup();
1664 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1665
1666 return S_OK;
1667#else
1668 NOREF(aEnabled);
1669 return E_NOTIMPL;
1670#endif
1671}
1672
1673STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1674{
1675 CheckComArgOutPointerValid(aEnabled);
1676
1677 AutoCaller autoCaller(this);
1678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1680
1681 *aEnabled = mHWData->mHPETEnabled;
1682
1683 return S_OK;
1684}
1685
1686STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1687{
1688 HRESULT rc = S_OK;
1689
1690 AutoCaller autoCaller(this);
1691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1693
1694 rc = checkStateDependency(MutableStateDep);
1695 if (FAILED(rc)) return rc;
1696
1697 setModified(IsModified_MachineData);
1698 mHWData.backup();
1699
1700 mHWData->mHPETEnabled = aEnabled;
1701
1702 return rc;
1703}
1704
1705STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1706{
1707 AutoCaller autoCaller(this);
1708 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1709
1710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1711
1712 *fEnabled = mHWData->mVideoCaptureEnabled;
1713 return S_OK;
1714}
1715
1716STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1717{
1718 AutoCaller autoCaller(this);
1719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1720
1721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1722 mHWData->mVideoCaptureEnabled = fEnabled;
1723 return S_OK;
1724}
1725
1726STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1727{
1728 AutoCaller autoCaller(this);
1729 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1730
1731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1732 mHWData->mVideoCaptureFile.cloneTo(apFile);
1733 return S_OK;
1734}
1735
1736STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1737{
1738 Utf8Str strFile(aFile);
1739 AutoCaller autoCaller(this);
1740 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1741
1742 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1743 if (strFile.isEmpty())
1744 strFile = "VideoCap.webm";
1745 mHWData->mVideoCaptureFile = strFile;
1746 return S_OK;
1747}
1748
1749STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1750{
1751 AutoCaller autoCaller(this);
1752 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1753
1754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1755 *aHorzRes = mHWData->mVideoCaptureWidth;
1756 return S_OK;
1757}
1758
1759STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1760{
1761 AutoCaller autoCaller(this);
1762 if (FAILED(autoCaller.rc()))
1763 {
1764 LogFlow(("Autolocked failed\n"));
1765 return autoCaller.rc();
1766 }
1767
1768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1769 mHWData->mVideoCaptureWidth = aHorzRes;
1770 return S_OK;
1771}
1772
1773STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1774{
1775 AutoCaller autoCaller(this);
1776 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1777
1778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1779 *aVertRes = mHWData->mVideoCaptureHeight;
1780 return S_OK;
1781}
1782
1783STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1784{
1785 AutoCaller autoCaller(this);
1786 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1787
1788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1789 mHWData->mVideoCaptureHeight = aVertRes;
1790 return S_OK;
1791}
1792
1793STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1794{
1795 AutoCaller autoCaller(this);
1796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1797
1798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1799 *aRate = mHWData->mVideoCaptureRate;
1800 return S_OK;
1801}
1802
1803STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1804{
1805 AutoCaller autoCaller(this);
1806 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1807
1808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1809 mHWData->mVideoCaptureHeight = aRate;
1810 return S_OK;
1811}
1812
1813STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1814{
1815 CheckComArgOutPointerValid(aGraphicsControllerType);
1816
1817 AutoCaller autoCaller(this);
1818 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1819
1820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1821
1822 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1823
1824 return S_OK;
1825}
1826
1827STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1828{
1829 switch (aGraphicsControllerType)
1830 {
1831 case GraphicsControllerType_Null:
1832 case GraphicsControllerType_VBoxVGA:
1833 break;
1834 default:
1835 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1836 }
1837
1838 AutoCaller autoCaller(this);
1839 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1840
1841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1842
1843 HRESULT rc = checkStateDependency(MutableStateDep);
1844 if (FAILED(rc)) return rc;
1845
1846 setModified(IsModified_MachineData);
1847 mHWData.backup();
1848 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1849
1850 return S_OK;
1851}
1852
1853STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1854{
1855 CheckComArgOutPointerValid(memorySize);
1856
1857 AutoCaller autoCaller(this);
1858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1859
1860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 *memorySize = mHWData->mVRAMSize;
1863
1864 return S_OK;
1865}
1866
1867STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1868{
1869 /* check VRAM limits */
1870 if (memorySize < SchemaDefs::MinGuestVRAM ||
1871 memorySize > SchemaDefs::MaxGuestVRAM)
1872 return setError(E_INVALIDARG,
1873 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1874 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1875
1876 AutoCaller autoCaller(this);
1877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1878
1879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1880
1881 HRESULT rc = checkStateDependency(MutableStateDep);
1882 if (FAILED(rc)) return rc;
1883
1884 setModified(IsModified_MachineData);
1885 mHWData.backup();
1886 mHWData->mVRAMSize = memorySize;
1887
1888 return S_OK;
1889}
1890
1891/** @todo this method should not be public */
1892STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1893{
1894 CheckComArgOutPointerValid(memoryBalloonSize);
1895
1896 AutoCaller autoCaller(this);
1897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1898
1899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1900
1901 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1902
1903 return S_OK;
1904}
1905
1906/**
1907 * Set the memory balloon size.
1908 *
1909 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1910 * we have to make sure that we never call IGuest from here.
1911 */
1912STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1913{
1914 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1915#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1916 /* check limits */
1917 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1918 return setError(E_INVALIDARG,
1919 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1920 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1921
1922 AutoCaller autoCaller(this);
1923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1924
1925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 setModified(IsModified_MachineData);
1928 mHWData.backup();
1929 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1930
1931 return S_OK;
1932#else
1933 NOREF(memoryBalloonSize);
1934 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1935#endif
1936}
1937
1938STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
1939{
1940 CheckComArgOutPointerValid(aEnabled);
1941
1942 AutoCaller autoCaller(this);
1943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1944
1945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1946
1947 *aEnabled = mHWData->mPageFusionEnabled;
1948 return S_OK;
1949}
1950
1951STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
1952{
1953#ifdef VBOX_WITH_PAGE_SHARING
1954 AutoCaller autoCaller(this);
1955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1956
1957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1958
1959 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1960 setModified(IsModified_MachineData);
1961 mHWData.backup();
1962 mHWData->mPageFusionEnabled = aEnabled;
1963 return S_OK;
1964#else
1965 NOREF(aEnabled);
1966 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1967#endif
1968}
1969
1970STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
1971{
1972 CheckComArgOutPointerValid(aEnabled);
1973
1974 AutoCaller autoCaller(this);
1975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1976
1977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1978
1979 *aEnabled = mHWData->mAccelerate3DEnabled;
1980
1981 return S_OK;
1982}
1983
1984STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1985{
1986 AutoCaller autoCaller(this);
1987 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1988
1989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1990
1991 HRESULT rc = checkStateDependency(MutableStateDep);
1992 if (FAILED(rc)) return rc;
1993
1994 /** @todo check validity! */
1995
1996 setModified(IsModified_MachineData);
1997 mHWData.backup();
1998 mHWData->mAccelerate3DEnabled = enable;
1999
2000 return S_OK;
2001}
2002
2003
2004STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2005{
2006 CheckComArgOutPointerValid(aEnabled);
2007
2008 AutoCaller autoCaller(this);
2009 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2010
2011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2012
2013 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2014
2015 return S_OK;
2016}
2017
2018STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2019{
2020 AutoCaller autoCaller(this);
2021 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2022
2023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2024
2025 HRESULT rc = checkStateDependency(MutableStateDep);
2026 if (FAILED(rc)) return rc;
2027
2028 /** @todo check validity! */
2029
2030 setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mAccelerate2DVideoEnabled = enable;
2033
2034 return S_OK;
2035}
2036
2037STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2038{
2039 CheckComArgOutPointerValid(monitorCount);
2040
2041 AutoCaller autoCaller(this);
2042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2043
2044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2045
2046 *monitorCount = mHWData->mMonitorCount;
2047
2048 return S_OK;
2049}
2050
2051STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2052{
2053 /* make sure monitor count is a sensible number */
2054 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2055 return setError(E_INVALIDARG,
2056 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2057 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2058
2059 AutoCaller autoCaller(this);
2060 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2061
2062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2063
2064 HRESULT rc = checkStateDependency(MutableStateDep);
2065 if (FAILED(rc)) return rc;
2066
2067 setModified(IsModified_MachineData);
2068 mHWData.backup();
2069 mHWData->mMonitorCount = monitorCount;
2070
2071 return S_OK;
2072}
2073
2074STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2075{
2076 CheckComArgOutPointerValid(biosSettings);
2077
2078 AutoCaller autoCaller(this);
2079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2080
2081 /* mBIOSSettings is constant during life time, no need to lock */
2082 mBIOSSettings.queryInterfaceTo(biosSettings);
2083
2084 return S_OK;
2085}
2086
2087STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2088{
2089 CheckComArgOutPointerValid(aVal);
2090
2091 AutoCaller autoCaller(this);
2092 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2093
2094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2095
2096 switch (property)
2097 {
2098 case CPUPropertyType_PAE:
2099 *aVal = mHWData->mPAEEnabled;
2100 break;
2101
2102 case CPUPropertyType_Synthetic:
2103 *aVal = mHWData->mSyntheticCpu;
2104 break;
2105
2106 case CPUPropertyType_LongMode:
2107 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2108 *aVal = TRUE;
2109 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2110 *aVal = FALSE;
2111#if HC_ARCH_BITS == 64
2112 else
2113 *aVal = TRUE;
2114#else
2115 else
2116 {
2117 *aVal = FALSE;
2118
2119 ComPtr<IGuestOSType> ptrGuestOSType;
2120 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2121 if (SUCCEEDED(hrc2))
2122 {
2123 BOOL fIs64Bit = FALSE;
2124 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2125 if (SUCCEEDED(hrc2) && fIs64Bit)
2126 {
2127 ComObjPtr<Host> ptrHost = mParent->host();
2128 alock.release();
2129
2130 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2131 if (FAILED(hrc2))
2132 *aVal = FALSE;
2133 }
2134 }
2135 }
2136#endif
2137 break;
2138
2139 default:
2140 return E_INVALIDARG;
2141 }
2142 return S_OK;
2143}
2144
2145STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2146{
2147 AutoCaller autoCaller(this);
2148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2149
2150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2151
2152 HRESULT rc = checkStateDependency(MutableStateDep);
2153 if (FAILED(rc)) return rc;
2154
2155 switch (property)
2156 {
2157 case CPUPropertyType_PAE:
2158 setModified(IsModified_MachineData);
2159 mHWData.backup();
2160 mHWData->mPAEEnabled = !!aVal;
2161 break;
2162
2163 case CPUPropertyType_Synthetic:
2164 setModified(IsModified_MachineData);
2165 mHWData.backup();
2166 mHWData->mSyntheticCpu = !!aVal;
2167 break;
2168
2169 case CPUPropertyType_LongMode:
2170 setModified(IsModified_MachineData);
2171 mHWData.backup();
2172 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2173 break;
2174
2175 default:
2176 return E_INVALIDARG;
2177 }
2178 return S_OK;
2179}
2180
2181STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2182{
2183 CheckComArgOutPointerValid(aValEax);
2184 CheckComArgOutPointerValid(aValEbx);
2185 CheckComArgOutPointerValid(aValEcx);
2186 CheckComArgOutPointerValid(aValEdx);
2187
2188 AutoCaller autoCaller(this);
2189 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2190
2191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2192
2193 switch(aId)
2194 {
2195 case 0x0:
2196 case 0x1:
2197 case 0x2:
2198 case 0x3:
2199 case 0x4:
2200 case 0x5:
2201 case 0x6:
2202 case 0x7:
2203 case 0x8:
2204 case 0x9:
2205 case 0xA:
2206 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2207 return E_INVALIDARG;
2208
2209 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2210 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2211 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2212 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2213 break;
2214
2215 case 0x80000000:
2216 case 0x80000001:
2217 case 0x80000002:
2218 case 0x80000003:
2219 case 0x80000004:
2220 case 0x80000005:
2221 case 0x80000006:
2222 case 0x80000007:
2223 case 0x80000008:
2224 case 0x80000009:
2225 case 0x8000000A:
2226 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2227 return E_INVALIDARG;
2228
2229 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2230 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2231 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2232 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2233 break;
2234
2235 default:
2236 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2237 }
2238 return S_OK;
2239}
2240
2241STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2242{
2243 AutoCaller autoCaller(this);
2244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2245
2246 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2247
2248 HRESULT rc = checkStateDependency(MutableStateDep);
2249 if (FAILED(rc)) return rc;
2250
2251 switch(aId)
2252 {
2253 case 0x0:
2254 case 0x1:
2255 case 0x2:
2256 case 0x3:
2257 case 0x4:
2258 case 0x5:
2259 case 0x6:
2260 case 0x7:
2261 case 0x8:
2262 case 0x9:
2263 case 0xA:
2264 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2265 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2266 setModified(IsModified_MachineData);
2267 mHWData.backup();
2268 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2269 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2270 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2271 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2272 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2273 break;
2274
2275 case 0x80000000:
2276 case 0x80000001:
2277 case 0x80000002:
2278 case 0x80000003:
2279 case 0x80000004:
2280 case 0x80000005:
2281 case 0x80000006:
2282 case 0x80000007:
2283 case 0x80000008:
2284 case 0x80000009:
2285 case 0x8000000A:
2286 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2287 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2288 setModified(IsModified_MachineData);
2289 mHWData.backup();
2290 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2291 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2292 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2293 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2294 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2295 break;
2296
2297 default:
2298 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2299 }
2300 return S_OK;
2301}
2302
2303STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2304{
2305 AutoCaller autoCaller(this);
2306 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2307
2308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2309
2310 HRESULT rc = checkStateDependency(MutableStateDep);
2311 if (FAILED(rc)) return rc;
2312
2313 switch(aId)
2314 {
2315 case 0x0:
2316 case 0x1:
2317 case 0x2:
2318 case 0x3:
2319 case 0x4:
2320 case 0x5:
2321 case 0x6:
2322 case 0x7:
2323 case 0x8:
2324 case 0x9:
2325 case 0xA:
2326 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2327 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2328 setModified(IsModified_MachineData);
2329 mHWData.backup();
2330 /* Invalidate leaf. */
2331 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2332 break;
2333
2334 case 0x80000000:
2335 case 0x80000001:
2336 case 0x80000002:
2337 case 0x80000003:
2338 case 0x80000004:
2339 case 0x80000005:
2340 case 0x80000006:
2341 case 0x80000007:
2342 case 0x80000008:
2343 case 0x80000009:
2344 case 0x8000000A:
2345 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2346 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2347 setModified(IsModified_MachineData);
2348 mHWData.backup();
2349 /* Invalidate leaf. */
2350 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2351 break;
2352
2353 default:
2354 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2355 }
2356 return S_OK;
2357}
2358
2359STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2360{
2361 AutoCaller autoCaller(this);
2362 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2363
2364 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2365
2366 HRESULT rc = checkStateDependency(MutableStateDep);
2367 if (FAILED(rc)) return rc;
2368
2369 setModified(IsModified_MachineData);
2370 mHWData.backup();
2371
2372 /* Invalidate all standard leafs. */
2373 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2374 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2375
2376 /* Invalidate all extended leafs. */
2377 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2378 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2379
2380 return S_OK;
2381}
2382
2383STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2384{
2385 CheckComArgOutPointerValid(aVal);
2386
2387 AutoCaller autoCaller(this);
2388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2389
2390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2391
2392 switch(property)
2393 {
2394 case HWVirtExPropertyType_Enabled:
2395 *aVal = mHWData->mHWVirtExEnabled;
2396 break;
2397
2398 case HWVirtExPropertyType_Exclusive:
2399 *aVal = mHWData->mHWVirtExExclusive;
2400 break;
2401
2402 case HWVirtExPropertyType_VPID:
2403 *aVal = mHWData->mHWVirtExVPIDEnabled;
2404 break;
2405
2406 case HWVirtExPropertyType_NestedPaging:
2407 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2408 break;
2409
2410 case HWVirtExPropertyType_LargePages:
2411 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2412#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2413 *aVal = FALSE;
2414#endif
2415 break;
2416
2417 case HWVirtExPropertyType_Force:
2418 *aVal = mHWData->mHWVirtExForceEnabled;
2419 break;
2420
2421 default:
2422 return E_INVALIDARG;
2423 }
2424 return S_OK;
2425}
2426
2427STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2428{
2429 AutoCaller autoCaller(this);
2430 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2431
2432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2433
2434 HRESULT rc = checkStateDependency(MutableStateDep);
2435 if (FAILED(rc)) return rc;
2436
2437 switch(property)
2438 {
2439 case HWVirtExPropertyType_Enabled:
2440 setModified(IsModified_MachineData);
2441 mHWData.backup();
2442 mHWData->mHWVirtExEnabled = !!aVal;
2443 break;
2444
2445 case HWVirtExPropertyType_Exclusive:
2446 setModified(IsModified_MachineData);
2447 mHWData.backup();
2448 mHWData->mHWVirtExExclusive = !!aVal;
2449 break;
2450
2451 case HWVirtExPropertyType_VPID:
2452 setModified(IsModified_MachineData);
2453 mHWData.backup();
2454 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2455 break;
2456
2457 case HWVirtExPropertyType_NestedPaging:
2458 setModified(IsModified_MachineData);
2459 mHWData.backup();
2460 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2461 break;
2462
2463 case HWVirtExPropertyType_LargePages:
2464 setModified(IsModified_MachineData);
2465 mHWData.backup();
2466 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2467 break;
2468
2469 case HWVirtExPropertyType_Force:
2470 setModified(IsModified_MachineData);
2471 mHWData.backup();
2472 mHWData->mHWVirtExForceEnabled = !!aVal;
2473 break;
2474
2475 default:
2476 return E_INVALIDARG;
2477 }
2478
2479 return S_OK;
2480}
2481
2482STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2483{
2484 CheckComArgOutPointerValid(aSnapshotFolder);
2485
2486 AutoCaller autoCaller(this);
2487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2488
2489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2490
2491 Utf8Str strFullSnapshotFolder;
2492 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2493 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2494
2495 return S_OK;
2496}
2497
2498STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2499{
2500 /* @todo (r=dmik):
2501 * 1. Allow to change the name of the snapshot folder containing snapshots
2502 * 2. Rename the folder on disk instead of just changing the property
2503 * value (to be smart and not to leave garbage). Note that it cannot be
2504 * done here because the change may be rolled back. Thus, the right
2505 * place is #saveSettings().
2506 */
2507
2508 AutoCaller autoCaller(this);
2509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2510
2511 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2512
2513 HRESULT rc = checkStateDependency(MutableStateDep);
2514 if (FAILED(rc)) return rc;
2515
2516 if (!mData->mCurrentSnapshot.isNull())
2517 return setError(E_FAIL,
2518 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2519
2520 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2521
2522 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2523 if (strSnapshotFolder.isEmpty())
2524 strSnapshotFolder = "Snapshots";
2525 int vrc = calculateFullPath(strSnapshotFolder,
2526 strSnapshotFolder);
2527 if (RT_FAILURE(vrc))
2528 return setError(E_FAIL,
2529 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2530 aSnapshotFolder, vrc);
2531
2532 setModified(IsModified_MachineData);
2533 mUserData.backup();
2534
2535 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2536
2537 return S_OK;
2538}
2539
2540STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2541{
2542 CheckComArgOutSafeArrayPointerValid(aAttachments);
2543
2544 AutoCaller autoCaller(this);
2545 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2546
2547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2548
2549 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2550 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2551
2552 return S_OK;
2553}
2554
2555STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2556{
2557 CheckComArgOutPointerValid(vrdeServer);
2558
2559 AutoCaller autoCaller(this);
2560 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2561
2562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2563
2564 Assert(!!mVRDEServer);
2565 mVRDEServer.queryInterfaceTo(vrdeServer);
2566
2567 return S_OK;
2568}
2569
2570STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2571{
2572 CheckComArgOutPointerValid(audioAdapter);
2573
2574 AutoCaller autoCaller(this);
2575 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2576
2577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2578
2579 mAudioAdapter.queryInterfaceTo(audioAdapter);
2580 return S_OK;
2581}
2582
2583STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2584{
2585#ifdef VBOX_WITH_VUSB
2586 CheckComArgOutPointerValid(aUSBController);
2587
2588 AutoCaller autoCaller(this);
2589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2590
2591 clearError();
2592 MultiResult rc(S_OK);
2593
2594# ifdef VBOX_WITH_USB
2595 rc = mParent->host()->checkUSBProxyService();
2596 if (FAILED(rc)) return rc;
2597# endif
2598
2599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2600
2601 return rc = mUSBController.queryInterfaceTo(aUSBController);
2602#else
2603 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2604 * extended error info to indicate that USB is simply not available
2605 * (w/o treating it as a failure), for example, as in OSE */
2606 NOREF(aUSBController);
2607 ReturnComNotImplemented();
2608#endif /* VBOX_WITH_VUSB */
2609}
2610
2611STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2612{
2613 CheckComArgOutPointerValid(aFilePath);
2614
2615 AutoLimitedCaller autoCaller(this);
2616 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2617
2618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2619
2620 mData->m_strConfigFileFull.cloneTo(aFilePath);
2621 return S_OK;
2622}
2623
2624STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2625{
2626 CheckComArgOutPointerValid(aModified);
2627
2628 AutoCaller autoCaller(this);
2629 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2630
2631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 HRESULT rc = checkStateDependency(MutableStateDep);
2634 if (FAILED(rc)) return rc;
2635
2636 if (!mData->pMachineConfigFile->fileExists())
2637 // this is a new machine, and no config file exists yet:
2638 *aModified = TRUE;
2639 else
2640 *aModified = (mData->flModifications != 0);
2641
2642 return S_OK;
2643}
2644
2645STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2646{
2647 CheckComArgOutPointerValid(aSessionState);
2648
2649 AutoCaller autoCaller(this);
2650 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2651
2652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2653
2654 *aSessionState = mData->mSession.mState;
2655
2656 return S_OK;
2657}
2658
2659STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2660{
2661 CheckComArgOutPointerValid(aSessionType);
2662
2663 AutoCaller autoCaller(this);
2664 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2665
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 mData->mSession.mType.cloneTo(aSessionType);
2669
2670 return S_OK;
2671}
2672
2673STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2674{
2675 CheckComArgOutPointerValid(aSessionPID);
2676
2677 AutoCaller autoCaller(this);
2678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2679
2680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2681
2682 *aSessionPID = mData->mSession.mPID;
2683
2684 return S_OK;
2685}
2686
2687STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2688{
2689 CheckComArgOutPointerValid(machineState);
2690
2691 AutoCaller autoCaller(this);
2692 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2693
2694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2695
2696 *machineState = mData->mMachineState;
2697
2698 return S_OK;
2699}
2700
2701STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2702{
2703 CheckComArgOutPointerValid(aLastStateChange);
2704
2705 AutoCaller autoCaller(this);
2706 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2707
2708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2709
2710 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2711
2712 return S_OK;
2713}
2714
2715STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2716{
2717 CheckComArgOutPointerValid(aStateFilePath);
2718
2719 AutoCaller autoCaller(this);
2720 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2721
2722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2723
2724 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2725
2726 return S_OK;
2727}
2728
2729STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2730{
2731 CheckComArgOutPointerValid(aLogFolder);
2732
2733 AutoCaller autoCaller(this);
2734 AssertComRCReturnRC(autoCaller.rc());
2735
2736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2737
2738 Utf8Str logFolder;
2739 getLogFolder(logFolder);
2740 logFolder.cloneTo(aLogFolder);
2741
2742 return S_OK;
2743}
2744
2745STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2746{
2747 CheckComArgOutPointerValid(aCurrentSnapshot);
2748
2749 AutoCaller autoCaller(this);
2750 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2751
2752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2753
2754 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2755
2756 return S_OK;
2757}
2758
2759STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2760{
2761 CheckComArgOutPointerValid(aSnapshotCount);
2762
2763 AutoCaller autoCaller(this);
2764 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2765
2766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2767
2768 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2769 ? 0
2770 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2771
2772 return S_OK;
2773}
2774
2775STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2776{
2777 CheckComArgOutPointerValid(aCurrentStateModified);
2778
2779 AutoCaller autoCaller(this);
2780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2781
2782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2783
2784 /* Note: for machines with no snapshots, we always return FALSE
2785 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2786 * reasons :) */
2787
2788 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2789 ? FALSE
2790 : mData->mCurrentStateModified;
2791
2792 return S_OK;
2793}
2794
2795STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2796{
2797 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2798
2799 AutoCaller autoCaller(this);
2800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2801
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803
2804 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2805 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2806
2807 return S_OK;
2808}
2809
2810STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2811{
2812 CheckComArgOutPointerValid(aClipboardMode);
2813
2814 AutoCaller autoCaller(this);
2815 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2816
2817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2818
2819 *aClipboardMode = mHWData->mClipboardMode;
2820
2821 return S_OK;
2822}
2823
2824STDMETHODIMP
2825Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2826{
2827 HRESULT rc = S_OK;
2828
2829 AutoCaller autoCaller(this);
2830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2831
2832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2833
2834 alock.release();
2835 rc = onClipboardModeChange(aClipboardMode);
2836 alock.acquire();
2837 if (FAILED(rc)) return rc;
2838
2839 setModified(IsModified_MachineData);
2840 mHWData.backup();
2841 mHWData->mClipboardMode = aClipboardMode;
2842
2843 /* Save settings if online - todo why is this required?? */
2844 if (Global::IsOnline(mData->mMachineState))
2845 saveSettings(NULL);
2846
2847 return S_OK;
2848}
2849
2850STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2851{
2852 CheckComArgOutPointerValid(aDragAndDropMode);
2853
2854 AutoCaller autoCaller(this);
2855 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2856
2857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 *aDragAndDropMode = mHWData->mDragAndDropMode;
2860
2861 return S_OK;
2862}
2863
2864STDMETHODIMP
2865Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2866{
2867 HRESULT rc = S_OK;
2868
2869 AutoCaller autoCaller(this);
2870 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2871
2872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2873
2874 alock.release();
2875 rc = onDragAndDropModeChange(aDragAndDropMode);
2876 alock.acquire();
2877 if (FAILED(rc)) return rc;
2878
2879 setModified(IsModified_MachineData);
2880 mHWData.backup();
2881 mHWData->mDragAndDropMode = aDragAndDropMode;
2882
2883 /* Save settings if online - todo why is this required?? */
2884 if (Global::IsOnline(mData->mMachineState))
2885 saveSettings(NULL);
2886
2887 return S_OK;
2888}
2889
2890STDMETHODIMP
2891Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2892{
2893 CheckComArgOutPointerValid(aPatterns);
2894
2895 AutoCaller autoCaller(this);
2896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2897
2898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 try
2901 {
2902 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2903 }
2904 catch (...)
2905 {
2906 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2907 }
2908
2909 return S_OK;
2910}
2911
2912STDMETHODIMP
2913Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2914{
2915 AutoCaller autoCaller(this);
2916 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2917
2918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2919
2920 HRESULT rc = checkStateDependency(MutableStateDep);
2921 if (FAILED(rc)) return rc;
2922
2923 setModified(IsModified_MachineData);
2924 mHWData.backup();
2925 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2926 return rc;
2927}
2928
2929STDMETHODIMP
2930Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2931{
2932 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2933
2934 AutoCaller autoCaller(this);
2935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2936
2937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2940 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2941
2942 return S_OK;
2943}
2944
2945STDMETHODIMP
2946Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2947{
2948 CheckComArgOutPointerValid(aEnabled);
2949
2950 AutoCaller autoCaller(this);
2951 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2952
2953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 *aEnabled = mUserData->s.fTeleporterEnabled;
2956
2957 return S_OK;
2958}
2959
2960STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2961{
2962 AutoCaller autoCaller(this);
2963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2964
2965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2966
2967 /* Only allow it to be set to true when PoweredOff or Aborted.
2968 (Clearing it is always permitted.) */
2969 if ( aEnabled
2970 && mData->mRegistered
2971 && ( !isSessionMachine()
2972 || ( mData->mMachineState != MachineState_PoweredOff
2973 && mData->mMachineState != MachineState_Teleported
2974 && mData->mMachineState != MachineState_Aborted
2975 )
2976 )
2977 )
2978 return setError(VBOX_E_INVALID_VM_STATE,
2979 tr("The machine is not powered off (state is %s)"),
2980 Global::stringifyMachineState(mData->mMachineState));
2981
2982 setModified(IsModified_MachineData);
2983 mUserData.backup();
2984 mUserData->s.fTeleporterEnabled = !!aEnabled;
2985
2986 return S_OK;
2987}
2988
2989STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2990{
2991 CheckComArgOutPointerValid(aPort);
2992
2993 AutoCaller autoCaller(this);
2994 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2995
2996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2997
2998 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2999
3000 return S_OK;
3001}
3002
3003STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3004{
3005 if (aPort >= _64K)
3006 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3007
3008 AutoCaller autoCaller(this);
3009 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3010
3011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3012
3013 HRESULT rc = checkStateDependency(MutableStateDep);
3014 if (FAILED(rc)) return rc;
3015
3016 setModified(IsModified_MachineData);
3017 mUserData.backup();
3018 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3019
3020 return S_OK;
3021}
3022
3023STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3024{
3025 CheckComArgOutPointerValid(aAddress);
3026
3027 AutoCaller autoCaller(this);
3028 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3029
3030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3031
3032 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3033
3034 return S_OK;
3035}
3036
3037STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3038{
3039 AutoCaller autoCaller(this);
3040 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3041
3042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3043
3044 HRESULT rc = checkStateDependency(MutableStateDep);
3045 if (FAILED(rc)) return rc;
3046
3047 setModified(IsModified_MachineData);
3048 mUserData.backup();
3049 mUserData->s.strTeleporterAddress = aAddress;
3050
3051 return S_OK;
3052}
3053
3054STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3055{
3056 CheckComArgOutPointerValid(aPassword);
3057
3058 AutoCaller autoCaller(this);
3059 HRESULT hrc = autoCaller.rc();
3060 if (SUCCEEDED(hrc))
3061 {
3062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3063 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3064 }
3065
3066 return hrc;
3067}
3068
3069STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3070{
3071 /*
3072 * Hash the password first.
3073 */
3074 Utf8Str strPassword(aPassword);
3075 if (!strPassword.isEmpty())
3076 {
3077 if (VBoxIsPasswordHashed(&strPassword))
3078 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3079 VBoxHashPassword(&strPassword);
3080 }
3081
3082 /*
3083 * Do the update.
3084 */
3085 AutoCaller autoCaller(this);
3086 HRESULT hrc = autoCaller.rc();
3087 if (SUCCEEDED(hrc))
3088 {
3089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3090 hrc = checkStateDependency(MutableStateDep);
3091 if (SUCCEEDED(hrc))
3092 {
3093 setModified(IsModified_MachineData);
3094 mUserData.backup();
3095 mUserData->s.strTeleporterPassword = strPassword;
3096 }
3097 }
3098
3099 return hrc;
3100}
3101
3102STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3103{
3104 CheckComArgOutPointerValid(aState);
3105
3106 AutoCaller autoCaller(this);
3107 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3108
3109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3110
3111 *aState = mUserData->s.enmFaultToleranceState;
3112 return S_OK;
3113}
3114
3115STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3116{
3117 AutoCaller autoCaller(this);
3118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3119
3120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3121
3122 /* @todo deal with running state change. */
3123 HRESULT rc = checkStateDependency(MutableStateDep);
3124 if (FAILED(rc)) return rc;
3125
3126 setModified(IsModified_MachineData);
3127 mUserData.backup();
3128 mUserData->s.enmFaultToleranceState = aState;
3129 return S_OK;
3130}
3131
3132STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3133{
3134 CheckComArgOutPointerValid(aAddress);
3135
3136 AutoCaller autoCaller(this);
3137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3138
3139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3140
3141 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3142 return S_OK;
3143}
3144
3145STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3146{
3147 AutoCaller autoCaller(this);
3148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3149
3150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3151
3152 /* @todo deal with running state change. */
3153 HRESULT rc = checkStateDependency(MutableStateDep);
3154 if (FAILED(rc)) return rc;
3155
3156 setModified(IsModified_MachineData);
3157 mUserData.backup();
3158 mUserData->s.strFaultToleranceAddress = aAddress;
3159 return S_OK;
3160}
3161
3162STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3163{
3164 CheckComArgOutPointerValid(aPort);
3165
3166 AutoCaller autoCaller(this);
3167 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3168
3169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3170
3171 *aPort = mUserData->s.uFaultTolerancePort;
3172 return S_OK;
3173}
3174
3175STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3176{
3177 AutoCaller autoCaller(this);
3178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3179
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 /* @todo deal with running state change. */
3183 HRESULT rc = checkStateDependency(MutableStateDep);
3184 if (FAILED(rc)) return rc;
3185
3186 setModified(IsModified_MachineData);
3187 mUserData.backup();
3188 mUserData->s.uFaultTolerancePort = aPort;
3189 return S_OK;
3190}
3191
3192STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3193{
3194 CheckComArgOutPointerValid(aPassword);
3195
3196 AutoCaller autoCaller(this);
3197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3198
3199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3200
3201 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3202
3203 return S_OK;
3204}
3205
3206STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3207{
3208 AutoCaller autoCaller(this);
3209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3210
3211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3212
3213 /* @todo deal with running state change. */
3214 HRESULT rc = checkStateDependency(MutableStateDep);
3215 if (FAILED(rc)) return rc;
3216
3217 setModified(IsModified_MachineData);
3218 mUserData.backup();
3219 mUserData->s.strFaultTolerancePassword = aPassword;
3220
3221 return S_OK;
3222}
3223
3224STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3225{
3226 CheckComArgOutPointerValid(aInterval);
3227
3228 AutoCaller autoCaller(this);
3229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3230
3231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3232
3233 *aInterval = mUserData->s.uFaultToleranceInterval;
3234 return S_OK;
3235}
3236
3237STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3238{
3239 AutoCaller autoCaller(this);
3240 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3241
3242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3243
3244 /* @todo deal with running state change. */
3245 HRESULT rc = checkStateDependency(MutableStateDep);
3246 if (FAILED(rc)) return rc;
3247
3248 setModified(IsModified_MachineData);
3249 mUserData.backup();
3250 mUserData->s.uFaultToleranceInterval = aInterval;
3251 return S_OK;
3252}
3253
3254STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3255{
3256 CheckComArgOutPointerValid(aEnabled);
3257
3258 AutoCaller autoCaller(this);
3259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3260
3261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3262
3263 *aEnabled = mUserData->s.fRTCUseUTC;
3264
3265 return S_OK;
3266}
3267
3268STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3269{
3270 AutoCaller autoCaller(this);
3271 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3272
3273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3274
3275 /* Only allow it to be set to true when PoweredOff or Aborted.
3276 (Clearing it is always permitted.) */
3277 if ( aEnabled
3278 && mData->mRegistered
3279 && ( !isSessionMachine()
3280 || ( mData->mMachineState != MachineState_PoweredOff
3281 && mData->mMachineState != MachineState_Teleported
3282 && mData->mMachineState != MachineState_Aborted
3283 )
3284 )
3285 )
3286 return setError(VBOX_E_INVALID_VM_STATE,
3287 tr("The machine is not powered off (state is %s)"),
3288 Global::stringifyMachineState(mData->mMachineState));
3289
3290 setModified(IsModified_MachineData);
3291 mUserData.backup();
3292 mUserData->s.fRTCUseUTC = !!aEnabled;
3293
3294 return S_OK;
3295}
3296
3297STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3298{
3299 CheckComArgOutPointerValid(aEnabled);
3300
3301 AutoCaller autoCaller(this);
3302 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3303
3304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3305
3306 *aEnabled = mHWData->mIOCacheEnabled;
3307
3308 return S_OK;
3309}
3310
3311STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3312{
3313 AutoCaller autoCaller(this);
3314 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3315
3316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3317
3318 HRESULT rc = checkStateDependency(MutableStateDep);
3319 if (FAILED(rc)) return rc;
3320
3321 setModified(IsModified_MachineData);
3322 mHWData.backup();
3323 mHWData->mIOCacheEnabled = aEnabled;
3324
3325 return S_OK;
3326}
3327
3328STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3329{
3330 CheckComArgOutPointerValid(aIOCacheSize);
3331
3332 AutoCaller autoCaller(this);
3333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3334
3335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3336
3337 *aIOCacheSize = mHWData->mIOCacheSize;
3338
3339 return S_OK;
3340}
3341
3342STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3343{
3344 AutoCaller autoCaller(this);
3345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3346
3347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3348
3349 HRESULT rc = checkStateDependency(MutableStateDep);
3350 if (FAILED(rc)) return rc;
3351
3352 setModified(IsModified_MachineData);
3353 mHWData.backup();
3354 mHWData->mIOCacheSize = aIOCacheSize;
3355
3356 return S_OK;
3357}
3358
3359
3360/**
3361 * @note Locks objects!
3362 */
3363STDMETHODIMP Machine::LockMachine(ISession *aSession,
3364 LockType_T lockType)
3365{
3366 CheckComArgNotNull(aSession);
3367
3368 AutoCaller autoCaller(this);
3369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3370
3371 /* check the session state */
3372 SessionState_T state;
3373 HRESULT rc = aSession->COMGETTER(State)(&state);
3374 if (FAILED(rc)) return rc;
3375
3376 if (state != SessionState_Unlocked)
3377 return setError(VBOX_E_INVALID_OBJECT_STATE,
3378 tr("The given session is busy"));
3379
3380 // get the client's IInternalSessionControl interface
3381 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3382 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3383 E_INVALIDARG);
3384
3385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3386
3387 if (!mData->mRegistered)
3388 return setError(E_UNEXPECTED,
3389 tr("The machine '%s' is not registered"),
3390 mUserData->s.strName.c_str());
3391
3392 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3393
3394 SessionState_T oldState = mData->mSession.mState;
3395 /* Hack: in case the session is closing and there is a progress object
3396 * which allows waiting for the session to be closed, take the opportunity
3397 * and do a limited wait (max. 1 second). This helps a lot when the system
3398 * is busy and thus session closing can take a little while. */
3399 if ( mData->mSession.mState == SessionState_Unlocking
3400 && mData->mSession.mProgress)
3401 {
3402 alock.release();
3403 mData->mSession.mProgress->WaitForCompletion(1000);
3404 alock.acquire();
3405 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3406 }
3407
3408 // try again now
3409 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3410 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3411 )
3412 {
3413 // OK, share the session... we are now dealing with three processes:
3414 // 1) VBoxSVC (where this code runs);
3415 // 2) process C: the caller's client process (who wants a shared session);
3416 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3417
3418 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3419 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3420 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3421 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3422 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3423
3424 /*
3425 * Release the lock before calling the client process. It's safe here
3426 * since the only thing to do after we get the lock again is to add
3427 * the remote control to the list (which doesn't directly influence
3428 * anything).
3429 */
3430 alock.release();
3431
3432 // get the console of the session holding the write lock (this is a remote call)
3433 ComPtr<IConsole> pConsoleW;
3434 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3435 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3436 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3437 if (FAILED(rc))
3438 // the failure may occur w/o any error info (from RPC), so provide one
3439 return setError(VBOX_E_VM_ERROR,
3440 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3441
3442 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3443
3444 // share the session machine and W's console with the caller's session
3445 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3446 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3447 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3448
3449 if (FAILED(rc))
3450 // the failure may occur w/o any error info (from RPC), so provide one
3451 return setError(VBOX_E_VM_ERROR,
3452 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3453 alock.acquire();
3454
3455 // need to revalidate the state after acquiring the lock again
3456 if (mData->mSession.mState != SessionState_Locked)
3457 {
3458 pSessionControl->Uninitialize();
3459 return setError(VBOX_E_INVALID_SESSION_STATE,
3460 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3461 mUserData->s.strName.c_str());
3462 }
3463
3464 // add the caller's session to the list
3465 mData->mSession.mRemoteControls.push_back(pSessionControl);
3466 }
3467 else if ( mData->mSession.mState == SessionState_Locked
3468 || mData->mSession.mState == SessionState_Unlocking
3469 )
3470 {
3471 // sharing not permitted, or machine still unlocking:
3472 return setError(VBOX_E_INVALID_OBJECT_STATE,
3473 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3474 mUserData->s.strName.c_str());
3475 }
3476 else
3477 {
3478 // machine is not locked: then write-lock the machine (create the session machine)
3479
3480 // must not be busy
3481 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3482
3483 // get the caller's session PID
3484 RTPROCESS pid = NIL_RTPROCESS;
3485 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3486 pSessionControl->GetPID((ULONG*)&pid);
3487 Assert(pid != NIL_RTPROCESS);
3488
3489 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3490
3491 if (fLaunchingVMProcess)
3492 {
3493 // this machine is awaiting for a spawning session to be opened:
3494 // then the calling process must be the one that got started by
3495 // LaunchVMProcess()
3496
3497 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3498 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3499
3500 if (mData->mSession.mPID != pid)
3501 return setError(E_ACCESSDENIED,
3502 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3503 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3504 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3505 }
3506
3507 // create the mutable SessionMachine from the current machine
3508 ComObjPtr<SessionMachine> sessionMachine;
3509 sessionMachine.createObject();
3510 rc = sessionMachine->init(this);
3511 AssertComRC(rc);
3512
3513 /* NOTE: doing return from this function after this point but
3514 * before the end is forbidden since it may call SessionMachine::uninit()
3515 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3516 * lock while still holding the Machine lock in alock so that a deadlock
3517 * is possible due to the wrong lock order. */
3518
3519 if (SUCCEEDED(rc))
3520 {
3521 /*
3522 * Set the session state to Spawning to protect against subsequent
3523 * attempts to open a session and to unregister the machine after
3524 * we release the lock.
3525 */
3526 SessionState_T origState = mData->mSession.mState;
3527 mData->mSession.mState = SessionState_Spawning;
3528
3529 /*
3530 * Release the lock before calling the client process -- it will call
3531 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3532 * because the state is Spawning, so that LaunchVMProcess() and
3533 * LockMachine() calls will fail. This method, called before we
3534 * acquire the lock again, will fail because of the wrong PID.
3535 *
3536 * Note that mData->mSession.mRemoteControls accessed outside
3537 * the lock may not be modified when state is Spawning, so it's safe.
3538 */
3539 alock.release();
3540
3541 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3542 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3543 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3544
3545 /* The failure may occur w/o any error info (from RPC), so provide one */
3546 if (FAILED(rc))
3547 setError(VBOX_E_VM_ERROR,
3548 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3549
3550 if ( SUCCEEDED(rc)
3551 && fLaunchingVMProcess
3552 )
3553 {
3554 /* complete the remote session initialization */
3555
3556 /* get the console from the direct session */
3557 ComPtr<IConsole> console;
3558 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3559 ComAssertComRC(rc);
3560
3561 if (SUCCEEDED(rc) && !console)
3562 {
3563 ComAssert(!!console);
3564 rc = E_FAIL;
3565 }
3566
3567 /* assign machine & console to the remote session */
3568 if (SUCCEEDED(rc))
3569 {
3570 /*
3571 * after LaunchVMProcess(), the first and the only
3572 * entry in remoteControls is that remote session
3573 */
3574 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3575 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3576 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3577
3578 /* The failure may occur w/o any error info (from RPC), so provide one */
3579 if (FAILED(rc))
3580 setError(VBOX_E_VM_ERROR,
3581 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3582 }
3583
3584 if (FAILED(rc))
3585 pSessionControl->Uninitialize();
3586 }
3587
3588 /* acquire the lock again */
3589 alock.acquire();
3590
3591 /* Restore the session state */
3592 mData->mSession.mState = origState;
3593 }
3594
3595 // finalize spawning anyway (this is why we don't return on errors above)
3596 if (fLaunchingVMProcess)
3597 {
3598 /* Note that the progress object is finalized later */
3599 /** @todo Consider checking mData->mSession.mProgress for cancellation
3600 * around here. */
3601
3602 /* We don't reset mSession.mPID here because it is necessary for
3603 * SessionMachine::uninit() to reap the child process later. */
3604
3605 if (FAILED(rc))
3606 {
3607 /* Close the remote session, remove the remote control from the list
3608 * and reset session state to Closed (@note keep the code in sync
3609 * with the relevant part in openSession()). */
3610
3611 Assert(mData->mSession.mRemoteControls.size() == 1);
3612 if (mData->mSession.mRemoteControls.size() == 1)
3613 {
3614 ErrorInfoKeeper eik;
3615 mData->mSession.mRemoteControls.front()->Uninitialize();
3616 }
3617
3618 mData->mSession.mRemoteControls.clear();
3619 mData->mSession.mState = SessionState_Unlocked;
3620 }
3621 }
3622 else
3623 {
3624 /* memorize PID of the directly opened session */
3625 if (SUCCEEDED(rc))
3626 mData->mSession.mPID = pid;
3627 }
3628
3629 if (SUCCEEDED(rc))
3630 {
3631 /* memorize the direct session control and cache IUnknown for it */
3632 mData->mSession.mDirectControl = pSessionControl;
3633 mData->mSession.mState = SessionState_Locked;
3634 /* associate the SessionMachine with this Machine */
3635 mData->mSession.mMachine = sessionMachine;
3636
3637 /* request an IUnknown pointer early from the remote party for later
3638 * identity checks (it will be internally cached within mDirectControl
3639 * at least on XPCOM) */
3640 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3641 NOREF(unk);
3642 }
3643
3644 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3645 * would break the lock order */
3646 alock.release();
3647
3648 /* uninitialize the created session machine on failure */
3649 if (FAILED(rc))
3650 sessionMachine->uninit();
3651
3652 }
3653
3654 if (SUCCEEDED(rc))
3655 {
3656 /*
3657 * tell the client watcher thread to update the set of
3658 * machines that have open sessions
3659 */
3660 mParent->updateClientWatcher();
3661
3662 if (oldState != SessionState_Locked)
3663 /* fire an event */
3664 mParent->onSessionStateChange(getId(), SessionState_Locked);
3665 }
3666
3667 return rc;
3668}
3669
3670/**
3671 * @note Locks objects!
3672 */
3673STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3674 IN_BSTR aFrontend,
3675 IN_BSTR aEnvironment,
3676 IProgress **aProgress)
3677{
3678 CheckComArgStr(aFrontend);
3679 Utf8Str strFrontend(aFrontend);
3680 Utf8Str strEnvironment(aEnvironment);
3681 /* "emergencystop" doesn't need the session, so skip the checks/interface
3682 * retrieval. This code doesn't quite fit in here, but introducing a
3683 * special API method would be even more effort, and would require explicit
3684 * support by every API client. It's better to hide the feature a bit. */
3685 if (strFrontend != "emergencystop")
3686 CheckComArgNotNull(aSession);
3687 CheckComArgOutPointerValid(aProgress);
3688
3689 AutoCaller autoCaller(this);
3690 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3691
3692 HRESULT rc = S_OK;
3693 if (strFrontend.isEmpty())
3694 {
3695 Bstr bstrFrontend;
3696 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3697 if (FAILED(rc))
3698 return rc;
3699 strFrontend = bstrFrontend;
3700 if (strFrontend.isEmpty())
3701 {
3702 ComPtr<ISystemProperties> systemProperties;
3703 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3704 if (FAILED(rc))
3705 return rc;
3706 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3707 if (FAILED(rc))
3708 return rc;
3709 strFrontend = bstrFrontend;
3710 }
3711 /* paranoia - emergencystop is not a valid default */
3712 if (strFrontend == "emergencystop")
3713 strFrontend = Utf8Str::Empty;
3714 }
3715
3716 if (strFrontend != "emergencystop")
3717 {
3718 /* check the session state */
3719 SessionState_T state;
3720 rc = aSession->COMGETTER(State)(&state);
3721 if (FAILED(rc))
3722 return rc;
3723
3724 if (state != SessionState_Unlocked)
3725 return setError(VBOX_E_INVALID_OBJECT_STATE,
3726 tr("The given session is busy"));
3727
3728 /* get the IInternalSessionControl interface */
3729 ComPtr<IInternalSessionControl> control(aSession);
3730 ComAssertMsgRet(!control.isNull(),
3731 ("No IInternalSessionControl interface"),
3732 E_INVALIDARG);
3733
3734 /* get the teleporter enable state for the progress object init. */
3735 BOOL fTeleporterEnabled;
3736 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3737 if (FAILED(rc))
3738 return rc;
3739
3740 /* create a progress object */
3741 ComObjPtr<ProgressProxy> progress;
3742 progress.createObject();
3743 rc = progress->init(mParent,
3744 static_cast<IMachine*>(this),
3745 Bstr(tr("Starting VM")).raw(),
3746 TRUE /* aCancelable */,
3747 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3748 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3749 2 /* uFirstOperationWeight */,
3750 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3751
3752 if (SUCCEEDED(rc))
3753 {
3754 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3755 if (SUCCEEDED(rc))
3756 {
3757 progress.queryInterfaceTo(aProgress);
3758
3759 /* signal the client watcher thread */
3760 mParent->updateClientWatcher();
3761
3762 /* fire an event */
3763 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3764 }
3765 }
3766 }
3767 else
3768 {
3769 /* no progress object - either instant success or failure */
3770 *aProgress = NULL;
3771
3772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3773
3774 if (mData->mSession.mState != SessionState_Locked)
3775 return setError(VBOX_E_INVALID_OBJECT_STATE,
3776 tr("The machine '%s' is not locked by a session"),
3777 mUserData->s.strName.c_str());
3778
3779 /* must have a VM process associated - do not kill normal API clients
3780 * with an open session */
3781 if (!Global::IsOnline(mData->mMachineState))
3782 return setError(VBOX_E_INVALID_OBJECT_STATE,
3783 tr("The machine '%s' does not have a VM process"),
3784 mUserData->s.strName.c_str());
3785
3786 /* forcibly terminate the VM process */
3787 if (mData->mSession.mPID != NIL_RTPROCESS)
3788 RTProcTerminate(mData->mSession.mPID);
3789
3790 /* signal the client watcher thread, as most likely the client has
3791 * been terminated */
3792 mParent->updateClientWatcher();
3793 }
3794
3795 return rc;
3796}
3797
3798STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3799{
3800 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3801 return setError(E_INVALIDARG,
3802 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3803 aPosition, SchemaDefs::MaxBootPosition);
3804
3805 if (aDevice == DeviceType_USB)
3806 return setError(E_NOTIMPL,
3807 tr("Booting from USB device is currently not supported"));
3808
3809 AutoCaller autoCaller(this);
3810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3811
3812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3813
3814 HRESULT rc = checkStateDependency(MutableStateDep);
3815 if (FAILED(rc)) return rc;
3816
3817 setModified(IsModified_MachineData);
3818 mHWData.backup();
3819 mHWData->mBootOrder[aPosition - 1] = aDevice;
3820
3821 return S_OK;
3822}
3823
3824STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3825{
3826 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3827 return setError(E_INVALIDARG,
3828 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3829 aPosition, SchemaDefs::MaxBootPosition);
3830
3831 AutoCaller autoCaller(this);
3832 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3833
3834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3835
3836 *aDevice = mHWData->mBootOrder[aPosition - 1];
3837
3838 return S_OK;
3839}
3840
3841STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3842 LONG aControllerPort,
3843 LONG aDevice,
3844 DeviceType_T aType,
3845 IMedium *aMedium)
3846{
3847 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3848 aControllerName, aControllerPort, aDevice, aType, aMedium));
3849
3850 CheckComArgStrNotEmptyOrNull(aControllerName);
3851
3852 AutoCaller autoCaller(this);
3853 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3854
3855 // request the host lock first, since might be calling Host methods for getting host drives;
3856 // next, protect the media tree all the while we're in here, as well as our member variables
3857 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3858 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3859
3860 HRESULT rc = checkStateDependency(MutableStateDep);
3861 if (FAILED(rc)) return rc;
3862
3863 /// @todo NEWMEDIA implicit machine registration
3864 if (!mData->mRegistered)
3865 return setError(VBOX_E_INVALID_OBJECT_STATE,
3866 tr("Cannot attach storage devices to an unregistered machine"));
3867
3868 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3869
3870 /* Check for an existing controller. */
3871 ComObjPtr<StorageController> ctl;
3872 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3873 if (FAILED(rc)) return rc;
3874
3875 StorageControllerType_T ctrlType;
3876 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3877 if (FAILED(rc))
3878 return setError(E_FAIL,
3879 tr("Could not get type of controller '%ls'"),
3880 aControllerName);
3881
3882 bool fSilent = false;
3883 Utf8Str strReconfig;
3884
3885 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3886 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3887 if (FAILED(rc))
3888 return rc;
3889 if ( mData->mMachineState == MachineState_Paused
3890 && strReconfig == "1")
3891 fSilent = true;
3892
3893 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3894 bool fHotplug = false;
3895 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3896 fHotplug = true;
3897
3898 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3899 return setError(VBOX_E_INVALID_VM_STATE,
3900 tr("Controller '%ls' does not support hotplugging"),
3901 aControllerName);
3902
3903 // check that the port and device are not out of range
3904 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3905 if (FAILED(rc)) return rc;
3906
3907 /* check if the device slot is already busy */
3908 MediumAttachment *pAttachTemp;
3909 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3910 aControllerName,
3911 aControllerPort,
3912 aDevice)))
3913 {
3914 Medium *pMedium = pAttachTemp->getMedium();
3915 if (pMedium)
3916 {
3917 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3918 return setError(VBOX_E_OBJECT_IN_USE,
3919 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3920 pMedium->getLocationFull().c_str(),
3921 aControllerPort,
3922 aDevice,
3923 aControllerName);
3924 }
3925 else
3926 return setError(VBOX_E_OBJECT_IN_USE,
3927 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3928 aControllerPort, aDevice, aControllerName);
3929 }
3930
3931 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3932 if (aMedium && medium.isNull())
3933 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3934
3935 AutoCaller mediumCaller(medium);
3936 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3937
3938 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3939
3940 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3941 && !medium.isNull()
3942 )
3943 return setError(VBOX_E_OBJECT_IN_USE,
3944 tr("Medium '%s' is already attached to this virtual machine"),
3945 medium->getLocationFull().c_str());
3946
3947 if (!medium.isNull())
3948 {
3949 MediumType_T mtype = medium->getType();
3950 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3951 // For DVDs it's not written to the config file, so needs no global config
3952 // version bump. For floppies it's a new attribute "type", which is ignored
3953 // by older VirtualBox version, so needs no global config version bump either.
3954 // For hard disks this type is not accepted.
3955 if (mtype == MediumType_MultiAttach)
3956 {
3957 // This type is new with VirtualBox 4.0 and therefore requires settings
3958 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3959 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3960 // two reasons: The medium type is a property of the media registry tree, which
3961 // can reside in the global config file (for pre-4.0 media); we would therefore
3962 // possibly need to bump the global config version. We don't want to do that though
3963 // because that might make downgrading to pre-4.0 impossible.
3964 // As a result, we can only use these two new types if the medium is NOT in the
3965 // global registry:
3966 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3967 if ( medium->isInRegistry(uuidGlobalRegistry)
3968 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3969 )
3970 return setError(VBOX_E_INVALID_OBJECT_STATE,
3971 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3972 "to machines that were created with VirtualBox 4.0 or later"),
3973 medium->getLocationFull().c_str());
3974 }
3975 }
3976
3977 bool fIndirect = false;
3978 if (!medium.isNull())
3979 fIndirect = medium->isReadOnly();
3980 bool associate = true;
3981
3982 do
3983 {
3984 if ( aType == DeviceType_HardDisk
3985 && mMediaData.isBackedUp())
3986 {
3987 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3988
3989 /* check if the medium was attached to the VM before we started
3990 * changing attachments in which case the attachment just needs to
3991 * be restored */
3992 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3993 {
3994 AssertReturn(!fIndirect, E_FAIL);
3995
3996 /* see if it's the same bus/channel/device */
3997 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3998 {
3999 /* the simplest case: restore the whole attachment
4000 * and return, nothing else to do */
4001 mMediaData->mAttachments.push_back(pAttachTemp);
4002 return S_OK;
4003 }
4004
4005 /* bus/channel/device differ; we need a new attachment object,
4006 * but don't try to associate it again */
4007 associate = false;
4008 break;
4009 }
4010 }
4011
4012 /* go further only if the attachment is to be indirect */
4013 if (!fIndirect)
4014 break;
4015
4016 /* perform the so called smart attachment logic for indirect
4017 * attachments. Note that smart attachment is only applicable to base
4018 * hard disks. */
4019
4020 if (medium->getParent().isNull())
4021 {
4022 /* first, investigate the backup copy of the current hard disk
4023 * attachments to make it possible to re-attach existing diffs to
4024 * another device slot w/o losing their contents */
4025 if (mMediaData.isBackedUp())
4026 {
4027 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4028
4029 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4030 uint32_t foundLevel = 0;
4031
4032 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4033 it != oldAtts.end();
4034 ++it)
4035 {
4036 uint32_t level = 0;
4037 MediumAttachment *pAttach = *it;
4038 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4039 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4040 if (pMedium.isNull())
4041 continue;
4042
4043 if (pMedium->getBase(&level) == medium)
4044 {
4045 /* skip the hard disk if its currently attached (we
4046 * cannot attach the same hard disk twice) */
4047 if (findAttachment(mMediaData->mAttachments,
4048 pMedium))
4049 continue;
4050
4051 /* matched device, channel and bus (i.e. attached to the
4052 * same place) will win and immediately stop the search;
4053 * otherwise the attachment that has the youngest
4054 * descendant of medium will be used
4055 */
4056 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4057 {
4058 /* the simplest case: restore the whole attachment
4059 * and return, nothing else to do */
4060 mMediaData->mAttachments.push_back(*it);
4061 return S_OK;
4062 }
4063 else if ( foundIt == oldAtts.end()
4064 || level > foundLevel /* prefer younger */
4065 )
4066 {
4067 foundIt = it;
4068 foundLevel = level;
4069 }
4070 }
4071 }
4072
4073 if (foundIt != oldAtts.end())
4074 {
4075 /* use the previously attached hard disk */
4076 medium = (*foundIt)->getMedium();
4077 mediumCaller.attach(medium);
4078 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4079 mediumLock.attach(medium);
4080 /* not implicit, doesn't require association with this VM */
4081 fIndirect = false;
4082 associate = false;
4083 /* go right to the MediumAttachment creation */
4084 break;
4085 }
4086 }
4087
4088 /* must give up the medium lock and medium tree lock as below we
4089 * go over snapshots, which needs a lock with higher lock order. */
4090 mediumLock.release();
4091 treeLock.release();
4092
4093 /* then, search through snapshots for the best diff in the given
4094 * hard disk's chain to base the new diff on */
4095
4096 ComObjPtr<Medium> base;
4097 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4098 while (snap)
4099 {
4100 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4101
4102 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4103
4104 MediumAttachment *pAttachFound = NULL;
4105 uint32_t foundLevel = 0;
4106
4107 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4108 it != snapAtts.end();
4109 ++it)
4110 {
4111 MediumAttachment *pAttach = *it;
4112 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4113 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4114 if (pMedium.isNull())
4115 continue;
4116
4117 uint32_t level = 0;
4118 if (pMedium->getBase(&level) == medium)
4119 {
4120 /* matched device, channel and bus (i.e. attached to the
4121 * same place) will win and immediately stop the search;
4122 * otherwise the attachment that has the youngest
4123 * descendant of medium will be used
4124 */
4125 if ( pAttach->getDevice() == aDevice
4126 && pAttach->getPort() == aControllerPort
4127 && pAttach->getControllerName() == aControllerName
4128 )
4129 {
4130 pAttachFound = pAttach;
4131 break;
4132 }
4133 else if ( !pAttachFound
4134 || level > foundLevel /* prefer younger */
4135 )
4136 {
4137 pAttachFound = pAttach;
4138 foundLevel = level;
4139 }
4140 }
4141 }
4142
4143 if (pAttachFound)
4144 {
4145 base = pAttachFound->getMedium();
4146 break;
4147 }
4148
4149 snap = snap->getParent();
4150 }
4151
4152 /* re-lock medium tree and the medium, as we need it below */
4153 treeLock.acquire();
4154 mediumLock.acquire();
4155
4156 /* found a suitable diff, use it as a base */
4157 if (!base.isNull())
4158 {
4159 medium = base;
4160 mediumCaller.attach(medium);
4161 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4162 mediumLock.attach(medium);
4163 }
4164 }
4165
4166 Utf8Str strFullSnapshotFolder;
4167 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4168
4169 ComObjPtr<Medium> diff;
4170 diff.createObject();
4171 // store this diff in the same registry as the parent
4172 Guid uuidRegistryParent;
4173 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4174 {
4175 // parent image has no registry: this can happen if we're attaching a new immutable
4176 // image that has not yet been attached (medium then points to the base and we're
4177 // creating the diff image for the immutable, and the parent is not yet registered);
4178 // put the parent in the machine registry then
4179 mediumLock.release();
4180 treeLock.release();
4181 alock.release();
4182 addMediumToRegistry(medium);
4183 alock.acquire();
4184 treeLock.acquire();
4185 mediumLock.acquire();
4186 medium->getFirstRegistryMachineId(uuidRegistryParent);
4187 }
4188 rc = diff->init(mParent,
4189 medium->getPreferredDiffFormat(),
4190 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4191 uuidRegistryParent);
4192 if (FAILED(rc)) return rc;
4193
4194 /* Apply the normal locking logic to the entire chain. */
4195 MediumLockList *pMediumLockList(new MediumLockList());
4196 mediumLock.release();
4197 treeLock.release();
4198 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4199 true /* fMediumLockWrite */,
4200 medium,
4201 *pMediumLockList);
4202 treeLock.acquire();
4203 mediumLock.acquire();
4204 if (SUCCEEDED(rc))
4205 {
4206 mediumLock.release();
4207 treeLock.release();
4208 rc = pMediumLockList->Lock();
4209 treeLock.acquire();
4210 mediumLock.acquire();
4211 if (FAILED(rc))
4212 setError(rc,
4213 tr("Could not lock medium when creating diff '%s'"),
4214 diff->getLocationFull().c_str());
4215 else
4216 {
4217 /* will release the lock before the potentially lengthy
4218 * operation, so protect with the special state */
4219 MachineState_T oldState = mData->mMachineState;
4220 setMachineState(MachineState_SettingUp);
4221
4222 mediumLock.release();
4223 treeLock.release();
4224 alock.release();
4225
4226 rc = medium->createDiffStorage(diff,
4227 MediumVariant_Standard,
4228 pMediumLockList,
4229 NULL /* aProgress */,
4230 true /* aWait */);
4231
4232 alock.acquire();
4233 treeLock.acquire();
4234 mediumLock.acquire();
4235
4236 setMachineState(oldState);
4237 }
4238 }
4239
4240 /* Unlock the media and free the associated memory. */
4241 delete pMediumLockList;
4242
4243 if (FAILED(rc)) return rc;
4244
4245 /* use the created diff for the actual attachment */
4246 medium = diff;
4247 mediumCaller.attach(medium);
4248 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4249 mediumLock.attach(medium);
4250 }
4251 while (0);
4252
4253 ComObjPtr<MediumAttachment> attachment;
4254 attachment.createObject();
4255 rc = attachment->init(this,
4256 medium,
4257 aControllerName,
4258 aControllerPort,
4259 aDevice,
4260 aType,
4261 fIndirect,
4262 false /* fPassthrough */,
4263 false /* fTempEject */,
4264 false /* fNonRotational */,
4265 false /* fDiscard */,
4266 Utf8Str::Empty);
4267 if (FAILED(rc)) return rc;
4268
4269 if (associate && !medium.isNull())
4270 {
4271 // as the last step, associate the medium to the VM
4272 rc = medium->addBackReference(mData->mUuid);
4273 // here we can fail because of Deleting, or being in process of creating a Diff
4274 if (FAILED(rc)) return rc;
4275
4276 mediumLock.release();
4277 treeLock.release();
4278 alock.release();
4279 addMediumToRegistry(medium);
4280 alock.acquire();
4281 treeLock.acquire();
4282 mediumLock.acquire();
4283 }
4284
4285 /* success: finally remember the attachment */
4286 setModified(IsModified_Storage);
4287 mMediaData.backup();
4288 mMediaData->mAttachments.push_back(attachment);
4289
4290 mediumLock.release();
4291 treeLock.release();
4292 alock.release();
4293
4294 if (fHotplug || fSilent)
4295 {
4296 MediumLockList *pMediumLockList(new MediumLockList());
4297
4298 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4299 true /* fMediumLockWrite */,
4300 NULL,
4301 *pMediumLockList);
4302 alock.acquire();
4303 if (FAILED(rc))
4304 delete pMediumLockList;
4305 else
4306 {
4307 mData->mSession.mLockedMedia.Unlock();
4308 alock.release();
4309 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4310 mData->mSession.mLockedMedia.Lock();
4311 alock.acquire();
4312 }
4313 alock.release();
4314
4315 if (SUCCEEDED(rc))
4316 {
4317 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4318 /* Remove lock list in case of error. */
4319 if (FAILED(rc))
4320 {
4321 mData->mSession.mLockedMedia.Unlock();
4322 mData->mSession.mLockedMedia.Remove(attachment);
4323 mData->mSession.mLockedMedia.Lock();
4324 }
4325 }
4326 }
4327
4328 mParent->saveModifiedRegistries();
4329
4330 return rc;
4331}
4332
4333STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4334 LONG aDevice)
4335{
4336 CheckComArgStrNotEmptyOrNull(aControllerName);
4337
4338 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4339 aControllerName, aControllerPort, aDevice));
4340
4341 AutoCaller autoCaller(this);
4342 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4343
4344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4345
4346 HRESULT rc = checkStateDependency(MutableStateDep);
4347 if (FAILED(rc)) return rc;
4348
4349 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4350
4351 /* Check for an existing controller. */
4352 ComObjPtr<StorageController> ctl;
4353 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4354 if (FAILED(rc)) return rc;
4355
4356 StorageControllerType_T ctrlType;
4357 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4358 if (FAILED(rc))
4359 return setError(E_FAIL,
4360 tr("Could not get type of controller '%ls'"),
4361 aControllerName);
4362
4363 bool fSilent = false;
4364 Utf8Str strReconfig;
4365
4366 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4367 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4368 if (FAILED(rc))
4369 return rc;
4370 if ( mData->mMachineState == MachineState_Paused
4371 && strReconfig == "1")
4372 fSilent = true;
4373
4374 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4375 bool fHotplug = false;
4376 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4377 fHotplug = true;
4378
4379 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4380 return setError(VBOX_E_INVALID_VM_STATE,
4381 tr("Controller '%ls' does not support hotplugging"),
4382 aControllerName);
4383
4384 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4385 aControllerName,
4386 aControllerPort,
4387 aDevice);
4388 if (!pAttach)
4389 return setError(VBOX_E_OBJECT_NOT_FOUND,
4390 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4391 aDevice, aControllerPort, aControllerName);
4392
4393 /*
4394 * The VM has to detach the device before we delete any implicit diffs.
4395 * If this fails we can roll back without loosing data.
4396 */
4397 if (fHotplug || fSilent)
4398 {
4399 alock.release();
4400 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4401 alock.acquire();
4402 }
4403 if (FAILED(rc)) return rc;
4404
4405 /* If we are here everything went well and we can delete the implicit now. */
4406 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4407
4408 alock.release();
4409
4410 mParent->saveModifiedRegistries();
4411
4412 return rc;
4413}
4414
4415STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4416 LONG aDevice, BOOL aPassthrough)
4417{
4418 CheckComArgStrNotEmptyOrNull(aControllerName);
4419
4420 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4421 aControllerName, aControllerPort, aDevice, aPassthrough));
4422
4423 AutoCaller autoCaller(this);
4424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4425
4426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4427
4428 HRESULT rc = checkStateDependency(MutableStateDep);
4429 if (FAILED(rc)) return rc;
4430
4431 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4432
4433 if (Global::IsOnlineOrTransient(mData->mMachineState))
4434 return setError(VBOX_E_INVALID_VM_STATE,
4435 tr("Invalid machine state: %s"),
4436 Global::stringifyMachineState(mData->mMachineState));
4437
4438 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4439 aControllerName,
4440 aControllerPort,
4441 aDevice);
4442 if (!pAttach)
4443 return setError(VBOX_E_OBJECT_NOT_FOUND,
4444 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4445 aDevice, aControllerPort, aControllerName);
4446
4447
4448 setModified(IsModified_Storage);
4449 mMediaData.backup();
4450
4451 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4452
4453 if (pAttach->getType() != DeviceType_DVD)
4454 return setError(E_INVALIDARG,
4455 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4456 aDevice, aControllerPort, aControllerName);
4457 pAttach->updatePassthrough(!!aPassthrough);
4458
4459 return S_OK;
4460}
4461
4462STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4463 LONG aDevice, BOOL aTemporaryEject)
4464{
4465 CheckComArgStrNotEmptyOrNull(aControllerName);
4466
4467 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4468 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4469
4470 AutoCaller autoCaller(this);
4471 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4472
4473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4474
4475 HRESULT rc = checkStateDependency(MutableStateDep);
4476 if (FAILED(rc)) return rc;
4477
4478 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4479 aControllerName,
4480 aControllerPort,
4481 aDevice);
4482 if (!pAttach)
4483 return setError(VBOX_E_OBJECT_NOT_FOUND,
4484 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4485 aDevice, aControllerPort, aControllerName);
4486
4487
4488 setModified(IsModified_Storage);
4489 mMediaData.backup();
4490
4491 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4492
4493 if (pAttach->getType() != DeviceType_DVD)
4494 return setError(E_INVALIDARG,
4495 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4496 aDevice, aControllerPort, aControllerName);
4497 pAttach->updateTempEject(!!aTemporaryEject);
4498
4499 return S_OK;
4500}
4501
4502STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4503 LONG aDevice, BOOL aNonRotational)
4504{
4505 CheckComArgStrNotEmptyOrNull(aControllerName);
4506
4507 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4508 aControllerName, aControllerPort, aDevice, aNonRotational));
4509
4510 AutoCaller autoCaller(this);
4511 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4512
4513 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4514
4515 HRESULT rc = checkStateDependency(MutableStateDep);
4516 if (FAILED(rc)) return rc;
4517
4518 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4519
4520 if (Global::IsOnlineOrTransient(mData->mMachineState))
4521 return setError(VBOX_E_INVALID_VM_STATE,
4522 tr("Invalid machine state: %s"),
4523 Global::stringifyMachineState(mData->mMachineState));
4524
4525 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4526 aControllerName,
4527 aControllerPort,
4528 aDevice);
4529 if (!pAttach)
4530 return setError(VBOX_E_OBJECT_NOT_FOUND,
4531 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4532 aDevice, aControllerPort, aControllerName);
4533
4534
4535 setModified(IsModified_Storage);
4536 mMediaData.backup();
4537
4538 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4539
4540 if (pAttach->getType() != DeviceType_HardDisk)
4541 return setError(E_INVALIDARG,
4542 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"),
4543 aDevice, aControllerPort, aControllerName);
4544 pAttach->updateNonRotational(!!aNonRotational);
4545
4546 return S_OK;
4547}
4548
4549STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4550 LONG aDevice, BOOL aDiscard)
4551{
4552 CheckComArgStrNotEmptyOrNull(aControllerName);
4553
4554 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4555 aControllerName, aControllerPort, aDevice, aDiscard));
4556
4557 AutoCaller autoCaller(this);
4558 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4559
4560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4561
4562 HRESULT rc = checkStateDependency(MutableStateDep);
4563 if (FAILED(rc)) return rc;
4564
4565 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4566
4567 if (Global::IsOnlineOrTransient(mData->mMachineState))
4568 return setError(VBOX_E_INVALID_VM_STATE,
4569 tr("Invalid machine state: %s"),
4570 Global::stringifyMachineState(mData->mMachineState));
4571
4572 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4573 aControllerName,
4574 aControllerPort,
4575 aDevice);
4576 if (!pAttach)
4577 return setError(VBOX_E_OBJECT_NOT_FOUND,
4578 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4579 aDevice, aControllerPort, aControllerName);
4580
4581
4582 setModified(IsModified_Storage);
4583 mMediaData.backup();
4584
4585 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4586
4587 if (pAttach->getType() != DeviceType_HardDisk)
4588 return setError(E_INVALIDARG,
4589 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"),
4590 aDevice, aControllerPort, aControllerName);
4591 pAttach->updateDiscard(!!aDiscard);
4592
4593 return S_OK;
4594}
4595
4596STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4597 LONG aDevice)
4598{
4599 int rc = S_OK;
4600 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4601 aControllerName, aControllerPort, aDevice));
4602
4603 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4604
4605 return rc;
4606}
4607
4608STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4609 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4610{
4611 CheckComArgStrNotEmptyOrNull(aControllerName);
4612
4613 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4614 aControllerName, aControllerPort, aDevice));
4615
4616 AutoCaller autoCaller(this);
4617 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4618
4619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4620
4621 HRESULT rc = checkStateDependency(MutableStateDep);
4622 if (FAILED(rc)) return rc;
4623
4624 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4625
4626 if (Global::IsOnlineOrTransient(mData->mMachineState))
4627 return setError(VBOX_E_INVALID_VM_STATE,
4628 tr("Invalid machine state: %s"),
4629 Global::stringifyMachineState(mData->mMachineState));
4630
4631 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4632 aControllerName,
4633 aControllerPort,
4634 aDevice);
4635 if (!pAttach)
4636 return setError(VBOX_E_OBJECT_NOT_FOUND,
4637 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4638 aDevice, aControllerPort, aControllerName);
4639
4640
4641 setModified(IsModified_Storage);
4642 mMediaData.backup();
4643
4644 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4645 if (aBandwidthGroup && group.isNull())
4646 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4647
4648 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4649
4650 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4651 if (strBandwidthGroupOld.isNotEmpty())
4652 {
4653 /* Get the bandwidth group object and release it - this must not fail. */
4654 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4655 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4656 Assert(SUCCEEDED(rc));
4657
4658 pBandwidthGroupOld->release();
4659 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4660 }
4661
4662 if (!group.isNull())
4663 {
4664 group->reference();
4665 pAttach->updateBandwidthGroup(group->getName());
4666 }
4667
4668 return S_OK;
4669}
4670
4671STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4672 LONG aControllerPort,
4673 LONG aDevice,
4674 DeviceType_T aType)
4675{
4676 HRESULT rc = S_OK;
4677
4678 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4679 aControllerName, aControllerPort, aDevice, aType));
4680
4681 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4682
4683 return rc;
4684}
4685
4686
4687
4688STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4689 LONG aControllerPort,
4690 LONG aDevice,
4691 BOOL aForce)
4692{
4693 int rc = S_OK;
4694 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4695 aControllerName, aControllerPort, aForce));
4696
4697 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4698
4699 return rc;
4700}
4701
4702STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4703 LONG aControllerPort,
4704 LONG aDevice,
4705 IMedium *aMedium,
4706 BOOL aForce)
4707{
4708 int rc = S_OK;
4709 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4710 aControllerName, aControllerPort, aDevice, aForce));
4711
4712 CheckComArgStrNotEmptyOrNull(aControllerName);
4713
4714 AutoCaller autoCaller(this);
4715 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4716
4717 // request the host lock first, since might be calling Host methods for getting host drives;
4718 // next, protect the media tree all the while we're in here, as well as our member variables
4719 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4720 this->lockHandle(),
4721 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4722
4723 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4724 aControllerName,
4725 aControllerPort,
4726 aDevice);
4727 if (pAttach.isNull())
4728 return setError(VBOX_E_OBJECT_NOT_FOUND,
4729 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4730 aDevice, aControllerPort, aControllerName);
4731
4732 /* Remember previously mounted medium. The medium before taking the
4733 * backup is not necessarily the same thing. */
4734 ComObjPtr<Medium> oldmedium;
4735 oldmedium = pAttach->getMedium();
4736
4737 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4738 if (aMedium && pMedium.isNull())
4739 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4740
4741 AutoCaller mediumCaller(pMedium);
4742 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4743
4744 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4745 if (pMedium)
4746 {
4747 DeviceType_T mediumType = pAttach->getType();
4748 switch (mediumType)
4749 {
4750 case DeviceType_DVD:
4751 case DeviceType_Floppy:
4752 break;
4753
4754 default:
4755 return setError(VBOX_E_INVALID_OBJECT_STATE,
4756 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4757 aControllerPort,
4758 aDevice,
4759 aControllerName);
4760 }
4761 }
4762
4763 setModified(IsModified_Storage);
4764 mMediaData.backup();
4765
4766 {
4767 // The backup operation makes the pAttach reference point to the
4768 // old settings. Re-get the correct reference.
4769 pAttach = findAttachment(mMediaData->mAttachments,
4770 aControllerName,
4771 aControllerPort,
4772 aDevice);
4773 if (!oldmedium.isNull())
4774 oldmedium->removeBackReference(mData->mUuid);
4775 if (!pMedium.isNull())
4776 {
4777 pMedium->addBackReference(mData->mUuid);
4778
4779 mediumLock.release();
4780 multiLock.release();
4781 addMediumToRegistry(pMedium);
4782 multiLock.acquire();
4783 mediumLock.acquire();
4784 }
4785
4786 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4787 pAttach->updateMedium(pMedium);
4788 }
4789
4790 setModified(IsModified_Storage);
4791
4792 mediumLock.release();
4793 multiLock.release();
4794 rc = onMediumChange(pAttach, aForce);
4795 multiLock.acquire();
4796 mediumLock.acquire();
4797
4798 /* On error roll back this change only. */
4799 if (FAILED(rc))
4800 {
4801 if (!pMedium.isNull())
4802 pMedium->removeBackReference(mData->mUuid);
4803 pAttach = findAttachment(mMediaData->mAttachments,
4804 aControllerName,
4805 aControllerPort,
4806 aDevice);
4807 /* If the attachment is gone in the meantime, bail out. */
4808 if (pAttach.isNull())
4809 return rc;
4810 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4811 if (!oldmedium.isNull())
4812 oldmedium->addBackReference(mData->mUuid);
4813 pAttach->updateMedium(oldmedium);
4814 }
4815
4816 mediumLock.release();
4817 multiLock.release();
4818
4819 mParent->saveModifiedRegistries();
4820
4821 return rc;
4822}
4823
4824STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4825 LONG aControllerPort,
4826 LONG aDevice,
4827 IMedium **aMedium)
4828{
4829 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4830 aControllerName, aControllerPort, aDevice));
4831
4832 CheckComArgStrNotEmptyOrNull(aControllerName);
4833 CheckComArgOutPointerValid(aMedium);
4834
4835 AutoCaller autoCaller(this);
4836 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4837
4838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4839
4840 *aMedium = NULL;
4841
4842 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4843 aControllerName,
4844 aControllerPort,
4845 aDevice);
4846 if (pAttach.isNull())
4847 return setError(VBOX_E_OBJECT_NOT_FOUND,
4848 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4849 aDevice, aControllerPort, aControllerName);
4850
4851 pAttach->getMedium().queryInterfaceTo(aMedium);
4852
4853 return S_OK;
4854}
4855
4856STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4857{
4858 CheckComArgOutPointerValid(port);
4859 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4860
4861 AutoCaller autoCaller(this);
4862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4863
4864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4865
4866 mSerialPorts[slot].queryInterfaceTo(port);
4867
4868 return S_OK;
4869}
4870
4871STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4872{
4873 CheckComArgOutPointerValid(port);
4874 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4875
4876 AutoCaller autoCaller(this);
4877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4878
4879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4880
4881 mParallelPorts[slot].queryInterfaceTo(port);
4882
4883 return S_OK;
4884}
4885
4886STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4887{
4888 CheckComArgOutPointerValid(adapter);
4889 /* Do not assert if slot is out of range, just return the advertised
4890 status. testdriver/vbox.py triggers this in logVmInfo. */
4891 if (slot >= mNetworkAdapters.size())
4892 return setError(E_INVALIDARG,
4893 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4894 slot, mNetworkAdapters.size());
4895
4896 AutoCaller autoCaller(this);
4897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4898
4899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4900
4901 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4902
4903 return S_OK;
4904}
4905
4906STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4907{
4908 CheckComArgOutSafeArrayPointerValid(aKeys);
4909
4910 AutoCaller autoCaller(this);
4911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4912
4913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4914
4915 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4916 int i = 0;
4917 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4918 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4919 ++it, ++i)
4920 {
4921 const Utf8Str &strKey = it->first;
4922 strKey.cloneTo(&saKeys[i]);
4923 }
4924 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4925
4926 return S_OK;
4927 }
4928
4929 /**
4930 * @note Locks this object for reading.
4931 */
4932STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4933 BSTR *aValue)
4934{
4935 CheckComArgStrNotEmptyOrNull(aKey);
4936 CheckComArgOutPointerValid(aValue);
4937
4938 AutoCaller autoCaller(this);
4939 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4940
4941 /* start with nothing found */
4942 Bstr bstrResult("");
4943
4944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4945
4946 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4947 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4948 // found:
4949 bstrResult = it->second; // source is a Utf8Str
4950
4951 /* return the result to caller (may be empty) */
4952 bstrResult.cloneTo(aValue);
4953
4954 return S_OK;
4955}
4956
4957 /**
4958 * @note Locks mParent for writing + this object for writing.
4959 */
4960STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4961{
4962 CheckComArgStrNotEmptyOrNull(aKey);
4963
4964 AutoCaller autoCaller(this);
4965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4966
4967 Utf8Str strKey(aKey);
4968 Utf8Str strValue(aValue);
4969 Utf8Str strOldValue; // empty
4970
4971 // locking note: we only hold the read lock briefly to look up the old value,
4972 // then release it and call the onExtraCanChange callbacks. There is a small
4973 // chance of a race insofar as the callback might be called twice if two callers
4974 // change the same key at the same time, but that's a much better solution
4975 // than the deadlock we had here before. The actual changing of the extradata
4976 // is then performed under the write lock and race-free.
4977
4978 // look up the old value first; if nothing has changed then we need not do anything
4979 {
4980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4981 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4982 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4983 strOldValue = it->second;
4984 }
4985
4986 bool fChanged;
4987 if ((fChanged = (strOldValue != strValue)))
4988 {
4989 // ask for permission from all listeners outside the locks;
4990 // onExtraDataCanChange() only briefly requests the VirtualBox
4991 // lock to copy the list of callbacks to invoke
4992 Bstr error;
4993 Bstr bstrValue(aValue);
4994
4995 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4996 {
4997 const char *sep = error.isEmpty() ? "" : ": ";
4998 CBSTR err = error.raw();
4999 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5000 sep, err));
5001 return setError(E_ACCESSDENIED,
5002 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5003 aKey,
5004 bstrValue.raw(),
5005 sep,
5006 err);
5007 }
5008
5009 // data is changing and change not vetoed: then write it out under the lock
5010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5011
5012 if (isSnapshotMachine())
5013 {
5014 HRESULT rc = checkStateDependency(MutableStateDep);
5015 if (FAILED(rc)) return rc;
5016 }
5017
5018 if (strValue.isEmpty())
5019 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5020 else
5021 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5022 // creates a new key if needed
5023
5024 bool fNeedsGlobalSaveSettings = false;
5025 saveSettings(&fNeedsGlobalSaveSettings);
5026
5027 if (fNeedsGlobalSaveSettings)
5028 {
5029 // save the global settings; for that we should hold only the VirtualBox lock
5030 alock.release();
5031 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5032 mParent->saveSettings();
5033 }
5034 }
5035
5036 // fire notification outside the lock
5037 if (fChanged)
5038 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5039
5040 return S_OK;
5041}
5042
5043STDMETHODIMP Machine::SaveSettings()
5044{
5045 AutoCaller autoCaller(this);
5046 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5047
5048 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5049
5050 /* when there was auto-conversion, we want to save the file even if
5051 * the VM is saved */
5052 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5053 if (FAILED(rc)) return rc;
5054
5055 /* the settings file path may never be null */
5056 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5057
5058 /* save all VM data excluding snapshots */
5059 bool fNeedsGlobalSaveSettings = false;
5060 rc = saveSettings(&fNeedsGlobalSaveSettings);
5061 mlock.release();
5062
5063 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5064 {
5065 // save the global settings; for that we should hold only the VirtualBox lock
5066 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5067 rc = mParent->saveSettings();
5068 }
5069
5070 return rc;
5071}
5072
5073STDMETHODIMP Machine::DiscardSettings()
5074{
5075 AutoCaller autoCaller(this);
5076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5077
5078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5079
5080 HRESULT rc = checkStateDependency(MutableStateDep);
5081 if (FAILED(rc)) return rc;
5082
5083 /*
5084 * during this rollback, the session will be notified if data has
5085 * been actually changed
5086 */
5087 rollback(true /* aNotify */);
5088
5089 return S_OK;
5090}
5091
5092/** @note Locks objects! */
5093STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5094 ComSafeArrayOut(IMedium*, aMedia))
5095{
5096 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5097 AutoLimitedCaller autoCaller(this);
5098 AssertComRCReturnRC(autoCaller.rc());
5099
5100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5101
5102 Guid id(getId());
5103
5104 if (mData->mSession.mState != SessionState_Unlocked)
5105 return setError(VBOX_E_INVALID_OBJECT_STATE,
5106 tr("Cannot unregister the machine '%s' while it is locked"),
5107 mUserData->s.strName.c_str());
5108
5109 // wait for state dependents to drop to zero
5110 ensureNoStateDependencies();
5111
5112 if (!mData->mAccessible)
5113 {
5114 // inaccessible maschines can only be unregistered; uninitialize ourselves
5115 // here because currently there may be no unregistered that are inaccessible
5116 // (this state combination is not supported). Note releasing the caller and
5117 // leaving the lock before calling uninit()
5118 alock.release();
5119 autoCaller.release();
5120
5121 uninit();
5122
5123 mParent->unregisterMachine(this, id);
5124 // calls VirtualBox::saveSettings()
5125
5126 return S_OK;
5127 }
5128
5129 HRESULT rc = S_OK;
5130
5131 // discard saved state
5132 if (mData->mMachineState == MachineState_Saved)
5133 {
5134 // add the saved state file to the list of files the caller should delete
5135 Assert(!mSSData->strStateFilePath.isEmpty());
5136 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5137
5138 mSSData->strStateFilePath.setNull();
5139
5140 // unconditionally set the machine state to powered off, we now
5141 // know no session has locked the machine
5142 mData->mMachineState = MachineState_PoweredOff;
5143 }
5144
5145 size_t cSnapshots = 0;
5146 if (mData->mFirstSnapshot)
5147 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5148 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5149 // fail now before we start detaching media
5150 return setError(VBOX_E_INVALID_OBJECT_STATE,
5151 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5152 mUserData->s.strName.c_str(), cSnapshots);
5153
5154 // This list collects the medium objects from all medium attachments
5155 // which we will detach from the machine and its snapshots, in a specific
5156 // order which allows for closing all media without getting "media in use"
5157 // errors, simply by going through the list from the front to the back:
5158 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5159 // and must be closed before the parent media from the snapshots, or closing the parents
5160 // will fail because they still have children);
5161 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5162 // the root ("first") snapshot of the machine.
5163 MediaList llMedia;
5164
5165 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5166 && mMediaData->mAttachments.size()
5167 )
5168 {
5169 // we have media attachments: detach them all and add the Medium objects to our list
5170 if (cleanupMode != CleanupMode_UnregisterOnly)
5171 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5172 else
5173 return setError(VBOX_E_INVALID_OBJECT_STATE,
5174 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5175 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5176 }
5177
5178 if (cSnapshots)
5179 {
5180 // autoCleanup must be true here, or we would have failed above
5181
5182 // add the media from the medium attachments of the snapshots to llMedia
5183 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5184 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5185 // into the children first
5186
5187 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5188 MachineState_T oldState = mData->mMachineState;
5189 mData->mMachineState = MachineState_DeletingSnapshot;
5190
5191 // make a copy of the first snapshot so the refcount does not drop to 0
5192 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5193 // because of the AutoCaller voodoo)
5194 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5195
5196 // GO!
5197 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5198
5199 mData->mMachineState = oldState;
5200 }
5201
5202 if (FAILED(rc))
5203 {
5204 rollbackMedia();
5205 return rc;
5206 }
5207
5208 // commit all the media changes made above
5209 commitMedia();
5210
5211 mData->mRegistered = false;
5212
5213 // machine lock no longer needed
5214 alock.release();
5215
5216 // return media to caller
5217 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5218 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5219
5220 mParent->unregisterMachine(this, id);
5221 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5222
5223 return S_OK;
5224}
5225
5226struct Machine::DeleteTask
5227{
5228 ComObjPtr<Machine> pMachine;
5229 RTCList<ComPtr<IMedium> > llMediums;
5230 StringsList llFilesToDelete;
5231 ComObjPtr<Progress> pProgress;
5232};
5233
5234STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5235{
5236 LogFlowFuncEnter();
5237
5238 AutoCaller autoCaller(this);
5239 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5240
5241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5242
5243 HRESULT rc = checkStateDependency(MutableStateDep);
5244 if (FAILED(rc)) return rc;
5245
5246 if (mData->mRegistered)
5247 return setError(VBOX_E_INVALID_VM_STATE,
5248 tr("Cannot delete settings of a registered machine"));
5249
5250 DeleteTask *pTask = new DeleteTask;
5251 pTask->pMachine = this;
5252 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5253
5254 // collect files to delete
5255 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5256
5257 for (size_t i = 0; i < sfaMedia.size(); ++i)
5258 {
5259 IMedium *pIMedium(sfaMedia[i]);
5260 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5261 if (pMedium.isNull())
5262 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5263 SafeArray<BSTR> ids;
5264 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5265 if (FAILED(rc)) return rc;
5266 /* At this point the medium should not have any back references
5267 * anymore. If it has it is attached to another VM and *must* not
5268 * deleted. */
5269 if (ids.size() < 1)
5270 pTask->llMediums.append(pMedium);
5271 }
5272 if (mData->pMachineConfigFile->fileExists())
5273 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5274
5275 pTask->pProgress.createObject();
5276 pTask->pProgress->init(getVirtualBox(),
5277 static_cast<IMachine*>(this) /* aInitiator */,
5278 Bstr(tr("Deleting files")).raw(),
5279 true /* fCancellable */,
5280 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5281 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5282
5283 int vrc = RTThreadCreate(NULL,
5284 Machine::deleteThread,
5285 (void*)pTask,
5286 0,
5287 RTTHREADTYPE_MAIN_WORKER,
5288 0,
5289 "MachineDelete");
5290
5291 pTask->pProgress.queryInterfaceTo(aProgress);
5292
5293 if (RT_FAILURE(vrc))
5294 {
5295 delete pTask;
5296 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5297 }
5298
5299 LogFlowFuncLeave();
5300
5301 return S_OK;
5302}
5303
5304/**
5305 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5306 * calls Machine::deleteTaskWorker() on the actual machine object.
5307 * @param Thread
5308 * @param pvUser
5309 * @return
5310 */
5311/*static*/
5312DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5313{
5314 LogFlowFuncEnter();
5315
5316 DeleteTask *pTask = (DeleteTask*)pvUser;
5317 Assert(pTask);
5318 Assert(pTask->pMachine);
5319 Assert(pTask->pProgress);
5320
5321 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5322 pTask->pProgress->notifyComplete(rc);
5323
5324 delete pTask;
5325
5326 LogFlowFuncLeave();
5327
5328 NOREF(Thread);
5329
5330 return VINF_SUCCESS;
5331}
5332
5333/**
5334 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5335 * @param task
5336 * @return
5337 */
5338HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5339{
5340 AutoCaller autoCaller(this);
5341 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5342
5343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5344
5345 HRESULT rc = S_OK;
5346
5347 try
5348 {
5349 ULONG uLogHistoryCount = 3;
5350 ComPtr<ISystemProperties> systemProperties;
5351 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5352 if (FAILED(rc)) throw rc;
5353
5354 if (!systemProperties.isNull())
5355 {
5356 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5357 if (FAILED(rc)) throw rc;
5358 }
5359
5360 MachineState_T oldState = mData->mMachineState;
5361 setMachineState(MachineState_SettingUp);
5362 alock.release();
5363 for (size_t i = 0; i < task.llMediums.size(); ++i)
5364 {
5365 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5366 {
5367 AutoCaller mac(pMedium);
5368 if (FAILED(mac.rc())) throw mac.rc();
5369 Utf8Str strLocation = pMedium->getLocationFull();
5370 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5371 if (FAILED(rc)) throw rc;
5372 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5373 }
5374 ComPtr<IProgress> pProgress2;
5375 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5376 if (FAILED(rc)) throw rc;
5377 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5378 if (FAILED(rc)) throw rc;
5379 /* Check the result of the asynchrony process. */
5380 LONG iRc;
5381 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5382 if (FAILED(rc)) throw rc;
5383 /* If the thread of the progress object has an error, then
5384 * retrieve the error info from there, or it'll be lost. */
5385 if (FAILED(iRc))
5386 throw setError(ProgressErrorInfo(pProgress2));
5387 }
5388 setMachineState(oldState);
5389 alock.acquire();
5390
5391 // delete the files pushed on the task list by Machine::Delete()
5392 // (this includes saved states of the machine and snapshots and
5393 // medium storage files from the IMedium list passed in, and the
5394 // machine XML file)
5395 StringsList::const_iterator it = task.llFilesToDelete.begin();
5396 while (it != task.llFilesToDelete.end())
5397 {
5398 const Utf8Str &strFile = *it;
5399 LogFunc(("Deleting file %s\n", strFile.c_str()));
5400 int vrc = RTFileDelete(strFile.c_str());
5401 if (RT_FAILURE(vrc))
5402 throw setError(VBOX_E_IPRT_ERROR,
5403 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5404
5405 ++it;
5406 if (it == task.llFilesToDelete.end())
5407 {
5408 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5409 if (FAILED(rc)) throw rc;
5410 break;
5411 }
5412
5413 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5414 if (FAILED(rc)) throw rc;
5415 }
5416
5417 /* delete the settings only when the file actually exists */
5418 if (mData->pMachineConfigFile->fileExists())
5419 {
5420 /* Delete any backup or uncommitted XML files. Ignore failures.
5421 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5422 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5423 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5424 RTFileDelete(otherXml.c_str());
5425 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5426 RTFileDelete(otherXml.c_str());
5427
5428 /* delete the Logs folder, nothing important should be left
5429 * there (we don't check for errors because the user might have
5430 * some private files there that we don't want to delete) */
5431 Utf8Str logFolder;
5432 getLogFolder(logFolder);
5433 Assert(logFolder.length());
5434 if (RTDirExists(logFolder.c_str()))
5435 {
5436 /* Delete all VBox.log[.N] files from the Logs folder
5437 * (this must be in sync with the rotation logic in
5438 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5439 * files that may have been created by the GUI. */
5440 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5441 logFolder.c_str(), RTPATH_DELIMITER);
5442 RTFileDelete(log.c_str());
5443 log = Utf8StrFmt("%s%cVBox.png",
5444 logFolder.c_str(), RTPATH_DELIMITER);
5445 RTFileDelete(log.c_str());
5446 for (int i = uLogHistoryCount; i > 0; i--)
5447 {
5448 log = Utf8StrFmt("%s%cVBox.log.%d",
5449 logFolder.c_str(), RTPATH_DELIMITER, i);
5450 RTFileDelete(log.c_str());
5451 log = Utf8StrFmt("%s%cVBox.png.%d",
5452 logFolder.c_str(), RTPATH_DELIMITER, i);
5453 RTFileDelete(log.c_str());
5454 }
5455
5456 RTDirRemove(logFolder.c_str());
5457 }
5458
5459 /* delete the Snapshots folder, nothing important should be left
5460 * there (we don't check for errors because the user might have
5461 * some private files there that we don't want to delete) */
5462 Utf8Str strFullSnapshotFolder;
5463 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5464 Assert(!strFullSnapshotFolder.isEmpty());
5465 if (RTDirExists(strFullSnapshotFolder.c_str()))
5466 RTDirRemove(strFullSnapshotFolder.c_str());
5467
5468 // delete the directory that contains the settings file, but only
5469 // if it matches the VM name
5470 Utf8Str settingsDir;
5471 if (isInOwnDir(&settingsDir))
5472 RTDirRemove(settingsDir.c_str());
5473 }
5474
5475 alock.release();
5476
5477 mParent->saveModifiedRegistries();
5478 }
5479 catch (HRESULT aRC) { rc = aRC; }
5480
5481 return rc;
5482}
5483
5484STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5485{
5486 CheckComArgOutPointerValid(aSnapshot);
5487
5488 AutoCaller autoCaller(this);
5489 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5490
5491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5492
5493 ComObjPtr<Snapshot> pSnapshot;
5494 HRESULT rc;
5495
5496 if (!aNameOrId || !*aNameOrId)
5497 // null case (caller wants root snapshot): findSnapshotById() handles this
5498 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5499 else
5500 {
5501 Guid uuid(aNameOrId);
5502 if (uuid.isValid())
5503 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5504 else
5505 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5506 }
5507 pSnapshot.queryInterfaceTo(aSnapshot);
5508
5509 return rc;
5510}
5511
5512STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5513{
5514 CheckComArgStrNotEmptyOrNull(aName);
5515 CheckComArgStrNotEmptyOrNull(aHostPath);
5516
5517 AutoCaller autoCaller(this);
5518 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5519
5520 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5521
5522 HRESULT rc = checkStateDependency(MutableStateDep);
5523 if (FAILED(rc)) return rc;
5524
5525 Utf8Str strName(aName);
5526
5527 ComObjPtr<SharedFolder> sharedFolder;
5528 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5529 if (SUCCEEDED(rc))
5530 return setError(VBOX_E_OBJECT_IN_USE,
5531 tr("Shared folder named '%s' already exists"),
5532 strName.c_str());
5533
5534 sharedFolder.createObject();
5535 rc = sharedFolder->init(getMachine(),
5536 strName,
5537 aHostPath,
5538 !!aWritable,
5539 !!aAutoMount,
5540 true /* fFailOnError */);
5541 if (FAILED(rc)) return rc;
5542
5543 setModified(IsModified_SharedFolders);
5544 mHWData.backup();
5545 mHWData->mSharedFolders.push_back(sharedFolder);
5546
5547 /* inform the direct session if any */
5548 alock.release();
5549 onSharedFolderChange();
5550
5551 return S_OK;
5552}
5553
5554STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5555{
5556 CheckComArgStrNotEmptyOrNull(aName);
5557
5558 AutoCaller autoCaller(this);
5559 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5560
5561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5562
5563 HRESULT rc = checkStateDependency(MutableStateDep);
5564 if (FAILED(rc)) return rc;
5565
5566 ComObjPtr<SharedFolder> sharedFolder;
5567 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5568 if (FAILED(rc)) return rc;
5569
5570 setModified(IsModified_SharedFolders);
5571 mHWData.backup();
5572 mHWData->mSharedFolders.remove(sharedFolder);
5573
5574 /* inform the direct session if any */
5575 alock.release();
5576 onSharedFolderChange();
5577
5578 return S_OK;
5579}
5580
5581STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5582{
5583 CheckComArgOutPointerValid(aCanShow);
5584
5585 /* start with No */
5586 *aCanShow = FALSE;
5587
5588 AutoCaller autoCaller(this);
5589 AssertComRCReturnRC(autoCaller.rc());
5590
5591 ComPtr<IInternalSessionControl> directControl;
5592 {
5593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5594
5595 if (mData->mSession.mState != SessionState_Locked)
5596 return setError(VBOX_E_INVALID_VM_STATE,
5597 tr("Machine is not locked for session (session state: %s)"),
5598 Global::stringifySessionState(mData->mSession.mState));
5599
5600 directControl = mData->mSession.mDirectControl;
5601 }
5602
5603 /* ignore calls made after #OnSessionEnd() is called */
5604 if (!directControl)
5605 return S_OK;
5606
5607 LONG64 dummy;
5608 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5609}
5610
5611STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5612{
5613 CheckComArgOutPointerValid(aWinId);
5614
5615 AutoCaller autoCaller(this);
5616 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5617
5618 ComPtr<IInternalSessionControl> directControl;
5619 {
5620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5621
5622 if (mData->mSession.mState != SessionState_Locked)
5623 return setError(E_FAIL,
5624 tr("Machine is not locked for session (session state: %s)"),
5625 Global::stringifySessionState(mData->mSession.mState));
5626
5627 directControl = mData->mSession.mDirectControl;
5628 }
5629
5630 /* ignore calls made after #OnSessionEnd() is called */
5631 if (!directControl)
5632 return S_OK;
5633
5634 BOOL dummy;
5635 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5636}
5637
5638#ifdef VBOX_WITH_GUEST_PROPS
5639/**
5640 * Look up a guest property in VBoxSVC's internal structures.
5641 */
5642HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5643 BSTR *aValue,
5644 LONG64 *aTimestamp,
5645 BSTR *aFlags) const
5646{
5647 using namespace guestProp;
5648
5649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5650 Utf8Str strName(aName);
5651 HWData::GuestPropertyMap::const_iterator it =
5652 mHWData->mGuestProperties.find(strName);
5653
5654 if (it != mHWData->mGuestProperties.end())
5655 {
5656 char szFlags[MAX_FLAGS_LEN + 1];
5657 it->second.strValue.cloneTo(aValue);
5658 *aTimestamp = it->second.mTimestamp;
5659 writeFlags(it->second.mFlags, szFlags);
5660 Bstr(szFlags).cloneTo(aFlags);
5661 }
5662
5663 return S_OK;
5664}
5665
5666/**
5667 * Query the VM that a guest property belongs to for the property.
5668 * @returns E_ACCESSDENIED if the VM process is not available or not
5669 * currently handling queries and the lookup should then be done in
5670 * VBoxSVC.
5671 */
5672HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5673 BSTR *aValue,
5674 LONG64 *aTimestamp,
5675 BSTR *aFlags) const
5676{
5677 HRESULT rc;
5678 ComPtr<IInternalSessionControl> directControl;
5679 directControl = mData->mSession.mDirectControl;
5680
5681 /* fail if we were called after #OnSessionEnd() is called. This is a
5682 * silly race condition. */
5683
5684 if (!directControl)
5685 rc = E_ACCESSDENIED;
5686 else
5687 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5688 false /* isSetter */,
5689 aValue, aTimestamp, aFlags);
5690 return rc;
5691}
5692#endif // VBOX_WITH_GUEST_PROPS
5693
5694STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5695 BSTR *aValue,
5696 LONG64 *aTimestamp,
5697 BSTR *aFlags)
5698{
5699#ifndef VBOX_WITH_GUEST_PROPS
5700 ReturnComNotImplemented();
5701#else // VBOX_WITH_GUEST_PROPS
5702 CheckComArgStrNotEmptyOrNull(aName);
5703 CheckComArgOutPointerValid(aValue);
5704 CheckComArgOutPointerValid(aTimestamp);
5705 CheckComArgOutPointerValid(aFlags);
5706
5707 AutoCaller autoCaller(this);
5708 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5709
5710 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5711 if (rc == E_ACCESSDENIED)
5712 /* The VM is not running or the service is not (yet) accessible */
5713 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5714 return rc;
5715#endif // VBOX_WITH_GUEST_PROPS
5716}
5717
5718STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5719{
5720 LONG64 dummyTimestamp;
5721 Bstr dummyFlags;
5722 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5723}
5724
5725STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5726{
5727 Bstr dummyValue;
5728 Bstr dummyFlags;
5729 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5730}
5731
5732#ifdef VBOX_WITH_GUEST_PROPS
5733/**
5734 * Set a guest property in VBoxSVC's internal structures.
5735 */
5736HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5737 IN_BSTR aFlags)
5738{
5739 using namespace guestProp;
5740
5741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5742 HRESULT rc = S_OK;
5743 HWData::GuestProperty property;
5744 property.mFlags = NILFLAG;
5745
5746 rc = checkStateDependency(MutableStateDep);
5747 if (FAILED(rc)) return rc;
5748
5749 try
5750 {
5751 Utf8Str utf8Name(aName);
5752 Utf8Str utf8Flags(aFlags);
5753 uint32_t fFlags = NILFLAG;
5754 if ( (aFlags != NULL)
5755 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5756 )
5757 return setError(E_INVALIDARG,
5758 tr("Invalid guest property flag values: '%ls'"),
5759 aFlags);
5760
5761 HWData::GuestPropertyMap::iterator it =
5762 mHWData->mGuestProperties.find(utf8Name);
5763
5764 if (it == mHWData->mGuestProperties.end())
5765 {
5766 setModified(IsModified_MachineData);
5767 mHWData.backupEx();
5768
5769 RTTIMESPEC time;
5770 HWData::GuestProperty prop;
5771 prop.strValue = aValue;
5772 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5773 prop.mFlags = fFlags;
5774
5775 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5776 }
5777 else
5778 {
5779 if (it->second.mFlags & (RDONLYHOST))
5780 {
5781 rc = setError(E_ACCESSDENIED,
5782 tr("The property '%ls' cannot be changed by the host"),
5783 aName);
5784 }
5785 else
5786 {
5787 setModified(IsModified_MachineData);
5788 mHWData.backupEx();
5789
5790 /* The backupEx() operation invalidates our iterator,
5791 * so get a new one. */
5792 it = mHWData->mGuestProperties.find(utf8Name);
5793 Assert(it != mHWData->mGuestProperties.end());
5794
5795 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
5796 {
5797 RTTIMESPEC time;
5798 it->second.strValue = aValue;
5799 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5800 if (aFlags != NULL)
5801 it->second.mFlags = fFlags;
5802 }
5803 else
5804 {
5805 mHWData->mGuestProperties.erase(it);
5806 }
5807 }
5808 }
5809
5810 if ( SUCCEEDED(rc)
5811 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5812 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5813 RTSTR_MAX,
5814 utf8Name.c_str(),
5815 RTSTR_MAX,
5816 NULL)
5817 )
5818 )
5819 {
5820 alock.release();
5821
5822 mParent->onGuestPropertyChange(mData->mUuid, aName,
5823 aValue ? aValue : Bstr("").raw(),
5824 aFlags ? aFlags : Bstr("").raw());
5825 }
5826 }
5827 catch (std::bad_alloc &)
5828 {
5829 rc = E_OUTOFMEMORY;
5830 }
5831
5832 return rc;
5833}
5834
5835/**
5836 * Set a property on the VM that that property belongs to.
5837 * @returns E_ACCESSDENIED if the VM process is not available or not
5838 * currently handling queries and the setting should then be done in
5839 * VBoxSVC.
5840 */
5841HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5842 IN_BSTR aFlags)
5843{
5844 HRESULT rc;
5845
5846 try
5847 {
5848 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5849
5850 BSTR dummy = NULL; /* will not be changed (setter) */
5851 LONG64 dummy64;
5852 if (!directControl)
5853 rc = E_ACCESSDENIED;
5854 else
5855 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5856 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5857 true /* isSetter */,
5858 &dummy, &dummy64, &dummy);
5859 }
5860 catch (std::bad_alloc &)
5861 {
5862 rc = E_OUTOFMEMORY;
5863 }
5864
5865 return rc;
5866}
5867#endif // VBOX_WITH_GUEST_PROPS
5868
5869STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5870 IN_BSTR aFlags)
5871{
5872#ifndef VBOX_WITH_GUEST_PROPS
5873 ReturnComNotImplemented();
5874#else // VBOX_WITH_GUEST_PROPS
5875 CheckComArgStrNotEmptyOrNull(aName);
5876 CheckComArgMaybeNull(aFlags);
5877 CheckComArgMaybeNull(aValue);
5878
5879 AutoCaller autoCaller(this);
5880 if (FAILED(autoCaller.rc()))
5881 return autoCaller.rc();
5882
5883 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5884 if (rc == E_ACCESSDENIED)
5885 /* The VM is not running or the service is not (yet) accessible */
5886 rc = setGuestPropertyToService(aName, aValue, aFlags);
5887 return rc;
5888#endif // VBOX_WITH_GUEST_PROPS
5889}
5890
5891STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5892{
5893 return SetGuestProperty(aName, aValue, NULL);
5894}
5895
5896STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5897{
5898 return SetGuestProperty(aName, NULL, NULL);
5899}
5900
5901#ifdef VBOX_WITH_GUEST_PROPS
5902/**
5903 * Enumerate the guest properties in VBoxSVC's internal structures.
5904 */
5905HRESULT Machine::enumerateGuestPropertiesInService
5906 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5907 ComSafeArrayOut(BSTR, aValues),
5908 ComSafeArrayOut(LONG64, aTimestamps),
5909 ComSafeArrayOut(BSTR, aFlags))
5910{
5911 using namespace guestProp;
5912
5913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5914 Utf8Str strPatterns(aPatterns);
5915
5916 HWData::GuestPropertyMap propMap;
5917
5918 /*
5919 * Look for matching patterns and build up a list.
5920 */
5921 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5922 while (it != mHWData->mGuestProperties.end())
5923 {
5924 if ( strPatterns.isEmpty()
5925 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5926 RTSTR_MAX,
5927 it->first.c_str(),
5928 RTSTR_MAX,
5929 NULL)
5930 )
5931 {
5932 propMap.insert(*it);
5933 }
5934
5935 it++;
5936 }
5937
5938 alock.release();
5939
5940 /*
5941 * And build up the arrays for returning the property information.
5942 */
5943 size_t cEntries = propMap.size();
5944 SafeArray<BSTR> names(cEntries);
5945 SafeArray<BSTR> values(cEntries);
5946 SafeArray<LONG64> timestamps(cEntries);
5947 SafeArray<BSTR> flags(cEntries);
5948 size_t iProp = 0;
5949
5950 it = propMap.begin();
5951 while (it != propMap.end())
5952 {
5953 char szFlags[MAX_FLAGS_LEN + 1];
5954 it->first.cloneTo(&names[iProp]);
5955 it->second.strValue.cloneTo(&values[iProp]);
5956 timestamps[iProp] = it->second.mTimestamp;
5957 writeFlags(it->second.mFlags, szFlags);
5958 Bstr(szFlags).cloneTo(&flags[iProp++]);
5959 it++;
5960 }
5961 names.detachTo(ComSafeArrayOutArg(aNames));
5962 values.detachTo(ComSafeArrayOutArg(aValues));
5963 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5964 flags.detachTo(ComSafeArrayOutArg(aFlags));
5965 return S_OK;
5966}
5967
5968/**
5969 * Enumerate the properties managed by a VM.
5970 * @returns E_ACCESSDENIED if the VM process is not available or not
5971 * currently handling queries and the setting should then be done in
5972 * VBoxSVC.
5973 */
5974HRESULT Machine::enumerateGuestPropertiesOnVM
5975 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5976 ComSafeArrayOut(BSTR, aValues),
5977 ComSafeArrayOut(LONG64, aTimestamps),
5978 ComSafeArrayOut(BSTR, aFlags))
5979{
5980 HRESULT rc;
5981 ComPtr<IInternalSessionControl> directControl;
5982 directControl = mData->mSession.mDirectControl;
5983
5984 if (!directControl)
5985 rc = E_ACCESSDENIED;
5986 else
5987 rc = directControl->EnumerateGuestProperties
5988 (aPatterns, ComSafeArrayOutArg(aNames),
5989 ComSafeArrayOutArg(aValues),
5990 ComSafeArrayOutArg(aTimestamps),
5991 ComSafeArrayOutArg(aFlags));
5992 return rc;
5993}
5994#endif // VBOX_WITH_GUEST_PROPS
5995
5996STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5997 ComSafeArrayOut(BSTR, aNames),
5998 ComSafeArrayOut(BSTR, aValues),
5999 ComSafeArrayOut(LONG64, aTimestamps),
6000 ComSafeArrayOut(BSTR, aFlags))
6001{
6002#ifndef VBOX_WITH_GUEST_PROPS
6003 ReturnComNotImplemented();
6004#else // VBOX_WITH_GUEST_PROPS
6005 CheckComArgMaybeNull(aPatterns);
6006 CheckComArgOutSafeArrayPointerValid(aNames);
6007 CheckComArgOutSafeArrayPointerValid(aValues);
6008 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6009 CheckComArgOutSafeArrayPointerValid(aFlags);
6010
6011 AutoCaller autoCaller(this);
6012 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6013
6014 HRESULT rc = enumerateGuestPropertiesOnVM
6015 (aPatterns, ComSafeArrayOutArg(aNames),
6016 ComSafeArrayOutArg(aValues),
6017 ComSafeArrayOutArg(aTimestamps),
6018 ComSafeArrayOutArg(aFlags));
6019 if (rc == E_ACCESSDENIED)
6020 /* The VM is not running or the service is not (yet) accessible */
6021 rc = enumerateGuestPropertiesInService
6022 (aPatterns, ComSafeArrayOutArg(aNames),
6023 ComSafeArrayOutArg(aValues),
6024 ComSafeArrayOutArg(aTimestamps),
6025 ComSafeArrayOutArg(aFlags));
6026 return rc;
6027#endif // VBOX_WITH_GUEST_PROPS
6028}
6029
6030STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6031 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6032{
6033 MediaData::AttachmentList atts;
6034
6035 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6036 if (FAILED(rc)) return rc;
6037
6038 SafeIfaceArray<IMediumAttachment> attachments(atts);
6039 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6040
6041 return S_OK;
6042}
6043
6044STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6045 LONG aControllerPort,
6046 LONG aDevice,
6047 IMediumAttachment **aAttachment)
6048{
6049 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6050 aControllerName, aControllerPort, aDevice));
6051
6052 CheckComArgStrNotEmptyOrNull(aControllerName);
6053 CheckComArgOutPointerValid(aAttachment);
6054
6055 AutoCaller autoCaller(this);
6056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6057
6058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6059
6060 *aAttachment = NULL;
6061
6062 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6063 aControllerName,
6064 aControllerPort,
6065 aDevice);
6066 if (pAttach.isNull())
6067 return setError(VBOX_E_OBJECT_NOT_FOUND,
6068 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6069 aDevice, aControllerPort, aControllerName);
6070
6071 pAttach.queryInterfaceTo(aAttachment);
6072
6073 return S_OK;
6074}
6075
6076STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6077 StorageBus_T aConnectionType,
6078 IStorageController **controller)
6079{
6080 CheckComArgStrNotEmptyOrNull(aName);
6081
6082 if ( (aConnectionType <= StorageBus_Null)
6083 || (aConnectionType > StorageBus_SAS))
6084 return setError(E_INVALIDARG,
6085 tr("Invalid connection type: %d"),
6086 aConnectionType);
6087
6088 AutoCaller autoCaller(this);
6089 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6090
6091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6092
6093 HRESULT rc = checkStateDependency(MutableStateDep);
6094 if (FAILED(rc)) return rc;
6095
6096 /* try to find one with the name first. */
6097 ComObjPtr<StorageController> ctrl;
6098
6099 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6100 if (SUCCEEDED(rc))
6101 return setError(VBOX_E_OBJECT_IN_USE,
6102 tr("Storage controller named '%ls' already exists"),
6103 aName);
6104
6105 ctrl.createObject();
6106
6107 /* get a new instance number for the storage controller */
6108 ULONG ulInstance = 0;
6109 bool fBootable = true;
6110 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6111 it != mStorageControllers->end();
6112 ++it)
6113 {
6114 if ((*it)->getStorageBus() == aConnectionType)
6115 {
6116 ULONG ulCurInst = (*it)->getInstance();
6117
6118 if (ulCurInst >= ulInstance)
6119 ulInstance = ulCurInst + 1;
6120
6121 /* Only one controller of each type can be marked as bootable. */
6122 if ((*it)->getBootable())
6123 fBootable = false;
6124 }
6125 }
6126
6127 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6128 if (FAILED(rc)) return rc;
6129
6130 setModified(IsModified_Storage);
6131 mStorageControllers.backup();
6132 mStorageControllers->push_back(ctrl);
6133
6134 ctrl.queryInterfaceTo(controller);
6135
6136 /* inform the direct session if any */
6137 alock.release();
6138 onStorageControllerChange();
6139
6140 return S_OK;
6141}
6142
6143STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6144 IStorageController **aStorageController)
6145{
6146 CheckComArgStrNotEmptyOrNull(aName);
6147
6148 AutoCaller autoCaller(this);
6149 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6150
6151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6152
6153 ComObjPtr<StorageController> ctrl;
6154
6155 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6156 if (SUCCEEDED(rc))
6157 ctrl.queryInterfaceTo(aStorageController);
6158
6159 return rc;
6160}
6161
6162STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6163 IStorageController **aStorageController)
6164{
6165 AutoCaller autoCaller(this);
6166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6167
6168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6169
6170 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6171 it != mStorageControllers->end();
6172 ++it)
6173 {
6174 if ((*it)->getInstance() == aInstance)
6175 {
6176 (*it).queryInterfaceTo(aStorageController);
6177 return S_OK;
6178 }
6179 }
6180
6181 return setError(VBOX_E_OBJECT_NOT_FOUND,
6182 tr("Could not find a storage controller with instance number '%lu'"),
6183 aInstance);
6184}
6185
6186STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6187{
6188 AutoCaller autoCaller(this);
6189 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6190
6191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6192
6193 HRESULT rc = checkStateDependency(MutableStateDep);
6194 if (FAILED(rc)) return rc;
6195
6196 ComObjPtr<StorageController> ctrl;
6197
6198 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6199 if (SUCCEEDED(rc))
6200 {
6201 /* Ensure that only one controller of each type is marked as bootable. */
6202 if (fBootable == TRUE)
6203 {
6204 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6205 it != mStorageControllers->end();
6206 ++it)
6207 {
6208 ComObjPtr<StorageController> aCtrl = (*it);
6209
6210 if ( (aCtrl->getName() != Utf8Str(aName))
6211 && aCtrl->getBootable() == TRUE
6212 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6213 && aCtrl->getControllerType() == ctrl->getControllerType())
6214 {
6215 aCtrl->setBootable(FALSE);
6216 break;
6217 }
6218 }
6219 }
6220
6221 if (SUCCEEDED(rc))
6222 {
6223 ctrl->setBootable(fBootable);
6224 setModified(IsModified_Storage);
6225 }
6226 }
6227
6228 if (SUCCEEDED(rc))
6229 {
6230 /* inform the direct session if any */
6231 alock.release();
6232 onStorageControllerChange();
6233 }
6234
6235 return rc;
6236}
6237
6238STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6239{
6240 CheckComArgStrNotEmptyOrNull(aName);
6241
6242 AutoCaller autoCaller(this);
6243 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6244
6245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6246
6247 HRESULT rc = checkStateDependency(MutableStateDep);
6248 if (FAILED(rc)) return rc;
6249
6250 ComObjPtr<StorageController> ctrl;
6251 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6252 if (FAILED(rc)) return rc;
6253
6254 {
6255 /* find all attached devices to the appropriate storage controller and detach them all */
6256 // make a temporary list because detachDevice invalidates iterators into
6257 // mMediaData->mAttachments
6258 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6259
6260 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6261 it != llAttachments2.end();
6262 ++it)
6263 {
6264 MediumAttachment *pAttachTemp = *it;
6265
6266 AutoCaller localAutoCaller(pAttachTemp);
6267 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6268
6269 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6270
6271 if (pAttachTemp->getControllerName() == aName)
6272 {
6273 rc = detachDevice(pAttachTemp, alock, NULL);
6274 if (FAILED(rc)) return rc;
6275 }
6276 }
6277 }
6278
6279 /* We can remove it now. */
6280 setModified(IsModified_Storage);
6281 mStorageControllers.backup();
6282
6283 ctrl->unshare();
6284
6285 mStorageControllers->remove(ctrl);
6286
6287 /* inform the direct session if any */
6288 alock.release();
6289 onStorageControllerChange();
6290
6291 return S_OK;
6292}
6293
6294STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6295 ULONG *puOriginX,
6296 ULONG *puOriginY,
6297 ULONG *puWidth,
6298 ULONG *puHeight,
6299 BOOL *pfEnabled)
6300{
6301 LogFlowThisFunc(("\n"));
6302
6303 CheckComArgNotNull(puOriginX);
6304 CheckComArgNotNull(puOriginY);
6305 CheckComArgNotNull(puWidth);
6306 CheckComArgNotNull(puHeight);
6307 CheckComArgNotNull(pfEnabled);
6308
6309 uint32_t u32OriginX= 0;
6310 uint32_t u32OriginY= 0;
6311 uint32_t u32Width = 0;
6312 uint32_t u32Height = 0;
6313 uint16_t u16Flags = 0;
6314
6315 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6316 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6317 if (RT_FAILURE(vrc))
6318 {
6319#ifdef RT_OS_WINDOWS
6320 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6321 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6322 * So just assign fEnable to TRUE again.
6323 * The right fix would be to change GUI API wrappers to make sure that parameters
6324 * are changed only if API succeeds.
6325 */
6326 *pfEnabled = TRUE;
6327#endif
6328 return setError(VBOX_E_IPRT_ERROR,
6329 tr("Saved guest size is not available (%Rrc)"),
6330 vrc);
6331 }
6332
6333 *puOriginX = u32OriginX;
6334 *puOriginY = u32OriginY;
6335 *puWidth = u32Width;
6336 *puHeight = u32Height;
6337 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6338
6339 return S_OK;
6340}
6341
6342STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6343{
6344 LogFlowThisFunc(("\n"));
6345
6346 CheckComArgNotNull(aSize);
6347 CheckComArgNotNull(aWidth);
6348 CheckComArgNotNull(aHeight);
6349
6350 if (aScreenId != 0)
6351 return E_NOTIMPL;
6352
6353 AutoCaller autoCaller(this);
6354 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6355
6356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6357
6358 uint8_t *pu8Data = NULL;
6359 uint32_t cbData = 0;
6360 uint32_t u32Width = 0;
6361 uint32_t u32Height = 0;
6362
6363 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6364
6365 if (RT_FAILURE(vrc))
6366 return setError(VBOX_E_IPRT_ERROR,
6367 tr("Saved screenshot data is not available (%Rrc)"),
6368 vrc);
6369
6370 *aSize = cbData;
6371 *aWidth = u32Width;
6372 *aHeight = u32Height;
6373
6374 freeSavedDisplayScreenshot(pu8Data);
6375
6376 return S_OK;
6377}
6378
6379STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6380{
6381 LogFlowThisFunc(("\n"));
6382
6383 CheckComArgNotNull(aWidth);
6384 CheckComArgNotNull(aHeight);
6385 CheckComArgOutSafeArrayPointerValid(aData);
6386
6387 if (aScreenId != 0)
6388 return E_NOTIMPL;
6389
6390 AutoCaller autoCaller(this);
6391 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6392
6393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6394
6395 uint8_t *pu8Data = NULL;
6396 uint32_t cbData = 0;
6397 uint32_t u32Width = 0;
6398 uint32_t u32Height = 0;
6399
6400 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6401
6402 if (RT_FAILURE(vrc))
6403 return setError(VBOX_E_IPRT_ERROR,
6404 tr("Saved screenshot data is not available (%Rrc)"),
6405 vrc);
6406
6407 *aWidth = u32Width;
6408 *aHeight = u32Height;
6409
6410 com::SafeArray<BYTE> bitmap(cbData);
6411 /* Convert pixels to format expected by the API caller. */
6412 if (aBGR)
6413 {
6414 /* [0] B, [1] G, [2] R, [3] A. */
6415 for (unsigned i = 0; i < cbData; i += 4)
6416 {
6417 bitmap[i] = pu8Data[i];
6418 bitmap[i + 1] = pu8Data[i + 1];
6419 bitmap[i + 2] = pu8Data[i + 2];
6420 bitmap[i + 3] = 0xff;
6421 }
6422 }
6423 else
6424 {
6425 /* [0] R, [1] G, [2] B, [3] A. */
6426 for (unsigned i = 0; i < cbData; i += 4)
6427 {
6428 bitmap[i] = pu8Data[i + 2];
6429 bitmap[i + 1] = pu8Data[i + 1];
6430 bitmap[i + 2] = pu8Data[i];
6431 bitmap[i + 3] = 0xff;
6432 }
6433 }
6434 bitmap.detachTo(ComSafeArrayOutArg(aData));
6435
6436 freeSavedDisplayScreenshot(pu8Data);
6437
6438 return S_OK;
6439}
6440
6441
6442STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6443{
6444 LogFlowThisFunc(("\n"));
6445
6446 CheckComArgNotNull(aWidth);
6447 CheckComArgNotNull(aHeight);
6448 CheckComArgOutSafeArrayPointerValid(aData);
6449
6450 if (aScreenId != 0)
6451 return E_NOTIMPL;
6452
6453 AutoCaller autoCaller(this);
6454 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6455
6456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6457
6458 uint8_t *pu8Data = NULL;
6459 uint32_t cbData = 0;
6460 uint32_t u32Width = 0;
6461 uint32_t u32Height = 0;
6462
6463 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6464
6465 if (RT_FAILURE(vrc))
6466 return setError(VBOX_E_IPRT_ERROR,
6467 tr("Saved screenshot data is not available (%Rrc)"),
6468 vrc);
6469
6470 *aWidth = u32Width;
6471 *aHeight = u32Height;
6472
6473 HRESULT rc = S_OK;
6474 uint8_t *pu8PNG = NULL;
6475 uint32_t cbPNG = 0;
6476 uint32_t cxPNG = 0;
6477 uint32_t cyPNG = 0;
6478
6479 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6480
6481 if (RT_SUCCESS(vrc))
6482 {
6483 com::SafeArray<BYTE> screenData(cbPNG);
6484 screenData.initFrom(pu8PNG, cbPNG);
6485 if (pu8PNG)
6486 RTMemFree(pu8PNG);
6487 screenData.detachTo(ComSafeArrayOutArg(aData));
6488 }
6489 else
6490 {
6491 if (pu8PNG)
6492 RTMemFree(pu8PNG);
6493 return setError(VBOX_E_IPRT_ERROR,
6494 tr("Could not convert screenshot to PNG (%Rrc)"),
6495 vrc);
6496 }
6497
6498 freeSavedDisplayScreenshot(pu8Data);
6499
6500 return rc;
6501}
6502
6503STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6504{
6505 LogFlowThisFunc(("\n"));
6506
6507 CheckComArgNotNull(aSize);
6508 CheckComArgNotNull(aWidth);
6509 CheckComArgNotNull(aHeight);
6510
6511 if (aScreenId != 0)
6512 return E_NOTIMPL;
6513
6514 AutoCaller autoCaller(this);
6515 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6516
6517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6518
6519 uint8_t *pu8Data = NULL;
6520 uint32_t cbData = 0;
6521 uint32_t u32Width = 0;
6522 uint32_t u32Height = 0;
6523
6524 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6525
6526 if (RT_FAILURE(vrc))
6527 return setError(VBOX_E_IPRT_ERROR,
6528 tr("Saved screenshot data is not available (%Rrc)"),
6529 vrc);
6530
6531 *aSize = cbData;
6532 *aWidth = u32Width;
6533 *aHeight = u32Height;
6534
6535 freeSavedDisplayScreenshot(pu8Data);
6536
6537 return S_OK;
6538}
6539
6540STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6541{
6542 LogFlowThisFunc(("\n"));
6543
6544 CheckComArgNotNull(aWidth);
6545 CheckComArgNotNull(aHeight);
6546 CheckComArgOutSafeArrayPointerValid(aData);
6547
6548 if (aScreenId != 0)
6549 return E_NOTIMPL;
6550
6551 AutoCaller autoCaller(this);
6552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6553
6554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6555
6556 uint8_t *pu8Data = NULL;
6557 uint32_t cbData = 0;
6558 uint32_t u32Width = 0;
6559 uint32_t u32Height = 0;
6560
6561 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6562
6563 if (RT_FAILURE(vrc))
6564 return setError(VBOX_E_IPRT_ERROR,
6565 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6566 vrc);
6567
6568 *aWidth = u32Width;
6569 *aHeight = u32Height;
6570
6571 com::SafeArray<BYTE> png(cbData);
6572 png.initFrom(pu8Data, cbData);
6573 png.detachTo(ComSafeArrayOutArg(aData));
6574
6575 freeSavedDisplayScreenshot(pu8Data);
6576
6577 return S_OK;
6578}
6579
6580STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6581{
6582 HRESULT rc = S_OK;
6583 LogFlowThisFunc(("\n"));
6584
6585 AutoCaller autoCaller(this);
6586 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6587
6588 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6589
6590 if (!mHWData->mCPUHotPlugEnabled)
6591 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6592
6593 if (aCpu >= mHWData->mCPUCount)
6594 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6595
6596 if (mHWData->mCPUAttached[aCpu])
6597 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6598
6599 alock.release();
6600 rc = onCPUChange(aCpu, false);
6601 alock.acquire();
6602 if (FAILED(rc)) return rc;
6603
6604 setModified(IsModified_MachineData);
6605 mHWData.backup();
6606 mHWData->mCPUAttached[aCpu] = true;
6607
6608 /* Save settings if online */
6609 if (Global::IsOnline(mData->mMachineState))
6610 saveSettings(NULL);
6611
6612 return S_OK;
6613}
6614
6615STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6616{
6617 HRESULT rc = S_OK;
6618 LogFlowThisFunc(("\n"));
6619
6620 AutoCaller autoCaller(this);
6621 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6622
6623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6624
6625 if (!mHWData->mCPUHotPlugEnabled)
6626 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6627
6628 if (aCpu >= SchemaDefs::MaxCPUCount)
6629 return setError(E_INVALIDARG,
6630 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6631 SchemaDefs::MaxCPUCount);
6632
6633 if (!mHWData->mCPUAttached[aCpu])
6634 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6635
6636 /* CPU 0 can't be detached */
6637 if (aCpu == 0)
6638 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6639
6640 alock.release();
6641 rc = onCPUChange(aCpu, true);
6642 alock.acquire();
6643 if (FAILED(rc)) return rc;
6644
6645 setModified(IsModified_MachineData);
6646 mHWData.backup();
6647 mHWData->mCPUAttached[aCpu] = false;
6648
6649 /* Save settings if online */
6650 if (Global::IsOnline(mData->mMachineState))
6651 saveSettings(NULL);
6652
6653 return S_OK;
6654}
6655
6656STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6657{
6658 LogFlowThisFunc(("\n"));
6659
6660 CheckComArgNotNull(aCpuAttached);
6661
6662 *aCpuAttached = false;
6663
6664 AutoCaller autoCaller(this);
6665 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6666
6667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6668
6669 /* If hotplug is enabled the CPU is always enabled. */
6670 if (!mHWData->mCPUHotPlugEnabled)
6671 {
6672 if (aCpu < mHWData->mCPUCount)
6673 *aCpuAttached = true;
6674 }
6675 else
6676 {
6677 if (aCpu < SchemaDefs::MaxCPUCount)
6678 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6679 }
6680
6681 return S_OK;
6682}
6683
6684STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6685{
6686 CheckComArgOutPointerValid(aName);
6687
6688 AutoCaller autoCaller(this);
6689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6690
6691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6692
6693 Utf8Str log = queryLogFilename(aIdx);
6694 if (!RTFileExists(log.c_str()))
6695 log.setNull();
6696 log.cloneTo(aName);
6697
6698 return S_OK;
6699}
6700
6701STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6702{
6703 LogFlowThisFunc(("\n"));
6704 CheckComArgOutSafeArrayPointerValid(aData);
6705 if (aSize < 0)
6706 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6707
6708 AutoCaller autoCaller(this);
6709 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6710
6711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6712
6713 HRESULT rc = S_OK;
6714 Utf8Str log = queryLogFilename(aIdx);
6715
6716 /* do not unnecessarily hold the lock while doing something which does
6717 * not need the lock and potentially takes a long time. */
6718 alock.release();
6719
6720 /* Limit the chunk size to 32K for now, as that gives better performance
6721 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6722 * One byte expands to approx. 25 bytes of breathtaking XML. */
6723 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6724 com::SafeArray<BYTE> logData(cbData);
6725
6726 RTFILE LogFile;
6727 int vrc = RTFileOpen(&LogFile, log.c_str(),
6728 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6729 if (RT_SUCCESS(vrc))
6730 {
6731 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6732 if (RT_SUCCESS(vrc))
6733 logData.resize(cbData);
6734 else
6735 rc = setError(VBOX_E_IPRT_ERROR,
6736 tr("Could not read log file '%s' (%Rrc)"),
6737 log.c_str(), vrc);
6738 RTFileClose(LogFile);
6739 }
6740 else
6741 rc = setError(VBOX_E_IPRT_ERROR,
6742 tr("Could not open log file '%s' (%Rrc)"),
6743 log.c_str(), vrc);
6744
6745 if (FAILED(rc))
6746 logData.resize(0);
6747 logData.detachTo(ComSafeArrayOutArg(aData));
6748
6749 return rc;
6750}
6751
6752
6753/**
6754 * Currently this method doesn't attach device to the running VM,
6755 * just makes sure it's plugged on next VM start.
6756 */
6757STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6758{
6759 AutoCaller autoCaller(this);
6760 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6761
6762 // lock scope
6763 {
6764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6765
6766 HRESULT rc = checkStateDependency(MutableStateDep);
6767 if (FAILED(rc)) return rc;
6768
6769 ChipsetType_T aChipset = ChipsetType_PIIX3;
6770 COMGETTER(ChipsetType)(&aChipset);
6771
6772 if (aChipset != ChipsetType_ICH9)
6773 {
6774 return setError(E_INVALIDARG,
6775 tr("Host PCI attachment only supported with ICH9 chipset"));
6776 }
6777
6778 // check if device with this host PCI address already attached
6779 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6780 it != mHWData->mPCIDeviceAssignments.end();
6781 ++it)
6782 {
6783 LONG iHostAddress = -1;
6784 ComPtr<PCIDeviceAttachment> pAttach;
6785 pAttach = *it;
6786 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6787 if (iHostAddress == hostAddress)
6788 return setError(E_INVALIDARG,
6789 tr("Device with host PCI address already attached to this VM"));
6790 }
6791
6792 ComObjPtr<PCIDeviceAttachment> pda;
6793 char name[32];
6794
6795 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6796 Bstr bname(name);
6797 pda.createObject();
6798 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6799 setModified(IsModified_MachineData);
6800 mHWData.backup();
6801 mHWData->mPCIDeviceAssignments.push_back(pda);
6802 }
6803
6804 return S_OK;
6805}
6806
6807/**
6808 * Currently this method doesn't detach device from the running VM,
6809 * just makes sure it's not plugged on next VM start.
6810 */
6811STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6812{
6813 AutoCaller autoCaller(this);
6814 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6815
6816 ComObjPtr<PCIDeviceAttachment> pAttach;
6817 bool fRemoved = false;
6818 HRESULT rc;
6819
6820 // lock scope
6821 {
6822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6823
6824 rc = checkStateDependency(MutableStateDep);
6825 if (FAILED(rc)) return rc;
6826
6827 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6828 it != mHWData->mPCIDeviceAssignments.end();
6829 ++it)
6830 {
6831 LONG iHostAddress = -1;
6832 pAttach = *it;
6833 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6834 if (iHostAddress != -1 && iHostAddress == hostAddress)
6835 {
6836 setModified(IsModified_MachineData);
6837 mHWData.backup();
6838 mHWData->mPCIDeviceAssignments.remove(pAttach);
6839 fRemoved = true;
6840 break;
6841 }
6842 }
6843 }
6844
6845
6846 /* Fire event outside of the lock */
6847 if (fRemoved)
6848 {
6849 Assert(!pAttach.isNull());
6850 ComPtr<IEventSource> es;
6851 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6852 Assert(SUCCEEDED(rc));
6853 Bstr mid;
6854 rc = this->COMGETTER(Id)(mid.asOutParam());
6855 Assert(SUCCEEDED(rc));
6856 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6857 }
6858
6859 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6860 tr("No host PCI device %08x attached"),
6861 hostAddress
6862 );
6863}
6864
6865STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6866{
6867 CheckComArgOutSafeArrayPointerValid(aAssignments);
6868
6869 AutoCaller autoCaller(this);
6870 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6871
6872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6873
6874 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6875 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6876
6877 return S_OK;
6878}
6879
6880STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6881{
6882 CheckComArgOutPointerValid(aBandwidthControl);
6883
6884 AutoCaller autoCaller(this);
6885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6886
6887 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6888
6889 return S_OK;
6890}
6891
6892STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6893{
6894 CheckComArgOutPointerValid(pfEnabled);
6895 AutoCaller autoCaller(this);
6896 HRESULT hrc = autoCaller.rc();
6897 if (SUCCEEDED(hrc))
6898 {
6899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6900 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6901 }
6902 return hrc;
6903}
6904
6905STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6906{
6907 AutoCaller autoCaller(this);
6908 HRESULT hrc = autoCaller.rc();
6909 if (SUCCEEDED(hrc))
6910 {
6911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6912 hrc = checkStateDependency(MutableStateDep);
6913 if (SUCCEEDED(hrc))
6914 {
6915 hrc = mHWData.backupEx();
6916 if (SUCCEEDED(hrc))
6917 {
6918 setModified(IsModified_MachineData);
6919 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6920 }
6921 }
6922 }
6923 return hrc;
6924}
6925
6926STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6927{
6928 CheckComArgOutPointerValid(pbstrConfig);
6929 AutoCaller autoCaller(this);
6930 HRESULT hrc = autoCaller.rc();
6931 if (SUCCEEDED(hrc))
6932 {
6933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6934 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6935 }
6936 return hrc;
6937}
6938
6939STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6940{
6941 CheckComArgStr(bstrConfig);
6942 AutoCaller autoCaller(this);
6943 HRESULT hrc = autoCaller.rc();
6944 if (SUCCEEDED(hrc))
6945 {
6946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6947 hrc = checkStateDependency(MutableStateDep);
6948 if (SUCCEEDED(hrc))
6949 {
6950 hrc = mHWData.backupEx();
6951 if (SUCCEEDED(hrc))
6952 {
6953 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6954 if (SUCCEEDED(hrc))
6955 setModified(IsModified_MachineData);
6956 }
6957 }
6958 }
6959 return hrc;
6960
6961}
6962
6963STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6964{
6965 CheckComArgOutPointerValid(pfAllow);
6966 AutoCaller autoCaller(this);
6967 HRESULT hrc = autoCaller.rc();
6968 if (SUCCEEDED(hrc))
6969 {
6970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6971 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6972 }
6973 return hrc;
6974}
6975
6976STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6977{
6978 AutoCaller autoCaller(this);
6979 HRESULT hrc = autoCaller.rc();
6980 if (SUCCEEDED(hrc))
6981 {
6982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6983 hrc = checkStateDependency(MutableStateDep);
6984 if (SUCCEEDED(hrc))
6985 {
6986 hrc = mHWData.backupEx();
6987 if (SUCCEEDED(hrc))
6988 {
6989 setModified(IsModified_MachineData);
6990 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6991 }
6992 }
6993 }
6994 return hrc;
6995}
6996
6997STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6998{
6999 CheckComArgOutPointerValid(pfEnabled);
7000 AutoCaller autoCaller(this);
7001 HRESULT hrc = autoCaller.rc();
7002 if (SUCCEEDED(hrc))
7003 {
7004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7005 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7006 }
7007 return hrc;
7008}
7009
7010STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7011{
7012 AutoCaller autoCaller(this);
7013 HRESULT hrc = autoCaller.rc();
7014 if (SUCCEEDED(hrc))
7015 {
7016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7017 hrc = checkStateDependency(MutableStateDep);
7018 if ( SUCCEEDED(hrc)
7019 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7020 {
7021 AutostartDb *autostartDb = mParent->getAutostartDb();
7022 int vrc;
7023
7024 if (fEnabled)
7025 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7026 else
7027 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7028
7029 if (RT_SUCCESS(vrc))
7030 {
7031 hrc = mHWData.backupEx();
7032 if (SUCCEEDED(hrc))
7033 {
7034 setModified(IsModified_MachineData);
7035 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7036 }
7037 }
7038 else if (vrc == VERR_NOT_SUPPORTED)
7039 hrc = setError(VBOX_E_NOT_SUPPORTED,
7040 tr("The VM autostart feature is not supported on this platform"));
7041 else if (vrc == VERR_PATH_NOT_FOUND)
7042 hrc = setError(E_FAIL,
7043 tr("The path to the autostart database is not set"));
7044 else
7045 hrc = setError(E_UNEXPECTED,
7046 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7047 fEnabled ? "Adding" : "Removing",
7048 mUserData->s.strName.c_str(), vrc);
7049 }
7050 }
7051 return hrc;
7052}
7053
7054STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7055{
7056 CheckComArgOutPointerValid(puDelay);
7057 AutoCaller autoCaller(this);
7058 HRESULT hrc = autoCaller.rc();
7059 if (SUCCEEDED(hrc))
7060 {
7061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7062 *puDelay = mHWData->mAutostart.uAutostartDelay;
7063 }
7064 return hrc;
7065}
7066
7067STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7068{
7069 AutoCaller autoCaller(this);
7070 HRESULT hrc = autoCaller.rc();
7071 if (SUCCEEDED(hrc))
7072 {
7073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7074 hrc = checkStateDependency(MutableStateDep);
7075 if (SUCCEEDED(hrc))
7076 {
7077 hrc = mHWData.backupEx();
7078 if (SUCCEEDED(hrc))
7079 {
7080 setModified(IsModified_MachineData);
7081 mHWData->mAutostart.uAutostartDelay = uDelay;
7082 }
7083 }
7084 }
7085 return hrc;
7086}
7087
7088STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7089{
7090 CheckComArgOutPointerValid(penmAutostopType);
7091 AutoCaller autoCaller(this);
7092 HRESULT hrc = autoCaller.rc();
7093 if (SUCCEEDED(hrc))
7094 {
7095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7096 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7097 }
7098 return hrc;
7099}
7100
7101STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7102{
7103 AutoCaller autoCaller(this);
7104 HRESULT hrc = autoCaller.rc();
7105 if (SUCCEEDED(hrc))
7106 {
7107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7108 hrc = checkStateDependency(MutableStateDep);
7109 if ( SUCCEEDED(hrc)
7110 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7111 {
7112 AutostartDb *autostartDb = mParent->getAutostartDb();
7113 int vrc;
7114
7115 if (enmAutostopType != AutostopType_Disabled)
7116 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7117 else
7118 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7119
7120 if (RT_SUCCESS(vrc))
7121 {
7122 hrc = mHWData.backupEx();
7123 if (SUCCEEDED(hrc))
7124 {
7125 setModified(IsModified_MachineData);
7126 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7127 }
7128 }
7129 else if (vrc == VERR_NOT_SUPPORTED)
7130 hrc = setError(VBOX_E_NOT_SUPPORTED,
7131 tr("The VM autostop feature is not supported on this platform"));
7132 else if (vrc == VERR_PATH_NOT_FOUND)
7133 hrc = setError(E_FAIL,
7134 tr("The path to the autostart database is not set"));
7135 else
7136 hrc = setError(E_UNEXPECTED,
7137 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7138 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7139 mUserData->s.strName.c_str(), vrc);
7140 }
7141 }
7142 return hrc;
7143}
7144
7145STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7146{
7147 CheckComArgOutPointerValid(aDefaultFrontend);
7148 AutoCaller autoCaller(this);
7149 HRESULT hrc = autoCaller.rc();
7150 if (SUCCEEDED(hrc))
7151 {
7152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7153 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7154 }
7155 return hrc;
7156}
7157
7158STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7159{
7160 CheckComArgStr(aDefaultFrontend);
7161 AutoCaller autoCaller(this);
7162 HRESULT hrc = autoCaller.rc();
7163 if (SUCCEEDED(hrc))
7164 {
7165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7166 hrc = checkStateDependency(MutableOrSavedStateDep);
7167 if (SUCCEEDED(hrc))
7168 {
7169 hrc = mHWData.backupEx();
7170 if (SUCCEEDED(hrc))
7171 {
7172 setModified(IsModified_MachineData);
7173 mHWData->mDefaultFrontend = aDefaultFrontend;
7174 }
7175 }
7176 }
7177 return hrc;
7178}
7179
7180
7181STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7182{
7183 LogFlowFuncEnter();
7184
7185 CheckComArgNotNull(pTarget);
7186 CheckComArgOutPointerValid(pProgress);
7187
7188 /* Convert the options. */
7189 RTCList<CloneOptions_T> optList;
7190 if (options != NULL)
7191 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7192
7193 if (optList.contains(CloneOptions_Link))
7194 {
7195 if (!isSnapshotMachine())
7196 return setError(E_INVALIDARG,
7197 tr("Linked clone can only be created from a snapshot"));
7198 if (mode != CloneMode_MachineState)
7199 return setError(E_INVALIDARG,
7200 tr("Linked clone can only be created for a single machine state"));
7201 }
7202 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7203
7204 AutoCaller autoCaller(this);
7205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7206
7207
7208 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7209
7210 HRESULT rc = pWorker->start(pProgress);
7211
7212 LogFlowFuncLeave();
7213
7214 return rc;
7215}
7216
7217// public methods for internal purposes
7218/////////////////////////////////////////////////////////////////////////////
7219
7220/**
7221 * Adds the given IsModified_* flag to the dirty flags of the machine.
7222 * This must be called either during loadSettings or under the machine write lock.
7223 * @param fl
7224 */
7225void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7226{
7227 mData->flModifications |= fl;
7228 if (fAllowStateModification && isStateModificationAllowed())
7229 mData->mCurrentStateModified = true;
7230}
7231
7232/**
7233 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7234 * care of the write locking.
7235 *
7236 * @param fModifications The flag to add.
7237 */
7238void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7239{
7240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7241 setModified(fModification, fAllowStateModification);
7242}
7243
7244/**
7245 * Saves the registry entry of this machine to the given configuration node.
7246 *
7247 * @param aEntryNode Node to save the registry entry to.
7248 *
7249 * @note locks this object for reading.
7250 */
7251HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7252{
7253 AutoLimitedCaller autoCaller(this);
7254 AssertComRCReturnRC(autoCaller.rc());
7255
7256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7257
7258 data.uuid = mData->mUuid;
7259 data.strSettingsFile = mData->m_strConfigFile;
7260
7261 return S_OK;
7262}
7263
7264/**
7265 * Calculates the absolute path of the given path taking the directory of the
7266 * machine settings file as the current directory.
7267 *
7268 * @param aPath Path to calculate the absolute path for.
7269 * @param aResult Where to put the result (used only on success, can be the
7270 * same Utf8Str instance as passed in @a aPath).
7271 * @return IPRT result.
7272 *
7273 * @note Locks this object for reading.
7274 */
7275int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7276{
7277 AutoCaller autoCaller(this);
7278 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7279
7280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7281
7282 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7283
7284 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7285
7286 strSettingsDir.stripFilename();
7287 char folder[RTPATH_MAX];
7288 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7289 if (RT_SUCCESS(vrc))
7290 aResult = folder;
7291
7292 return vrc;
7293}
7294
7295/**
7296 * Copies strSource to strTarget, making it relative to the machine folder
7297 * if it is a subdirectory thereof, or simply copying it otherwise.
7298 *
7299 * @param strSource Path to evaluate and copy.
7300 * @param strTarget Buffer to receive target path.
7301 *
7302 * @note Locks this object for reading.
7303 */
7304void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7305 Utf8Str &strTarget)
7306{
7307 AutoCaller autoCaller(this);
7308 AssertComRCReturn(autoCaller.rc(), (void)0);
7309
7310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7311
7312 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7313 // use strTarget as a temporary buffer to hold the machine settings dir
7314 strTarget = mData->m_strConfigFileFull;
7315 strTarget.stripFilename();
7316 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7317 {
7318 // is relative: then append what's left
7319 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7320 // for empty paths (only possible for subdirs) use "." to avoid
7321 // triggering default settings for not present config attributes.
7322 if (strTarget.isEmpty())
7323 strTarget = ".";
7324 }
7325 else
7326 // is not relative: then overwrite
7327 strTarget = strSource;
7328}
7329
7330/**
7331 * Returns the full path to the machine's log folder in the
7332 * \a aLogFolder argument.
7333 */
7334void Machine::getLogFolder(Utf8Str &aLogFolder)
7335{
7336 AutoCaller autoCaller(this);
7337 AssertComRCReturnVoid(autoCaller.rc());
7338
7339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7340
7341 char szTmp[RTPATH_MAX];
7342 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7343 if (RT_SUCCESS(vrc))
7344 {
7345 if (szTmp[0] && !mUserData.isNull())
7346 {
7347 char szTmp2[RTPATH_MAX];
7348 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7349 if (RT_SUCCESS(vrc))
7350 aLogFolder = BstrFmt("%s%c%s",
7351 szTmp2,
7352 RTPATH_DELIMITER,
7353 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7354 }
7355 else
7356 vrc = VERR_PATH_IS_RELATIVE;
7357 }
7358
7359 if (RT_FAILURE(vrc))
7360 {
7361 // fallback if VBOX_USER_LOGHOME is not set or invalid
7362 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7363 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7364 aLogFolder.append(RTPATH_DELIMITER);
7365 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7366 }
7367}
7368
7369/**
7370 * Returns the full path to the machine's log file for an given index.
7371 */
7372Utf8Str Machine::queryLogFilename(ULONG idx)
7373{
7374 Utf8Str logFolder;
7375 getLogFolder(logFolder);
7376 Assert(logFolder.length());
7377 Utf8Str log;
7378 if (idx == 0)
7379 log = Utf8StrFmt("%s%cVBox.log",
7380 logFolder.c_str(), RTPATH_DELIMITER);
7381 else
7382 log = Utf8StrFmt("%s%cVBox.log.%d",
7383 logFolder.c_str(), RTPATH_DELIMITER, idx);
7384 return log;
7385}
7386
7387/**
7388 * Composes a unique saved state filename based on the current system time. The filename is
7389 * granular to the second so this will work so long as no more than one snapshot is taken on
7390 * a machine per second.
7391 *
7392 * Before version 4.1, we used this formula for saved state files:
7393 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7394 * which no longer works because saved state files can now be shared between the saved state of the
7395 * "saved" machine and an online snapshot, and the following would cause problems:
7396 * 1) save machine
7397 * 2) create online snapshot from that machine state --> reusing saved state file
7398 * 3) save machine again --> filename would be reused, breaking the online snapshot
7399 *
7400 * So instead we now use a timestamp.
7401 *
7402 * @param str
7403 */
7404void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7405{
7406 AutoCaller autoCaller(this);
7407 AssertComRCReturnVoid(autoCaller.rc());
7408
7409 {
7410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7411 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7412 }
7413
7414 RTTIMESPEC ts;
7415 RTTimeNow(&ts);
7416 RTTIME time;
7417 RTTimeExplode(&time, &ts);
7418
7419 strStateFilePath += RTPATH_DELIMITER;
7420 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7421 time.i32Year, time.u8Month, time.u8MonthDay,
7422 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7423}
7424
7425/**
7426 * @note Locks this object for writing, calls the client process
7427 * (inside the lock).
7428 */
7429HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7430 const Utf8Str &strFrontend,
7431 const Utf8Str &strEnvironment,
7432 ProgressProxy *aProgress)
7433{
7434 LogFlowThisFuncEnter();
7435
7436 AssertReturn(aControl, E_FAIL);
7437 AssertReturn(aProgress, E_FAIL);
7438
7439 AutoCaller autoCaller(this);
7440 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7441
7442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7443
7444 if (!mData->mRegistered)
7445 return setError(E_UNEXPECTED,
7446 tr("The machine '%s' is not registered"),
7447 mUserData->s.strName.c_str());
7448
7449 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7450
7451 if ( mData->mSession.mState == SessionState_Locked
7452 || mData->mSession.mState == SessionState_Spawning
7453 || mData->mSession.mState == SessionState_Unlocking)
7454 return setError(VBOX_E_INVALID_OBJECT_STATE,
7455 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7456 mUserData->s.strName.c_str());
7457
7458 /* may not be busy */
7459 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7460
7461 /* get the path to the executable */
7462 char szPath[RTPATH_MAX];
7463 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7464 size_t sz = strlen(szPath);
7465 szPath[sz++] = RTPATH_DELIMITER;
7466 szPath[sz] = 0;
7467 char *cmd = szPath + sz;
7468 sz = RTPATH_MAX - sz;
7469
7470 int vrc = VINF_SUCCESS;
7471 RTPROCESS pid = NIL_RTPROCESS;
7472
7473 RTENV env = RTENV_DEFAULT;
7474
7475 if (!strEnvironment.isEmpty())
7476 {
7477 char *newEnvStr = NULL;
7478
7479 do
7480 {
7481 /* clone the current environment */
7482 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7483 AssertRCBreakStmt(vrc2, vrc = vrc2);
7484
7485 newEnvStr = RTStrDup(strEnvironment.c_str());
7486 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7487
7488 /* put new variables to the environment
7489 * (ignore empty variable names here since RTEnv API
7490 * intentionally doesn't do that) */
7491 char *var = newEnvStr;
7492 for (char *p = newEnvStr; *p; ++p)
7493 {
7494 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7495 {
7496 *p = '\0';
7497 if (*var)
7498 {
7499 char *val = strchr(var, '=');
7500 if (val)
7501 {
7502 *val++ = '\0';
7503 vrc2 = RTEnvSetEx(env, var, val);
7504 }
7505 else
7506 vrc2 = RTEnvUnsetEx(env, var);
7507 if (RT_FAILURE(vrc2))
7508 break;
7509 }
7510 var = p + 1;
7511 }
7512 }
7513 if (RT_SUCCESS(vrc2) && *var)
7514 vrc2 = RTEnvPutEx(env, var);
7515
7516 AssertRCBreakStmt(vrc2, vrc = vrc2);
7517 }
7518 while (0);
7519
7520 if (newEnvStr != NULL)
7521 RTStrFree(newEnvStr);
7522 }
7523
7524 /* Qt is default */
7525#ifdef VBOX_WITH_QTGUI
7526 if (strFrontend == "gui" || strFrontend == "GUI/Qt" || strFrontend == "")
7527 {
7528# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7529 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7530# else
7531 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7532# endif
7533 Assert(sz >= sizeof(VirtualBox_exe));
7534 strcpy(cmd, VirtualBox_exe);
7535
7536 Utf8Str idStr = mData->mUuid.toString();
7537 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7538 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7539 }
7540#else /* !VBOX_WITH_QTGUI */
7541 if (0)
7542 ;
7543#endif /* VBOX_WITH_QTGUI */
7544
7545 else
7546
7547#ifdef VBOX_WITH_VBOXSDL
7548 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7549 {
7550 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7551 Assert(sz >= sizeof(VBoxSDL_exe));
7552 strcpy(cmd, VBoxSDL_exe);
7553
7554 Utf8Str idStr = mData->mUuid.toString();
7555 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7556 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7557 }
7558#else /* !VBOX_WITH_VBOXSDL */
7559 if (0)
7560 ;
7561#endif /* !VBOX_WITH_VBOXSDL */
7562
7563 else
7564
7565#ifdef VBOX_WITH_HEADLESS
7566 if ( strFrontend == "headless"
7567 || strFrontend == "capture"
7568 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7569 )
7570 {
7571 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7572 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7573 * and a VM works even if the server has not been installed.
7574 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7575 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7576 * differently in 4.0 and 3.x.
7577 */
7578 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7579 Assert(sz >= sizeof(VBoxHeadless_exe));
7580 strcpy(cmd, VBoxHeadless_exe);
7581
7582 Utf8Str idStr = mData->mUuid.toString();
7583 /* Leave space for "--capture" arg. */
7584 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7585 "--startvm", idStr.c_str(),
7586 "--vrde", "config",
7587 0, /* For "--capture". */
7588 0 };
7589 if (strFrontend == "capture")
7590 {
7591 unsigned pos = RT_ELEMENTS(args) - 2;
7592 args[pos] = "--capture";
7593 }
7594 vrc = RTProcCreate(szPath, args, env,
7595#ifdef RT_OS_WINDOWS
7596 RTPROC_FLAGS_NO_WINDOW
7597#else
7598 0
7599#endif
7600 , &pid);
7601 }
7602#else /* !VBOX_WITH_HEADLESS */
7603 if (0)
7604 ;
7605#endif /* !VBOX_WITH_HEADLESS */
7606 else
7607 {
7608 RTEnvDestroy(env);
7609 return setError(E_INVALIDARG,
7610 tr("Invalid frontend name: '%s'"),
7611 strFrontend.c_str());
7612 }
7613
7614 RTEnvDestroy(env);
7615
7616 if (RT_FAILURE(vrc))
7617 return setError(VBOX_E_IPRT_ERROR,
7618 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7619 mUserData->s.strName.c_str(), vrc);
7620
7621 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7622
7623 /*
7624 * Note that we don't release the lock here before calling the client,
7625 * because it doesn't need to call us back if called with a NULL argument.
7626 * Releasing the lock here is dangerous because we didn't prepare the
7627 * launch data yet, but the client we've just started may happen to be
7628 * too fast and call openSession() that will fail (because of PID, etc.),
7629 * so that the Machine will never get out of the Spawning session state.
7630 */
7631
7632 /* inform the session that it will be a remote one */
7633 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7634 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7635 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7636
7637 if (FAILED(rc))
7638 {
7639 /* restore the session state */
7640 mData->mSession.mState = SessionState_Unlocked;
7641 /* The failure may occur w/o any error info (from RPC), so provide one */
7642 return setError(VBOX_E_VM_ERROR,
7643 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7644 }
7645
7646 /* attach launch data to the machine */
7647 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7648 mData->mSession.mRemoteControls.push_back(aControl);
7649 mData->mSession.mProgress = aProgress;
7650 mData->mSession.mPID = pid;
7651 mData->mSession.mState = SessionState_Spawning;
7652 mData->mSession.mType = strFrontend;
7653
7654 LogFlowThisFuncLeave();
7655 return S_OK;
7656}
7657
7658/**
7659 * Returns @c true if the given machine has an open direct session and returns
7660 * the session machine instance and additional session data (on some platforms)
7661 * if so.
7662 *
7663 * Note that when the method returns @c false, the arguments remain unchanged.
7664 *
7665 * @param aMachine Session machine object.
7666 * @param aControl Direct session control object (optional).
7667 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7668 *
7669 * @note locks this object for reading.
7670 */
7671#if defined(RT_OS_WINDOWS)
7672bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7673 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7674 HANDLE *aIPCSem /*= NULL*/,
7675 bool aAllowClosing /*= false*/)
7676#elif defined(RT_OS_OS2)
7677bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7678 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7679 HMTX *aIPCSem /*= NULL*/,
7680 bool aAllowClosing /*= false*/)
7681#else
7682bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7683 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7684 bool aAllowClosing /*= false*/)
7685#endif
7686{
7687 AutoLimitedCaller autoCaller(this);
7688 AssertComRCReturn(autoCaller.rc(), false);
7689
7690 /* just return false for inaccessible machines */
7691 if (autoCaller.state() != Ready)
7692 return false;
7693
7694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7695
7696 if ( mData->mSession.mState == SessionState_Locked
7697 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7698 )
7699 {
7700 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7701
7702 aMachine = mData->mSession.mMachine;
7703
7704 if (aControl != NULL)
7705 *aControl = mData->mSession.mDirectControl;
7706
7707#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7708 /* Additional session data */
7709 if (aIPCSem != NULL)
7710 *aIPCSem = aMachine->mIPCSem;
7711#endif
7712 return true;
7713 }
7714
7715 return false;
7716}
7717
7718/**
7719 * Returns @c true if the given machine has an spawning direct session and
7720 * returns and additional session data (on some platforms) if so.
7721 *
7722 * Note that when the method returns @c false, the arguments remain unchanged.
7723 *
7724 * @param aPID PID of the spawned direct session process.
7725 *
7726 * @note locks this object for reading.
7727 */
7728#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7729bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7730#else
7731bool Machine::isSessionSpawning()
7732#endif
7733{
7734 AutoLimitedCaller autoCaller(this);
7735 AssertComRCReturn(autoCaller.rc(), false);
7736
7737 /* just return false for inaccessible machines */
7738 if (autoCaller.state() != Ready)
7739 return false;
7740
7741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7742
7743 if (mData->mSession.mState == SessionState_Spawning)
7744 {
7745#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7746 /* Additional session data */
7747 if (aPID != NULL)
7748 {
7749 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7750 *aPID = mData->mSession.mPID;
7751 }
7752#endif
7753 return true;
7754 }
7755
7756 return false;
7757}
7758
7759/**
7760 * Called from the client watcher thread to check for unexpected client process
7761 * death during Session_Spawning state (e.g. before it successfully opened a
7762 * direct session).
7763 *
7764 * On Win32 and on OS/2, this method is called only when we've got the
7765 * direct client's process termination notification, so it always returns @c
7766 * true.
7767 *
7768 * On other platforms, this method returns @c true if the client process is
7769 * terminated and @c false if it's still alive.
7770 *
7771 * @note Locks this object for writing.
7772 */
7773bool Machine::checkForSpawnFailure()
7774{
7775 AutoCaller autoCaller(this);
7776 if (!autoCaller.isOk())
7777 {
7778 /* nothing to do */
7779 LogFlowThisFunc(("Already uninitialized!\n"));
7780 return true;
7781 }
7782
7783 /* VirtualBox::addProcessToReap() needs a write lock */
7784 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7785
7786 if (mData->mSession.mState != SessionState_Spawning)
7787 {
7788 /* nothing to do */
7789 LogFlowThisFunc(("Not spawning any more!\n"));
7790 return true;
7791 }
7792
7793 HRESULT rc = S_OK;
7794
7795#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7796
7797 /* the process was already unexpectedly terminated, we just need to set an
7798 * error and finalize session spawning */
7799 rc = setError(E_FAIL,
7800 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7801 getName().c_str());
7802#else
7803
7804 /* PID not yet initialized, skip check. */
7805 if (mData->mSession.mPID == NIL_RTPROCESS)
7806 return false;
7807
7808 RTPROCSTATUS status;
7809 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7810 &status);
7811
7812 if (vrc != VERR_PROCESS_RUNNING)
7813 {
7814 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7815 rc = setError(E_FAIL,
7816 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7817 getName().c_str(), status.iStatus);
7818 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7819 rc = setError(E_FAIL,
7820 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7821 getName().c_str(), status.iStatus);
7822 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7823 rc = setError(E_FAIL,
7824 tr("The virtual machine '%s' has terminated abnormally"),
7825 getName().c_str(), status.iStatus);
7826 else
7827 rc = setError(E_FAIL,
7828 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7829 getName().c_str(), rc);
7830 }
7831
7832#endif
7833
7834 if (FAILED(rc))
7835 {
7836 /* Close the remote session, remove the remote control from the list
7837 * and reset session state to Closed (@note keep the code in sync with
7838 * the relevant part in checkForSpawnFailure()). */
7839
7840 Assert(mData->mSession.mRemoteControls.size() == 1);
7841 if (mData->mSession.mRemoteControls.size() == 1)
7842 {
7843 ErrorInfoKeeper eik;
7844 mData->mSession.mRemoteControls.front()->Uninitialize();
7845 }
7846
7847 mData->mSession.mRemoteControls.clear();
7848 mData->mSession.mState = SessionState_Unlocked;
7849
7850 /* finalize the progress after setting the state */
7851 if (!mData->mSession.mProgress.isNull())
7852 {
7853 mData->mSession.mProgress->notifyComplete(rc);
7854 mData->mSession.mProgress.setNull();
7855 }
7856
7857 mParent->addProcessToReap(mData->mSession.mPID);
7858 mData->mSession.mPID = NIL_RTPROCESS;
7859
7860 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7861 return true;
7862 }
7863
7864 return false;
7865}
7866
7867/**
7868 * Checks whether the machine can be registered. If so, commits and saves
7869 * all settings.
7870 *
7871 * @note Must be called from mParent's write lock. Locks this object and
7872 * children for writing.
7873 */
7874HRESULT Machine::prepareRegister()
7875{
7876 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7877
7878 AutoLimitedCaller autoCaller(this);
7879 AssertComRCReturnRC(autoCaller.rc());
7880
7881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7882
7883 /* wait for state dependents to drop to zero */
7884 ensureNoStateDependencies();
7885
7886 if (!mData->mAccessible)
7887 return setError(VBOX_E_INVALID_OBJECT_STATE,
7888 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7889 mUserData->s.strName.c_str(),
7890 mData->mUuid.toString().c_str());
7891
7892 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7893
7894 if (mData->mRegistered)
7895 return setError(VBOX_E_INVALID_OBJECT_STATE,
7896 tr("The machine '%s' with UUID {%s} is already registered"),
7897 mUserData->s.strName.c_str(),
7898 mData->mUuid.toString().c_str());
7899
7900 HRESULT rc = S_OK;
7901
7902 // Ensure the settings are saved. If we are going to be registered and
7903 // no config file exists yet, create it by calling saveSettings() too.
7904 if ( (mData->flModifications)
7905 || (!mData->pMachineConfigFile->fileExists())
7906 )
7907 {
7908 rc = saveSettings(NULL);
7909 // no need to check whether VirtualBox.xml needs saving too since
7910 // we can't have a machine XML file rename pending
7911 if (FAILED(rc)) return rc;
7912 }
7913
7914 /* more config checking goes here */
7915
7916 if (SUCCEEDED(rc))
7917 {
7918 /* we may have had implicit modifications we want to fix on success */
7919 commit();
7920
7921 mData->mRegistered = true;
7922 }
7923 else
7924 {
7925 /* we may have had implicit modifications we want to cancel on failure*/
7926 rollback(false /* aNotify */);
7927 }
7928
7929 return rc;
7930}
7931
7932/**
7933 * Increases the number of objects dependent on the machine state or on the
7934 * registered state. Guarantees that these two states will not change at least
7935 * until #releaseStateDependency() is called.
7936 *
7937 * Depending on the @a aDepType value, additional state checks may be made.
7938 * These checks will set extended error info on failure. See
7939 * #checkStateDependency() for more info.
7940 *
7941 * If this method returns a failure, the dependency is not added and the caller
7942 * is not allowed to rely on any particular machine state or registration state
7943 * value and may return the failed result code to the upper level.
7944 *
7945 * @param aDepType Dependency type to add.
7946 * @param aState Current machine state (NULL if not interested).
7947 * @param aRegistered Current registered state (NULL if not interested).
7948 *
7949 * @note Locks this object for writing.
7950 */
7951HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7952 MachineState_T *aState /* = NULL */,
7953 BOOL *aRegistered /* = NULL */)
7954{
7955 AutoCaller autoCaller(this);
7956 AssertComRCReturnRC(autoCaller.rc());
7957
7958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7959
7960 HRESULT rc = checkStateDependency(aDepType);
7961 if (FAILED(rc)) return rc;
7962
7963 {
7964 if (mData->mMachineStateChangePending != 0)
7965 {
7966 /* ensureNoStateDependencies() is waiting for state dependencies to
7967 * drop to zero so don't add more. It may make sense to wait a bit
7968 * and retry before reporting an error (since the pending state
7969 * transition should be really quick) but let's just assert for
7970 * now to see if it ever happens on practice. */
7971
7972 AssertFailed();
7973
7974 return setError(E_ACCESSDENIED,
7975 tr("Machine state change is in progress. Please retry the operation later."));
7976 }
7977
7978 ++mData->mMachineStateDeps;
7979 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7980 }
7981
7982 if (aState)
7983 *aState = mData->mMachineState;
7984 if (aRegistered)
7985 *aRegistered = mData->mRegistered;
7986
7987 return S_OK;
7988}
7989
7990/**
7991 * Decreases the number of objects dependent on the machine state.
7992 * Must always complete the #addStateDependency() call after the state
7993 * dependency is no more necessary.
7994 */
7995void Machine::releaseStateDependency()
7996{
7997 AutoCaller autoCaller(this);
7998 AssertComRCReturnVoid(autoCaller.rc());
7999
8000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8001
8002 /* releaseStateDependency() w/o addStateDependency()? */
8003 AssertReturnVoid(mData->mMachineStateDeps != 0);
8004 -- mData->mMachineStateDeps;
8005
8006 if (mData->mMachineStateDeps == 0)
8007 {
8008 /* inform ensureNoStateDependencies() that there are no more deps */
8009 if (mData->mMachineStateChangePending != 0)
8010 {
8011 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8012 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8013 }
8014 }
8015}
8016
8017Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8018{
8019 /* start with nothing found */
8020 Utf8Str strResult("");
8021
8022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8023
8024 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8025 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8026 // found:
8027 strResult = it->second; // source is a Utf8Str
8028
8029 return strResult;
8030}
8031
8032// protected methods
8033/////////////////////////////////////////////////////////////////////////////
8034
8035/**
8036 * Performs machine state checks based on the @a aDepType value. If a check
8037 * fails, this method will set extended error info, otherwise it will return
8038 * S_OK. It is supposed, that on failure, the caller will immediately return
8039 * the return value of this method to the upper level.
8040 *
8041 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8042 *
8043 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8044 * current state of this machine object allows to change settings of the
8045 * machine (i.e. the machine is not registered, or registered but not running
8046 * and not saved). It is useful to call this method from Machine setters
8047 * before performing any change.
8048 *
8049 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8050 * as for MutableStateDep except that if the machine is saved, S_OK is also
8051 * returned. This is useful in setters which allow changing machine
8052 * properties when it is in the saved state.
8053 *
8054 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8055 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8056 * Aborted).
8057 *
8058 * @param aDepType Dependency type to check.
8059 *
8060 * @note Non Machine based classes should use #addStateDependency() and
8061 * #releaseStateDependency() methods or the smart AutoStateDependency
8062 * template.
8063 *
8064 * @note This method must be called from under this object's read or write
8065 * lock.
8066 */
8067HRESULT Machine::checkStateDependency(StateDependency aDepType)
8068{
8069 switch (aDepType)
8070 {
8071 case AnyStateDep:
8072 {
8073 break;
8074 }
8075 case MutableStateDep:
8076 {
8077 if ( mData->mRegistered
8078 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8079 || ( mData->mMachineState != MachineState_Paused
8080 && mData->mMachineState != MachineState_Running
8081 && mData->mMachineState != MachineState_Aborted
8082 && mData->mMachineState != MachineState_Teleported
8083 && mData->mMachineState != MachineState_PoweredOff
8084 )
8085 )
8086 )
8087 return setError(VBOX_E_INVALID_VM_STATE,
8088 tr("The machine is not mutable (state is %s)"),
8089 Global::stringifyMachineState(mData->mMachineState));
8090 break;
8091 }
8092 case MutableOrSavedStateDep:
8093 {
8094 if ( mData->mRegistered
8095 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8096 || ( mData->mMachineState != MachineState_Paused
8097 && mData->mMachineState != MachineState_Running
8098 && mData->mMachineState != MachineState_Aborted
8099 && mData->mMachineState != MachineState_Teleported
8100 && mData->mMachineState != MachineState_Saved
8101 && mData->mMachineState != MachineState_PoweredOff
8102 )
8103 )
8104 )
8105 return setError(VBOX_E_INVALID_VM_STATE,
8106 tr("The machine is not mutable (state is %s)"),
8107 Global::stringifyMachineState(mData->mMachineState));
8108 break;
8109 }
8110 case OfflineStateDep:
8111 {
8112 if ( mData->mRegistered
8113 && ( !isSessionMachine()
8114 || ( mData->mMachineState != MachineState_PoweredOff
8115 && mData->mMachineState != MachineState_Saved
8116 && mData->mMachineState != MachineState_Aborted
8117 && mData->mMachineState != MachineState_Teleported
8118 )
8119 )
8120 )
8121 return setError(VBOX_E_INVALID_VM_STATE,
8122 tr("The machine is not offline (state is %s)"),
8123 Global::stringifyMachineState(mData->mMachineState));
8124 break;
8125 }
8126 }
8127
8128 return S_OK;
8129}
8130
8131/**
8132 * Helper to initialize all associated child objects and allocate data
8133 * structures.
8134 *
8135 * This method must be called as a part of the object's initialization procedure
8136 * (usually done in the #init() method).
8137 *
8138 * @note Must be called only from #init() or from #registeredInit().
8139 */
8140HRESULT Machine::initDataAndChildObjects()
8141{
8142 AutoCaller autoCaller(this);
8143 AssertComRCReturnRC(autoCaller.rc());
8144 AssertComRCReturn(autoCaller.state() == InInit ||
8145 autoCaller.state() == Limited, E_FAIL);
8146
8147 AssertReturn(!mData->mAccessible, E_FAIL);
8148
8149 /* allocate data structures */
8150 mSSData.allocate();
8151 mUserData.allocate();
8152 mHWData.allocate();
8153 mMediaData.allocate();
8154 mStorageControllers.allocate();
8155
8156 /* initialize mOSTypeId */
8157 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8158
8159 /* create associated BIOS settings object */
8160 unconst(mBIOSSettings).createObject();
8161 mBIOSSettings->init(this);
8162
8163 /* create an associated VRDE object (default is disabled) */
8164 unconst(mVRDEServer).createObject();
8165 mVRDEServer->init(this);
8166
8167 /* create associated serial port objects */
8168 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8169 {
8170 unconst(mSerialPorts[slot]).createObject();
8171 mSerialPorts[slot]->init(this, slot);
8172 }
8173
8174 /* create associated parallel port objects */
8175 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8176 {
8177 unconst(mParallelPorts[slot]).createObject();
8178 mParallelPorts[slot]->init(this, slot);
8179 }
8180
8181 /* create the audio adapter object (always present, default is disabled) */
8182 unconst(mAudioAdapter).createObject();
8183 mAudioAdapter->init(this);
8184
8185 /* create the USB controller object (always present, default is disabled) */
8186 unconst(mUSBController).createObject();
8187 mUSBController->init(this);
8188
8189 /* create associated network adapter objects */
8190 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8191 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8192 {
8193 unconst(mNetworkAdapters[slot]).createObject();
8194 mNetworkAdapters[slot]->init(this, slot);
8195 }
8196
8197 /* create the bandwidth control */
8198 unconst(mBandwidthControl).createObject();
8199 mBandwidthControl->init(this);
8200
8201 return S_OK;
8202}
8203
8204/**
8205 * Helper to uninitialize all associated child objects and to free all data
8206 * structures.
8207 *
8208 * This method must be called as a part of the object's uninitialization
8209 * procedure (usually done in the #uninit() method).
8210 *
8211 * @note Must be called only from #uninit() or from #registeredInit().
8212 */
8213void Machine::uninitDataAndChildObjects()
8214{
8215 AutoCaller autoCaller(this);
8216 AssertComRCReturnVoid(autoCaller.rc());
8217 AssertComRCReturnVoid( autoCaller.state() == InUninit
8218 || autoCaller.state() == Limited);
8219
8220 /* tell all our other child objects we've been uninitialized */
8221 if (mBandwidthControl)
8222 {
8223 mBandwidthControl->uninit();
8224 unconst(mBandwidthControl).setNull();
8225 }
8226
8227 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8228 {
8229 if (mNetworkAdapters[slot])
8230 {
8231 mNetworkAdapters[slot]->uninit();
8232 unconst(mNetworkAdapters[slot]).setNull();
8233 }
8234 }
8235
8236 if (mUSBController)
8237 {
8238 mUSBController->uninit();
8239 unconst(mUSBController).setNull();
8240 }
8241
8242 if (mAudioAdapter)
8243 {
8244 mAudioAdapter->uninit();
8245 unconst(mAudioAdapter).setNull();
8246 }
8247
8248 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8249 {
8250 if (mParallelPorts[slot])
8251 {
8252 mParallelPorts[slot]->uninit();
8253 unconst(mParallelPorts[slot]).setNull();
8254 }
8255 }
8256
8257 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8258 {
8259 if (mSerialPorts[slot])
8260 {
8261 mSerialPorts[slot]->uninit();
8262 unconst(mSerialPorts[slot]).setNull();
8263 }
8264 }
8265
8266 if (mVRDEServer)
8267 {
8268 mVRDEServer->uninit();
8269 unconst(mVRDEServer).setNull();
8270 }
8271
8272 if (mBIOSSettings)
8273 {
8274 mBIOSSettings->uninit();
8275 unconst(mBIOSSettings).setNull();
8276 }
8277
8278 /* Deassociate media (only when a real Machine or a SnapshotMachine
8279 * instance is uninitialized; SessionMachine instances refer to real
8280 * Machine media). This is necessary for a clean re-initialization of
8281 * the VM after successfully re-checking the accessibility state. Note
8282 * that in case of normal Machine or SnapshotMachine uninitialization (as
8283 * a result of unregistering or deleting the snapshot), outdated media
8284 * attachments will already be uninitialized and deleted, so this
8285 * code will not affect them. */
8286 if ( !!mMediaData
8287 && (!isSessionMachine())
8288 )
8289 {
8290 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8291 it != mMediaData->mAttachments.end();
8292 ++it)
8293 {
8294 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8295 if (pMedium.isNull())
8296 continue;
8297 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8298 AssertComRC(rc);
8299 }
8300 }
8301
8302 if (!isSessionMachine() && !isSnapshotMachine())
8303 {
8304 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8305 if (mData->mFirstSnapshot)
8306 {
8307 // snapshots tree is protected by machine write lock; strictly
8308 // this isn't necessary here since we're deleting the entire
8309 // machine, but otherwise we assert in Snapshot::uninit()
8310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8311 mData->mFirstSnapshot->uninit();
8312 mData->mFirstSnapshot.setNull();
8313 }
8314
8315 mData->mCurrentSnapshot.setNull();
8316 }
8317
8318 /* free data structures (the essential mData structure is not freed here
8319 * since it may be still in use) */
8320 mMediaData.free();
8321 mStorageControllers.free();
8322 mHWData.free();
8323 mUserData.free();
8324 mSSData.free();
8325}
8326
8327/**
8328 * Returns a pointer to the Machine object for this machine that acts like a
8329 * parent for complex machine data objects such as shared folders, etc.
8330 *
8331 * For primary Machine objects and for SnapshotMachine objects, returns this
8332 * object's pointer itself. For SessionMachine objects, returns the peer
8333 * (primary) machine pointer.
8334 */
8335Machine* Machine::getMachine()
8336{
8337 if (isSessionMachine())
8338 return (Machine*)mPeer;
8339 return this;
8340}
8341
8342/**
8343 * Makes sure that there are no machine state dependents. If necessary, waits
8344 * for the number of dependents to drop to zero.
8345 *
8346 * Make sure this method is called from under this object's write lock to
8347 * guarantee that no new dependents may be added when this method returns
8348 * control to the caller.
8349 *
8350 * @note Locks this object for writing. The lock will be released while waiting
8351 * (if necessary).
8352 *
8353 * @warning To be used only in methods that change the machine state!
8354 */
8355void Machine::ensureNoStateDependencies()
8356{
8357 AssertReturnVoid(isWriteLockOnCurrentThread());
8358
8359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8360
8361 /* Wait for all state dependents if necessary */
8362 if (mData->mMachineStateDeps != 0)
8363 {
8364 /* lazy semaphore creation */
8365 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8366 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8367
8368 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8369 mData->mMachineStateDeps));
8370
8371 ++mData->mMachineStateChangePending;
8372
8373 /* reset the semaphore before waiting, the last dependent will signal
8374 * it */
8375 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8376
8377 alock.release();
8378
8379 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8380
8381 alock.acquire();
8382
8383 -- mData->mMachineStateChangePending;
8384 }
8385}
8386
8387/**
8388 * Changes the machine state and informs callbacks.
8389 *
8390 * This method is not intended to fail so it either returns S_OK or asserts (and
8391 * returns a failure).
8392 *
8393 * @note Locks this object for writing.
8394 */
8395HRESULT Machine::setMachineState(MachineState_T aMachineState)
8396{
8397 LogFlowThisFuncEnter();
8398 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8399
8400 AutoCaller autoCaller(this);
8401 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8402
8403 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8404
8405 /* wait for state dependents to drop to zero */
8406 ensureNoStateDependencies();
8407
8408 if (mData->mMachineState != aMachineState)
8409 {
8410 mData->mMachineState = aMachineState;
8411
8412 RTTimeNow(&mData->mLastStateChange);
8413
8414 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8415 }
8416
8417 LogFlowThisFuncLeave();
8418 return S_OK;
8419}
8420
8421/**
8422 * Searches for a shared folder with the given logical name
8423 * in the collection of shared folders.
8424 *
8425 * @param aName logical name of the shared folder
8426 * @param aSharedFolder where to return the found object
8427 * @param aSetError whether to set the error info if the folder is
8428 * not found
8429 * @return
8430 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8431 *
8432 * @note
8433 * must be called from under the object's lock!
8434 */
8435HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8436 ComObjPtr<SharedFolder> &aSharedFolder,
8437 bool aSetError /* = false */)
8438{
8439 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8440 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8441 it != mHWData->mSharedFolders.end();
8442 ++it)
8443 {
8444 SharedFolder *pSF = *it;
8445 AutoCaller autoCaller(pSF);
8446 if (pSF->getName() == aName)
8447 {
8448 aSharedFolder = pSF;
8449 rc = S_OK;
8450 break;
8451 }
8452 }
8453
8454 if (aSetError && FAILED(rc))
8455 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8456
8457 return rc;
8458}
8459
8460/**
8461 * Initializes all machine instance data from the given settings structures
8462 * from XML. The exception is the machine UUID which needs special handling
8463 * depending on the caller's use case, so the caller needs to set that herself.
8464 *
8465 * This gets called in several contexts during machine initialization:
8466 *
8467 * -- When machine XML exists on disk already and needs to be loaded into memory,
8468 * for example, from registeredInit() to load all registered machines on
8469 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8470 * attached to the machine should be part of some media registry already.
8471 *
8472 * -- During OVF import, when a machine config has been constructed from an
8473 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8474 * ensure that the media listed as attachments in the config (which have
8475 * been imported from the OVF) receive the correct registry ID.
8476 *
8477 * -- During VM cloning.
8478 *
8479 * @param config Machine settings from XML.
8480 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8481 * @return
8482 */
8483HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8484 const Guid *puuidRegistry)
8485{
8486 // copy name, description, OS type, teleporter, UTC etc.
8487 mUserData->s = config.machineUserData;
8488
8489 // look up the object by Id to check it is valid
8490 ComPtr<IGuestOSType> guestOSType;
8491 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8492 guestOSType.asOutParam());
8493 if (FAILED(rc)) return rc;
8494
8495 // stateFile (optional)
8496 if (config.strStateFile.isEmpty())
8497 mSSData->strStateFilePath.setNull();
8498 else
8499 {
8500 Utf8Str stateFilePathFull(config.strStateFile);
8501 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8502 if (RT_FAILURE(vrc))
8503 return setError(E_FAIL,
8504 tr("Invalid saved state file path '%s' (%Rrc)"),
8505 config.strStateFile.c_str(),
8506 vrc);
8507 mSSData->strStateFilePath = stateFilePathFull;
8508 }
8509
8510 // snapshot folder needs special processing so set it again
8511 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8512 if (FAILED(rc)) return rc;
8513
8514 /* Copy the extra data items (Not in any case config is already the same as
8515 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8516 * make sure the extra data map is copied). */
8517 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8518
8519 /* currentStateModified (optional, default is true) */
8520 mData->mCurrentStateModified = config.fCurrentStateModified;
8521
8522 mData->mLastStateChange = config.timeLastStateChange;
8523
8524 /*
8525 * note: all mUserData members must be assigned prior this point because
8526 * we need to commit changes in order to let mUserData be shared by all
8527 * snapshot machine instances.
8528 */
8529 mUserData.commitCopy();
8530
8531 // machine registry, if present (must be loaded before snapshots)
8532 if (config.canHaveOwnMediaRegistry())
8533 {
8534 // determine machine folder
8535 Utf8Str strMachineFolder = getSettingsFileFull();
8536 strMachineFolder.stripFilename();
8537 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8538 config.mediaRegistry,
8539 strMachineFolder);
8540 if (FAILED(rc)) return rc;
8541 }
8542
8543 /* Snapshot node (optional) */
8544 size_t cRootSnapshots;
8545 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8546 {
8547 // there must be only one root snapshot
8548 Assert(cRootSnapshots == 1);
8549
8550 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8551
8552 rc = loadSnapshot(snap,
8553 config.uuidCurrentSnapshot,
8554 NULL); // no parent == first snapshot
8555 if (FAILED(rc)) return rc;
8556 }
8557
8558 // hardware data
8559 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8560 if (FAILED(rc)) return rc;
8561
8562 // load storage controllers
8563 rc = loadStorageControllers(config.storageMachine,
8564 puuidRegistry,
8565 NULL /* puuidSnapshot */);
8566 if (FAILED(rc)) return rc;
8567
8568 /*
8569 * NOTE: the assignment below must be the last thing to do,
8570 * otherwise it will be not possible to change the settings
8571 * somewhere in the code above because all setters will be
8572 * blocked by checkStateDependency(MutableStateDep).
8573 */
8574
8575 /* set the machine state to Aborted or Saved when appropriate */
8576 if (config.fAborted)
8577 {
8578 mSSData->strStateFilePath.setNull();
8579
8580 /* no need to use setMachineState() during init() */
8581 mData->mMachineState = MachineState_Aborted;
8582 }
8583 else if (!mSSData->strStateFilePath.isEmpty())
8584 {
8585 /* no need to use setMachineState() during init() */
8586 mData->mMachineState = MachineState_Saved;
8587 }
8588
8589 // after loading settings, we are no longer different from the XML on disk
8590 mData->flModifications = 0;
8591
8592 return S_OK;
8593}
8594
8595/**
8596 * Recursively loads all snapshots starting from the given.
8597 *
8598 * @param aNode <Snapshot> node.
8599 * @param aCurSnapshotId Current snapshot ID from the settings file.
8600 * @param aParentSnapshot Parent snapshot.
8601 */
8602HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8603 const Guid &aCurSnapshotId,
8604 Snapshot *aParentSnapshot)
8605{
8606 AssertReturn(!isSnapshotMachine(), E_FAIL);
8607 AssertReturn(!isSessionMachine(), E_FAIL);
8608
8609 HRESULT rc = S_OK;
8610
8611 Utf8Str strStateFile;
8612 if (!data.strStateFile.isEmpty())
8613 {
8614 /* optional */
8615 strStateFile = data.strStateFile;
8616 int vrc = calculateFullPath(strStateFile, strStateFile);
8617 if (RT_FAILURE(vrc))
8618 return setError(E_FAIL,
8619 tr("Invalid saved state file path '%s' (%Rrc)"),
8620 strStateFile.c_str(),
8621 vrc);
8622 }
8623
8624 /* create a snapshot machine object */
8625 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8626 pSnapshotMachine.createObject();
8627 rc = pSnapshotMachine->initFromSettings(this,
8628 data.hardware,
8629 &data.debugging,
8630 &data.autostart,
8631 data.storage,
8632 data.uuid.ref(),
8633 strStateFile);
8634 if (FAILED(rc)) return rc;
8635
8636 /* create a snapshot object */
8637 ComObjPtr<Snapshot> pSnapshot;
8638 pSnapshot.createObject();
8639 /* initialize the snapshot */
8640 rc = pSnapshot->init(mParent, // VirtualBox object
8641 data.uuid,
8642 data.strName,
8643 data.strDescription,
8644 data.timestamp,
8645 pSnapshotMachine,
8646 aParentSnapshot);
8647 if (FAILED(rc)) return rc;
8648
8649 /* memorize the first snapshot if necessary */
8650 if (!mData->mFirstSnapshot)
8651 mData->mFirstSnapshot = pSnapshot;
8652
8653 /* memorize the current snapshot when appropriate */
8654 if ( !mData->mCurrentSnapshot
8655 && pSnapshot->getId() == aCurSnapshotId
8656 )
8657 mData->mCurrentSnapshot = pSnapshot;
8658
8659 // now create the children
8660 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8661 it != data.llChildSnapshots.end();
8662 ++it)
8663 {
8664 const settings::Snapshot &childData = *it;
8665 // recurse
8666 rc = loadSnapshot(childData,
8667 aCurSnapshotId,
8668 pSnapshot); // parent = the one we created above
8669 if (FAILED(rc)) return rc;
8670 }
8671
8672 return rc;
8673}
8674
8675/**
8676 * Loads settings into mHWData.
8677 *
8678 * @param data Reference to the hardware settings.
8679 * @param pDbg Pointer to the debugging settings.
8680 * @param pAutostart Pointer to the autostart settings.
8681 */
8682HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8683 const settings::Autostart *pAutostart)
8684{
8685 AssertReturn(!isSessionMachine(), E_FAIL);
8686
8687 HRESULT rc = S_OK;
8688
8689 try
8690 {
8691 /* The hardware version attribute (optional). */
8692 mHWData->mHWVersion = data.strVersion;
8693 mHWData->mHardwareUUID = data.uuid;
8694
8695 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8696 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8697 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8698 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8699 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8700 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8701 mHWData->mPAEEnabled = data.fPAE;
8702 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8703 mHWData->mLongMode = data.enmLongMode;
8704 mHWData->mCPUCount = data.cCPUs;
8705 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8706 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8707
8708 // cpu
8709 if (mHWData->mCPUHotPlugEnabled)
8710 {
8711 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8712 it != data.llCpus.end();
8713 ++it)
8714 {
8715 const settings::Cpu &cpu = *it;
8716
8717 mHWData->mCPUAttached[cpu.ulId] = true;
8718 }
8719 }
8720
8721 // cpuid leafs
8722 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8723 it != data.llCpuIdLeafs.end();
8724 ++it)
8725 {
8726 const settings::CpuIdLeaf &leaf = *it;
8727
8728 switch (leaf.ulId)
8729 {
8730 case 0x0:
8731 case 0x1:
8732 case 0x2:
8733 case 0x3:
8734 case 0x4:
8735 case 0x5:
8736 case 0x6:
8737 case 0x7:
8738 case 0x8:
8739 case 0x9:
8740 case 0xA:
8741 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8742 break;
8743
8744 case 0x80000000:
8745 case 0x80000001:
8746 case 0x80000002:
8747 case 0x80000003:
8748 case 0x80000004:
8749 case 0x80000005:
8750 case 0x80000006:
8751 case 0x80000007:
8752 case 0x80000008:
8753 case 0x80000009:
8754 case 0x8000000A:
8755 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8756 break;
8757
8758 default:
8759 /* just ignore */
8760 break;
8761 }
8762 }
8763
8764 mHWData->mMemorySize = data.ulMemorySizeMB;
8765 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8766
8767 // boot order
8768 for (size_t i = 0;
8769 i < RT_ELEMENTS(mHWData->mBootOrder);
8770 i++)
8771 {
8772 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8773 if (it == data.mapBootOrder.end())
8774 mHWData->mBootOrder[i] = DeviceType_Null;
8775 else
8776 mHWData->mBootOrder[i] = it->second;
8777 }
8778
8779 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8780 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8781 mHWData->mMonitorCount = data.cMonitors;
8782 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8783 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8784 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8785 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8786 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8787 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8788 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8789 mHWData->mFirmwareType = data.firmwareType;
8790 mHWData->mPointingHIDType = data.pointingHIDType;
8791 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8792 mHWData->mChipsetType = data.chipsetType;
8793 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8794 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8795 mHWData->mHPETEnabled = data.fHPETEnabled;
8796
8797 /* VRDEServer */
8798 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8799 if (FAILED(rc)) return rc;
8800
8801 /* BIOS */
8802 rc = mBIOSSettings->loadSettings(data.biosSettings);
8803 if (FAILED(rc)) return rc;
8804
8805 // Bandwidth control (must come before network adapters)
8806 rc = mBandwidthControl->loadSettings(data.ioSettings);
8807 if (FAILED(rc)) return rc;
8808
8809 /* USB Controller */
8810 rc = mUSBController->loadSettings(data.usbController);
8811 if (FAILED(rc)) return rc;
8812
8813 // network adapters
8814 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8815 uint32_t oldCount = mNetworkAdapters.size();
8816 if (newCount > oldCount)
8817 {
8818 mNetworkAdapters.resize(newCount);
8819 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8820 {
8821 unconst(mNetworkAdapters[slot]).createObject();
8822 mNetworkAdapters[slot]->init(this, slot);
8823 }
8824 }
8825 else if (newCount < oldCount)
8826 mNetworkAdapters.resize(newCount);
8827 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8828 it != data.llNetworkAdapters.end();
8829 ++it)
8830 {
8831 const settings::NetworkAdapter &nic = *it;
8832
8833 /* slot unicity is guaranteed by XML Schema */
8834 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8835 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8836 if (FAILED(rc)) return rc;
8837 }
8838
8839 // serial ports
8840 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8841 it != data.llSerialPorts.end();
8842 ++it)
8843 {
8844 const settings::SerialPort &s = *it;
8845
8846 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8847 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8848 if (FAILED(rc)) return rc;
8849 }
8850
8851 // parallel ports (optional)
8852 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8853 it != data.llParallelPorts.end();
8854 ++it)
8855 {
8856 const settings::ParallelPort &p = *it;
8857
8858 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8859 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8860 if (FAILED(rc)) return rc;
8861 }
8862
8863 /* AudioAdapter */
8864 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8865 if (FAILED(rc)) return rc;
8866
8867 /* Shared folders */
8868 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8869 it != data.llSharedFolders.end();
8870 ++it)
8871 {
8872 const settings::SharedFolder &sf = *it;
8873
8874 ComObjPtr<SharedFolder> sharedFolder;
8875 /* Check for double entries. Not allowed! */
8876 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8877 if (SUCCEEDED(rc))
8878 return setError(VBOX_E_OBJECT_IN_USE,
8879 tr("Shared folder named '%s' already exists"),
8880 sf.strName.c_str());
8881
8882 /* Create the new shared folder. Don't break on error. This will be
8883 * reported when the machine starts. */
8884 sharedFolder.createObject();
8885 rc = sharedFolder->init(getMachine(),
8886 sf.strName,
8887 sf.strHostPath,
8888 RT_BOOL(sf.fWritable),
8889 RT_BOOL(sf.fAutoMount),
8890 false /* fFailOnError */);
8891 if (FAILED(rc)) return rc;
8892 mHWData->mSharedFolders.push_back(sharedFolder);
8893 }
8894
8895 // Clipboard
8896 mHWData->mClipboardMode = data.clipboardMode;
8897
8898 // drag'n'drop
8899 mHWData->mDragAndDropMode = data.dragAndDropMode;
8900
8901 // guest settings
8902 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8903
8904 // IO settings
8905 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8906 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8907
8908 // Host PCI devices
8909 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8910 it != data.pciAttachments.end();
8911 ++it)
8912 {
8913 const settings::HostPCIDeviceAttachment &hpda = *it;
8914 ComObjPtr<PCIDeviceAttachment> pda;
8915
8916 pda.createObject();
8917 pda->loadSettings(this, hpda);
8918 mHWData->mPCIDeviceAssignments.push_back(pda);
8919 }
8920
8921 /*
8922 * (The following isn't really real hardware, but it lives in HWData
8923 * for reasons of convenience.)
8924 */
8925
8926#ifdef VBOX_WITH_GUEST_PROPS
8927 /* Guest properties (optional) */
8928 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8929 it != data.llGuestProperties.end();
8930 ++it)
8931 {
8932 const settings::GuestProperty &prop = *it;
8933 uint32_t fFlags = guestProp::NILFLAG;
8934 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8935 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8936 mHWData->mGuestProperties[prop.strName] = property;
8937 }
8938
8939 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8940#endif /* VBOX_WITH_GUEST_PROPS defined */
8941
8942 rc = loadDebugging(pDbg);
8943 if (FAILED(rc))
8944 return rc;
8945
8946 mHWData->mAutostart = *pAutostart;
8947
8948 /* default frontend */
8949 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8950 }
8951 catch(std::bad_alloc &)
8952 {
8953 return E_OUTOFMEMORY;
8954 }
8955
8956 AssertComRC(rc);
8957 return rc;
8958}
8959
8960/**
8961 * Called from Machine::loadHardware() to load the debugging settings of the
8962 * machine.
8963 *
8964 * @param pDbg Pointer to the settings.
8965 */
8966HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8967{
8968 mHWData->mDebugging = *pDbg;
8969 /* no more processing currently required, this will probably change. */
8970 return S_OK;
8971}
8972
8973/**
8974 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8975 *
8976 * @param data
8977 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8978 * @param puuidSnapshot
8979 * @return
8980 */
8981HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8982 const Guid *puuidRegistry,
8983 const Guid *puuidSnapshot)
8984{
8985 AssertReturn(!isSessionMachine(), E_FAIL);
8986
8987 HRESULT rc = S_OK;
8988
8989 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8990 it != data.llStorageControllers.end();
8991 ++it)
8992 {
8993 const settings::StorageController &ctlData = *it;
8994
8995 ComObjPtr<StorageController> pCtl;
8996 /* Try to find one with the name first. */
8997 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8998 if (SUCCEEDED(rc))
8999 return setError(VBOX_E_OBJECT_IN_USE,
9000 tr("Storage controller named '%s' already exists"),
9001 ctlData.strName.c_str());
9002
9003 pCtl.createObject();
9004 rc = pCtl->init(this,
9005 ctlData.strName,
9006 ctlData.storageBus,
9007 ctlData.ulInstance,
9008 ctlData.fBootable);
9009 if (FAILED(rc)) return rc;
9010
9011 mStorageControllers->push_back(pCtl);
9012
9013 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9014 if (FAILED(rc)) return rc;
9015
9016 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9017 if (FAILED(rc)) return rc;
9018
9019 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9020 if (FAILED(rc)) return rc;
9021
9022 /* Set IDE emulation settings (only for AHCI controller). */
9023 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9024 {
9025 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9026 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9027 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9028 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9029 )
9030 return rc;
9031 }
9032
9033 /* Load the attached devices now. */
9034 rc = loadStorageDevices(pCtl,
9035 ctlData,
9036 puuidRegistry,
9037 puuidSnapshot);
9038 if (FAILED(rc)) return rc;
9039 }
9040
9041 return S_OK;
9042}
9043
9044/**
9045 * Called from loadStorageControllers for a controller's devices.
9046 *
9047 * @param aStorageController
9048 * @param data
9049 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9050 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9051 * @return
9052 */
9053HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9054 const settings::StorageController &data,
9055 const Guid *puuidRegistry,
9056 const Guid *puuidSnapshot)
9057{
9058 HRESULT rc = S_OK;
9059
9060 /* paranoia: detect duplicate attachments */
9061 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9062 it != data.llAttachedDevices.end();
9063 ++it)
9064 {
9065 const settings::AttachedDevice &ad = *it;
9066
9067 for (settings::AttachedDevicesList::const_iterator it2 = it;
9068 it2 != data.llAttachedDevices.end();
9069 ++it2)
9070 {
9071 if (it == it2)
9072 continue;
9073
9074 const settings::AttachedDevice &ad2 = *it2;
9075
9076 if ( ad.lPort == ad2.lPort
9077 && ad.lDevice == ad2.lDevice)
9078 {
9079 return setError(E_FAIL,
9080 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9081 aStorageController->getName().c_str(),
9082 ad.lPort,
9083 ad.lDevice,
9084 mUserData->s.strName.c_str());
9085 }
9086 }
9087 }
9088
9089 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9090 it != data.llAttachedDevices.end();
9091 ++it)
9092 {
9093 const settings::AttachedDevice &dev = *it;
9094 ComObjPtr<Medium> medium;
9095
9096 switch (dev.deviceType)
9097 {
9098 case DeviceType_Floppy:
9099 case DeviceType_DVD:
9100 if (dev.strHostDriveSrc.isNotEmpty())
9101 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9102 else
9103 rc = mParent->findRemoveableMedium(dev.deviceType,
9104 dev.uuid,
9105 false /* fRefresh */,
9106 false /* aSetError */,
9107 medium);
9108 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9109 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9110 rc = S_OK;
9111 break;
9112
9113 case DeviceType_HardDisk:
9114 {
9115 /* find a hard disk by UUID */
9116 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9117 if (FAILED(rc))
9118 {
9119 if (isSnapshotMachine())
9120 {
9121 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9122 // so the user knows that the bad disk is in a snapshot somewhere
9123 com::ErrorInfo info;
9124 return setError(E_FAIL,
9125 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9126 puuidSnapshot->raw(),
9127 info.getText().raw());
9128 }
9129 else
9130 return rc;
9131 }
9132
9133 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9134
9135 if (medium->getType() == MediumType_Immutable)
9136 {
9137 if (isSnapshotMachine())
9138 return setError(E_FAIL,
9139 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9140 "of the virtual machine '%s' ('%s')"),
9141 medium->getLocationFull().c_str(),
9142 dev.uuid.raw(),
9143 puuidSnapshot->raw(),
9144 mUserData->s.strName.c_str(),
9145 mData->m_strConfigFileFull.c_str());
9146
9147 return setError(E_FAIL,
9148 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9149 medium->getLocationFull().c_str(),
9150 dev.uuid.raw(),
9151 mUserData->s.strName.c_str(),
9152 mData->m_strConfigFileFull.c_str());
9153 }
9154
9155 if (medium->getType() == MediumType_MultiAttach)
9156 {
9157 if (isSnapshotMachine())
9158 return setError(E_FAIL,
9159 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9160 "of the virtual machine '%s' ('%s')"),
9161 medium->getLocationFull().c_str(),
9162 dev.uuid.raw(),
9163 puuidSnapshot->raw(),
9164 mUserData->s.strName.c_str(),
9165 mData->m_strConfigFileFull.c_str());
9166
9167 return setError(E_FAIL,
9168 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9169 medium->getLocationFull().c_str(),
9170 dev.uuid.raw(),
9171 mUserData->s.strName.c_str(),
9172 mData->m_strConfigFileFull.c_str());
9173 }
9174
9175 if ( !isSnapshotMachine()
9176 && medium->getChildren().size() != 0
9177 )
9178 return setError(E_FAIL,
9179 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9180 "because it has %d differencing child hard disks"),
9181 medium->getLocationFull().c_str(),
9182 dev.uuid.raw(),
9183 mUserData->s.strName.c_str(),
9184 mData->m_strConfigFileFull.c_str(),
9185 medium->getChildren().size());
9186
9187 if (findAttachment(mMediaData->mAttachments,
9188 medium))
9189 return setError(E_FAIL,
9190 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9191 medium->getLocationFull().c_str(),
9192 dev.uuid.raw(),
9193 mUserData->s.strName.c_str(),
9194 mData->m_strConfigFileFull.c_str());
9195
9196 break;
9197 }
9198
9199 default:
9200 return setError(E_FAIL,
9201 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9202 medium->getLocationFull().c_str(),
9203 mUserData->s.strName.c_str(),
9204 mData->m_strConfigFileFull.c_str());
9205 }
9206
9207 if (FAILED(rc))
9208 break;
9209
9210 /* Bandwidth groups are loaded at this point. */
9211 ComObjPtr<BandwidthGroup> pBwGroup;
9212
9213 if (!dev.strBwGroup.isEmpty())
9214 {
9215 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9216 if (FAILED(rc))
9217 return setError(E_FAIL,
9218 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9219 medium->getLocationFull().c_str(),
9220 dev.strBwGroup.c_str(),
9221 mUserData->s.strName.c_str(),
9222 mData->m_strConfigFileFull.c_str());
9223 pBwGroup->reference();
9224 }
9225
9226 const Bstr controllerName = aStorageController->getName();
9227 ComObjPtr<MediumAttachment> pAttachment;
9228 pAttachment.createObject();
9229 rc = pAttachment->init(this,
9230 medium,
9231 controllerName,
9232 dev.lPort,
9233 dev.lDevice,
9234 dev.deviceType,
9235 false,
9236 dev.fPassThrough,
9237 dev.fTempEject,
9238 dev.fNonRotational,
9239 dev.fDiscard,
9240 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9241 if (FAILED(rc)) break;
9242
9243 /* associate the medium with this machine and snapshot */
9244 if (!medium.isNull())
9245 {
9246 AutoCaller medCaller(medium);
9247 if (FAILED(medCaller.rc())) return medCaller.rc();
9248 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9249
9250 if (isSnapshotMachine())
9251 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9252 else
9253 rc = medium->addBackReference(mData->mUuid);
9254 /* If the medium->addBackReference fails it sets an appropriate
9255 * error message, so no need to do any guesswork here. */
9256
9257 if (puuidRegistry)
9258 // caller wants registry ID to be set on all attached media (OVF import case)
9259 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9260 }
9261
9262 if (FAILED(rc))
9263 break;
9264
9265 /* back up mMediaData to let registeredInit() properly rollback on failure
9266 * (= limited accessibility) */
9267 setModified(IsModified_Storage);
9268 mMediaData.backup();
9269 mMediaData->mAttachments.push_back(pAttachment);
9270 }
9271
9272 return rc;
9273}
9274
9275/**
9276 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9277 *
9278 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9279 * @param aSnapshot where to return the found snapshot
9280 * @param aSetError true to set extended error info on failure
9281 */
9282HRESULT Machine::findSnapshotById(const Guid &aId,
9283 ComObjPtr<Snapshot> &aSnapshot,
9284 bool aSetError /* = false */)
9285{
9286 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9287
9288 if (!mData->mFirstSnapshot)
9289 {
9290 if (aSetError)
9291 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9292 return E_FAIL;
9293 }
9294
9295 if (aId.isZero())
9296 aSnapshot = mData->mFirstSnapshot;
9297 else
9298 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9299
9300 if (!aSnapshot)
9301 {
9302 if (aSetError)
9303 return setError(E_FAIL,
9304 tr("Could not find a snapshot with UUID {%s}"),
9305 aId.toString().c_str());
9306 return E_FAIL;
9307 }
9308
9309 return S_OK;
9310}
9311
9312/**
9313 * Returns the snapshot with the given name or fails of no such snapshot.
9314 *
9315 * @param aName snapshot name to find
9316 * @param aSnapshot where to return the found snapshot
9317 * @param aSetError true to set extended error info on failure
9318 */
9319HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9320 ComObjPtr<Snapshot> &aSnapshot,
9321 bool aSetError /* = false */)
9322{
9323 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9324
9325 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9326
9327 if (!mData->mFirstSnapshot)
9328 {
9329 if (aSetError)
9330 return setError(VBOX_E_OBJECT_NOT_FOUND,
9331 tr("This machine does not have any snapshots"));
9332 return VBOX_E_OBJECT_NOT_FOUND;
9333 }
9334
9335 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9336
9337 if (!aSnapshot)
9338 {
9339 if (aSetError)
9340 return setError(VBOX_E_OBJECT_NOT_FOUND,
9341 tr("Could not find a snapshot named '%s'"), strName.c_str());
9342 return VBOX_E_OBJECT_NOT_FOUND;
9343 }
9344
9345 return S_OK;
9346}
9347
9348/**
9349 * Returns a storage controller object with the given name.
9350 *
9351 * @param aName storage controller name to find
9352 * @param aStorageController where to return the found storage controller
9353 * @param aSetError true to set extended error info on failure
9354 */
9355HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9356 ComObjPtr<StorageController> &aStorageController,
9357 bool aSetError /* = false */)
9358{
9359 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9360
9361 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9362 it != mStorageControllers->end();
9363 ++it)
9364 {
9365 if ((*it)->getName() == aName)
9366 {
9367 aStorageController = (*it);
9368 return S_OK;
9369 }
9370 }
9371
9372 if (aSetError)
9373 return setError(VBOX_E_OBJECT_NOT_FOUND,
9374 tr("Could not find a storage controller named '%s'"),
9375 aName.c_str());
9376 return VBOX_E_OBJECT_NOT_FOUND;
9377}
9378
9379HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9380 MediaData::AttachmentList &atts)
9381{
9382 AutoCaller autoCaller(this);
9383 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9384
9385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9386
9387 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9388 it != mMediaData->mAttachments.end();
9389 ++it)
9390 {
9391 const ComObjPtr<MediumAttachment> &pAtt = *it;
9392
9393 // should never happen, but deal with NULL pointers in the list.
9394 AssertStmt(!pAtt.isNull(), continue);
9395
9396 // getControllerName() needs caller+read lock
9397 AutoCaller autoAttCaller(pAtt);
9398 if (FAILED(autoAttCaller.rc()))
9399 {
9400 atts.clear();
9401 return autoAttCaller.rc();
9402 }
9403 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9404
9405 if (pAtt->getControllerName() == aName)
9406 atts.push_back(pAtt);
9407 }
9408
9409 return S_OK;
9410}
9411
9412/**
9413 * Helper for #saveSettings. Cares about renaming the settings directory and
9414 * file if the machine name was changed and about creating a new settings file
9415 * if this is a new machine.
9416 *
9417 * @note Must be never called directly but only from #saveSettings().
9418 */
9419HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9420{
9421 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9422
9423 HRESULT rc = S_OK;
9424
9425 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9426
9427 /// @todo need to handle primary group change, too
9428
9429 /* attempt to rename the settings file if machine name is changed */
9430 if ( mUserData->s.fNameSync
9431 && mUserData.isBackedUp()
9432 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9433 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9434 )
9435 {
9436 bool dirRenamed = false;
9437 bool fileRenamed = false;
9438
9439 Utf8Str configFile, newConfigFile;
9440 Utf8Str configFilePrev, newConfigFilePrev;
9441 Utf8Str configDir, newConfigDir;
9442
9443 do
9444 {
9445 int vrc = VINF_SUCCESS;
9446
9447 Utf8Str name = mUserData.backedUpData()->s.strName;
9448 Utf8Str newName = mUserData->s.strName;
9449 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9450 if (group == "/")
9451 group.setNull();
9452 Utf8Str newGroup = mUserData->s.llGroups.front();
9453 if (newGroup == "/")
9454 newGroup.setNull();
9455
9456 configFile = mData->m_strConfigFileFull;
9457
9458 /* first, rename the directory if it matches the group and machine name */
9459 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9460 group.c_str(), RTPATH_DELIMITER, name.c_str());
9461 /** @todo hack, make somehow use of ComposeMachineFilename */
9462 if (mUserData->s.fDirectoryIncludesUUID)
9463 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9464 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9465 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9466 /** @todo hack, make somehow use of ComposeMachineFilename */
9467 if (mUserData->s.fDirectoryIncludesUUID)
9468 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9469 configDir = configFile;
9470 configDir.stripFilename();
9471 newConfigDir = configDir;
9472 if ( configDir.length() >= groupPlusName.length()
9473 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9474 {
9475 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9476 Utf8Str newConfigBaseDir(newConfigDir);
9477 newConfigDir.append(newGroupPlusName);
9478 /* consistency: use \ if appropriate on the platform */
9479 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9480 /* new dir and old dir cannot be equal here because of 'if'
9481 * above and because name != newName */
9482 Assert(configDir != newConfigDir);
9483 if (!fSettingsFileIsNew)
9484 {
9485 /* perform real rename only if the machine is not new */
9486 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9487 if ( vrc == VERR_FILE_NOT_FOUND
9488 || vrc == VERR_PATH_NOT_FOUND)
9489 {
9490 /* create the parent directory, then retry renaming */
9491 Utf8Str parent(newConfigDir);
9492 parent.stripFilename();
9493 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9494 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9495 }
9496 if (RT_FAILURE(vrc))
9497 {
9498 rc = setError(E_FAIL,
9499 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9500 configDir.c_str(),
9501 newConfigDir.c_str(),
9502 vrc);
9503 break;
9504 }
9505 /* delete subdirectories which are no longer needed */
9506 Utf8Str dir(configDir);
9507 dir.stripFilename();
9508 while (dir != newConfigBaseDir && dir != ".")
9509 {
9510 vrc = RTDirRemove(dir.c_str());
9511 if (RT_FAILURE(vrc))
9512 break;
9513 dir.stripFilename();
9514 }
9515 dirRenamed = true;
9516 }
9517 }
9518
9519 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9520 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9521
9522 /* then try to rename the settings file itself */
9523 if (newConfigFile != configFile)
9524 {
9525 /* get the path to old settings file in renamed directory */
9526 configFile = Utf8StrFmt("%s%c%s",
9527 newConfigDir.c_str(),
9528 RTPATH_DELIMITER,
9529 RTPathFilename(configFile.c_str()));
9530 if (!fSettingsFileIsNew)
9531 {
9532 /* perform real rename only if the machine is not new */
9533 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9534 if (RT_FAILURE(vrc))
9535 {
9536 rc = setError(E_FAIL,
9537 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9538 configFile.c_str(),
9539 newConfigFile.c_str(),
9540 vrc);
9541 break;
9542 }
9543 fileRenamed = true;
9544 configFilePrev = configFile;
9545 configFilePrev += "-prev";
9546 newConfigFilePrev = newConfigFile;
9547 newConfigFilePrev += "-prev";
9548 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9549 }
9550 }
9551
9552 // update m_strConfigFileFull amd mConfigFile
9553 mData->m_strConfigFileFull = newConfigFile;
9554 // compute the relative path too
9555 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9556
9557 // store the old and new so that VirtualBox::saveSettings() can update
9558 // the media registry
9559 if ( mData->mRegistered
9560 && configDir != newConfigDir)
9561 {
9562 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9563
9564 if (pfNeedsGlobalSaveSettings)
9565 *pfNeedsGlobalSaveSettings = true;
9566 }
9567
9568 // in the saved state file path, replace the old directory with the new directory
9569 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9570 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9571
9572 // and do the same thing for the saved state file paths of all the online snapshots
9573 if (mData->mFirstSnapshot)
9574 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9575 newConfigDir.c_str());
9576 }
9577 while (0);
9578
9579 if (FAILED(rc))
9580 {
9581 /* silently try to rename everything back */
9582 if (fileRenamed)
9583 {
9584 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9585 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9586 }
9587 if (dirRenamed)
9588 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9589 }
9590
9591 if (FAILED(rc)) return rc;
9592 }
9593
9594 if (fSettingsFileIsNew)
9595 {
9596 /* create a virgin config file */
9597 int vrc = VINF_SUCCESS;
9598
9599 /* ensure the settings directory exists */
9600 Utf8Str path(mData->m_strConfigFileFull);
9601 path.stripFilename();
9602 if (!RTDirExists(path.c_str()))
9603 {
9604 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9605 if (RT_FAILURE(vrc))
9606 {
9607 return setError(E_FAIL,
9608 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9609 path.c_str(),
9610 vrc);
9611 }
9612 }
9613
9614 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9615 path = Utf8Str(mData->m_strConfigFileFull);
9616 RTFILE f = NIL_RTFILE;
9617 vrc = RTFileOpen(&f, path.c_str(),
9618 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9619 if (RT_FAILURE(vrc))
9620 return setError(E_FAIL,
9621 tr("Could not create the settings file '%s' (%Rrc)"),
9622 path.c_str(),
9623 vrc);
9624 RTFileClose(f);
9625 }
9626
9627 return rc;
9628}
9629
9630/**
9631 * Saves and commits machine data, user data and hardware data.
9632 *
9633 * Note that on failure, the data remains uncommitted.
9634 *
9635 * @a aFlags may combine the following flags:
9636 *
9637 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9638 * Used when saving settings after an operation that makes them 100%
9639 * correspond to the settings from the current snapshot.
9640 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9641 * #isReallyModified() returns false. This is necessary for cases when we
9642 * change machine data directly, not through the backup()/commit() mechanism.
9643 * - SaveS_Force: settings will be saved without doing a deep compare of the
9644 * settings structures. This is used when this is called because snapshots
9645 * have changed to avoid the overhead of the deep compare.
9646 *
9647 * @note Must be called from under this object's write lock. Locks children for
9648 * writing.
9649 *
9650 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9651 * initialized to false and that will be set to true by this function if
9652 * the caller must invoke VirtualBox::saveSettings() because the global
9653 * settings have changed. This will happen if a machine rename has been
9654 * saved and the global machine and media registries will therefore need
9655 * updating.
9656 */
9657HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9658 int aFlags /*= 0*/)
9659{
9660 LogFlowThisFuncEnter();
9661
9662 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9663
9664 /* make sure child objects are unable to modify the settings while we are
9665 * saving them */
9666 ensureNoStateDependencies();
9667
9668 AssertReturn(!isSnapshotMachine(),
9669 E_FAIL);
9670
9671 HRESULT rc = S_OK;
9672 bool fNeedsWrite = false;
9673
9674 /* First, prepare to save settings. It will care about renaming the
9675 * settings directory and file if the machine name was changed and about
9676 * creating a new settings file if this is a new machine. */
9677 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9678 if (FAILED(rc)) return rc;
9679
9680 // keep a pointer to the current settings structures
9681 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9682 settings::MachineConfigFile *pNewConfig = NULL;
9683
9684 try
9685 {
9686 // make a fresh one to have everyone write stuff into
9687 pNewConfig = new settings::MachineConfigFile(NULL);
9688 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9689
9690 // now go and copy all the settings data from COM to the settings structures
9691 // (this calles saveSettings() on all the COM objects in the machine)
9692 copyMachineDataToSettings(*pNewConfig);
9693
9694 if (aFlags & SaveS_ResetCurStateModified)
9695 {
9696 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9697 mData->mCurrentStateModified = FALSE;
9698 fNeedsWrite = true; // always, no need to compare
9699 }
9700 else if (aFlags & SaveS_Force)
9701 {
9702 fNeedsWrite = true; // always, no need to compare
9703 }
9704 else
9705 {
9706 if (!mData->mCurrentStateModified)
9707 {
9708 // do a deep compare of the settings that we just saved with the settings
9709 // previously stored in the config file; this invokes MachineConfigFile::operator==
9710 // which does a deep compare of all the settings, which is expensive but less expensive
9711 // than writing out XML in vain
9712 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9713
9714 // could still be modified if any settings changed
9715 mData->mCurrentStateModified = fAnySettingsChanged;
9716
9717 fNeedsWrite = fAnySettingsChanged;
9718 }
9719 else
9720 fNeedsWrite = true;
9721 }
9722
9723 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9724
9725 if (fNeedsWrite)
9726 // now spit it all out!
9727 pNewConfig->write(mData->m_strConfigFileFull);
9728
9729 mData->pMachineConfigFile = pNewConfig;
9730 delete pOldConfig;
9731 commit();
9732
9733 // after saving settings, we are no longer different from the XML on disk
9734 mData->flModifications = 0;
9735 }
9736 catch (HRESULT err)
9737 {
9738 // we assume that error info is set by the thrower
9739 rc = err;
9740
9741 // restore old config
9742 delete pNewConfig;
9743 mData->pMachineConfigFile = pOldConfig;
9744 }
9745 catch (...)
9746 {
9747 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9748 }
9749
9750 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9751 {
9752 /* Fire the data change event, even on failure (since we've already
9753 * committed all data). This is done only for SessionMachines because
9754 * mutable Machine instances are always not registered (i.e. private
9755 * to the client process that creates them) and thus don't need to
9756 * inform callbacks. */
9757 if (isSessionMachine())
9758 mParent->onMachineDataChange(mData->mUuid);
9759 }
9760
9761 LogFlowThisFunc(("rc=%08X\n", rc));
9762 LogFlowThisFuncLeave();
9763 return rc;
9764}
9765
9766/**
9767 * Implementation for saving the machine settings into the given
9768 * settings::MachineConfigFile instance. This copies machine extradata
9769 * from the previous machine config file in the instance data, if any.
9770 *
9771 * This gets called from two locations:
9772 *
9773 * -- Machine::saveSettings(), during the regular XML writing;
9774 *
9775 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9776 * exported to OVF and we write the VirtualBox proprietary XML
9777 * into a <vbox:Machine> tag.
9778 *
9779 * This routine fills all the fields in there, including snapshots, *except*
9780 * for the following:
9781 *
9782 * -- fCurrentStateModified. There is some special logic associated with that.
9783 *
9784 * The caller can then call MachineConfigFile::write() or do something else
9785 * with it.
9786 *
9787 * Caller must hold the machine lock!
9788 *
9789 * This throws XML errors and HRESULT, so the caller must have a catch block!
9790 */
9791void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9792{
9793 // deep copy extradata
9794 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9795
9796 config.uuid = mData->mUuid;
9797
9798 // copy name, description, OS type, teleport, UTC etc.
9799 config.machineUserData = mUserData->s;
9800
9801 if ( mData->mMachineState == MachineState_Saved
9802 || mData->mMachineState == MachineState_Restoring
9803 // when deleting a snapshot we may or may not have a saved state in the current state,
9804 // so let's not assert here please
9805 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9806 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9807 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9808 && (!mSSData->strStateFilePath.isEmpty())
9809 )
9810 )
9811 {
9812 Assert(!mSSData->strStateFilePath.isEmpty());
9813 /* try to make the file name relative to the settings file dir */
9814 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9815 }
9816 else
9817 {
9818 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9819 config.strStateFile.setNull();
9820 }
9821
9822 if (mData->mCurrentSnapshot)
9823 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9824 else
9825 config.uuidCurrentSnapshot.clear();
9826
9827 config.timeLastStateChange = mData->mLastStateChange;
9828 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9829 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9830
9831 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9832 if (FAILED(rc)) throw rc;
9833
9834 rc = saveStorageControllers(config.storageMachine);
9835 if (FAILED(rc)) throw rc;
9836
9837 // save machine's media registry if this is VirtualBox 4.0 or later
9838 if (config.canHaveOwnMediaRegistry())
9839 {
9840 // determine machine folder
9841 Utf8Str strMachineFolder = getSettingsFileFull();
9842 strMachineFolder.stripFilename();
9843 mParent->saveMediaRegistry(config.mediaRegistry,
9844 getId(), // only media with registry ID == machine UUID
9845 strMachineFolder);
9846 // this throws HRESULT
9847 }
9848
9849 // save snapshots
9850 rc = saveAllSnapshots(config);
9851 if (FAILED(rc)) throw rc;
9852}
9853
9854/**
9855 * Saves all snapshots of the machine into the given machine config file. Called
9856 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9857 * @param config
9858 * @return
9859 */
9860HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9861{
9862 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9863
9864 HRESULT rc = S_OK;
9865
9866 try
9867 {
9868 config.llFirstSnapshot.clear();
9869
9870 if (mData->mFirstSnapshot)
9871 {
9872 settings::Snapshot snapNew;
9873 config.llFirstSnapshot.push_back(snapNew);
9874
9875 // get reference to the fresh copy of the snapshot on the list and
9876 // work on that copy directly to avoid excessive copying later
9877 settings::Snapshot &snap = config.llFirstSnapshot.front();
9878
9879 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9880 if (FAILED(rc)) throw rc;
9881 }
9882
9883// if (mType == IsSessionMachine)
9884// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9885
9886 }
9887 catch (HRESULT err)
9888 {
9889 /* we assume that error info is set by the thrower */
9890 rc = err;
9891 }
9892 catch (...)
9893 {
9894 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9895 }
9896
9897 return rc;
9898}
9899
9900/**
9901 * Saves the VM hardware configuration. It is assumed that the
9902 * given node is empty.
9903 *
9904 * @param data Reference to the settings object for the hardware config.
9905 * @param pDbg Pointer to the settings object for the debugging config
9906 * which happens to live in mHWData.
9907 * @param pAutostart Pointer to the settings object for the autostart config
9908 * which happens to live in mHWData.
9909 */
9910HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9911 settings::Autostart *pAutostart)
9912{
9913 HRESULT rc = S_OK;
9914
9915 try
9916 {
9917 /* The hardware version attribute (optional).
9918 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9919 if ( mHWData->mHWVersion == "1"
9920 && mSSData->strStateFilePath.isEmpty()
9921 )
9922 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. */
9923
9924 data.strVersion = mHWData->mHWVersion;
9925 data.uuid = mHWData->mHardwareUUID;
9926
9927 // CPU
9928 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9929 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9930 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9931 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9932 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9933 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9934 data.fPAE = !!mHWData->mPAEEnabled;
9935 data.enmLongMode = mHWData->mLongMode;
9936 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9937
9938 /* Standard and Extended CPUID leafs. */
9939 data.llCpuIdLeafs.clear();
9940 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9941 {
9942 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9943 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9944 }
9945 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9946 {
9947 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9948 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9949 }
9950
9951 data.cCPUs = mHWData->mCPUCount;
9952 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9953 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9954
9955 data.llCpus.clear();
9956 if (data.fCpuHotPlug)
9957 {
9958 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9959 {
9960 if (mHWData->mCPUAttached[idx])
9961 {
9962 settings::Cpu cpu;
9963 cpu.ulId = idx;
9964 data.llCpus.push_back(cpu);
9965 }
9966 }
9967 }
9968
9969 // memory
9970 data.ulMemorySizeMB = mHWData->mMemorySize;
9971 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9972
9973 // firmware
9974 data.firmwareType = mHWData->mFirmwareType;
9975
9976 // HID
9977 data.pointingHIDType = mHWData->mPointingHIDType;
9978 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9979
9980 // chipset
9981 data.chipsetType = mHWData->mChipsetType;
9982
9983 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
9984 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9985
9986 // HPET
9987 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9988
9989 // boot order
9990 data.mapBootOrder.clear();
9991 for (size_t i = 0;
9992 i < RT_ELEMENTS(mHWData->mBootOrder);
9993 ++i)
9994 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9995
9996 // display
9997 data.graphicsControllerType = mHWData->mGraphicsControllerType;
9998 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9999 data.cMonitors = mHWData->mMonitorCount;
10000 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10001 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10002 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10003 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10004 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10005 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10006 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
10007
10008 /* VRDEServer settings (optional) */
10009 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10010 if (FAILED(rc)) throw rc;
10011
10012 /* BIOS (required) */
10013 rc = mBIOSSettings->saveSettings(data.biosSettings);
10014 if (FAILED(rc)) throw rc;
10015
10016 /* USB Controller (required) */
10017 rc = mUSBController->saveSettings(data.usbController);
10018 if (FAILED(rc)) throw rc;
10019
10020 /* Network adapters (required) */
10021 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10022 data.llNetworkAdapters.clear();
10023 /* Write out only the nominal number of network adapters for this
10024 * chipset type. Since Machine::commit() hasn't been called there
10025 * may be extra NIC settings in the vector. */
10026 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10027 {
10028 settings::NetworkAdapter nic;
10029 nic.ulSlot = slot;
10030 /* paranoia check... must not be NULL, but must not crash either. */
10031 if (mNetworkAdapters[slot])
10032 {
10033 rc = mNetworkAdapters[slot]->saveSettings(nic);
10034 if (FAILED(rc)) throw rc;
10035
10036 data.llNetworkAdapters.push_back(nic);
10037 }
10038 }
10039
10040 /* Serial ports */
10041 data.llSerialPorts.clear();
10042 for (ULONG slot = 0;
10043 slot < RT_ELEMENTS(mSerialPorts);
10044 ++slot)
10045 {
10046 settings::SerialPort s;
10047 s.ulSlot = slot;
10048 rc = mSerialPorts[slot]->saveSettings(s);
10049 if (FAILED(rc)) return rc;
10050
10051 data.llSerialPorts.push_back(s);
10052 }
10053
10054 /* Parallel ports */
10055 data.llParallelPorts.clear();
10056 for (ULONG slot = 0;
10057 slot < RT_ELEMENTS(mParallelPorts);
10058 ++slot)
10059 {
10060 settings::ParallelPort p;
10061 p.ulSlot = slot;
10062 rc = mParallelPorts[slot]->saveSettings(p);
10063 if (FAILED(rc)) return rc;
10064
10065 data.llParallelPorts.push_back(p);
10066 }
10067
10068 /* Audio adapter */
10069 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10070 if (FAILED(rc)) return rc;
10071
10072 /* Shared folders */
10073 data.llSharedFolders.clear();
10074 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10075 it != mHWData->mSharedFolders.end();
10076 ++it)
10077 {
10078 SharedFolder *pSF = *it;
10079 AutoCaller sfCaller(pSF);
10080 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10081 settings::SharedFolder sf;
10082 sf.strName = pSF->getName();
10083 sf.strHostPath = pSF->getHostPath();
10084 sf.fWritable = !!pSF->isWritable();
10085 sf.fAutoMount = !!pSF->isAutoMounted();
10086
10087 data.llSharedFolders.push_back(sf);
10088 }
10089
10090 // clipboard
10091 data.clipboardMode = mHWData->mClipboardMode;
10092
10093 // drag'n'drop
10094 data.dragAndDropMode = mHWData->mDragAndDropMode;
10095
10096 /* Guest */
10097 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10098
10099 // IO settings
10100 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10101 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10102
10103 /* BandwidthControl (required) */
10104 rc = mBandwidthControl->saveSettings(data.ioSettings);
10105 if (FAILED(rc)) throw rc;
10106
10107 /* Host PCI devices */
10108 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10109 it != mHWData->mPCIDeviceAssignments.end();
10110 ++it)
10111 {
10112 ComObjPtr<PCIDeviceAttachment> pda = *it;
10113 settings::HostPCIDeviceAttachment hpda;
10114
10115 rc = pda->saveSettings(hpda);
10116 if (FAILED(rc)) throw rc;
10117
10118 data.pciAttachments.push_back(hpda);
10119 }
10120
10121
10122 // guest properties
10123 data.llGuestProperties.clear();
10124#ifdef VBOX_WITH_GUEST_PROPS
10125 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10126 it != mHWData->mGuestProperties.end();
10127 ++it)
10128 {
10129 HWData::GuestProperty property = it->second;
10130
10131 /* Remove transient guest properties at shutdown unless we
10132 * are saving state */
10133 if ( ( mData->mMachineState == MachineState_PoweredOff
10134 || mData->mMachineState == MachineState_Aborted
10135 || mData->mMachineState == MachineState_Teleported)
10136 && ( property.mFlags & guestProp::TRANSIENT
10137 || property.mFlags & guestProp::TRANSRESET))
10138 continue;
10139 settings::GuestProperty prop;
10140 prop.strName = it->first;
10141 prop.strValue = property.strValue;
10142 prop.timestamp = property.mTimestamp;
10143 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10144 guestProp::writeFlags(property.mFlags, szFlags);
10145 prop.strFlags = szFlags;
10146
10147 data.llGuestProperties.push_back(prop);
10148 }
10149
10150 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10151 /* I presume this doesn't require a backup(). */
10152 mData->mGuestPropertiesModified = FALSE;
10153#endif /* VBOX_WITH_GUEST_PROPS defined */
10154
10155 *pDbg = mHWData->mDebugging;
10156 *pAutostart = mHWData->mAutostart;
10157
10158 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10159 }
10160 catch(std::bad_alloc &)
10161 {
10162 return E_OUTOFMEMORY;
10163 }
10164
10165 AssertComRC(rc);
10166 return rc;
10167}
10168
10169/**
10170 * Saves the storage controller configuration.
10171 *
10172 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10173 */
10174HRESULT Machine::saveStorageControllers(settings::Storage &data)
10175{
10176 data.llStorageControllers.clear();
10177
10178 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10179 it != mStorageControllers->end();
10180 ++it)
10181 {
10182 HRESULT rc;
10183 ComObjPtr<StorageController> pCtl = *it;
10184
10185 settings::StorageController ctl;
10186 ctl.strName = pCtl->getName();
10187 ctl.controllerType = pCtl->getControllerType();
10188 ctl.storageBus = pCtl->getStorageBus();
10189 ctl.ulInstance = pCtl->getInstance();
10190 ctl.fBootable = pCtl->getBootable();
10191
10192 /* Save the port count. */
10193 ULONG portCount;
10194 rc = pCtl->COMGETTER(PortCount)(&portCount);
10195 ComAssertComRCRet(rc, rc);
10196 ctl.ulPortCount = portCount;
10197
10198 /* Save fUseHostIOCache */
10199 BOOL fUseHostIOCache;
10200 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10201 ComAssertComRCRet(rc, rc);
10202 ctl.fUseHostIOCache = !!fUseHostIOCache;
10203
10204 /* Save IDE emulation settings. */
10205 if (ctl.controllerType == StorageControllerType_IntelAhci)
10206 {
10207 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10208 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10209 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10210 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10211 )
10212 ComAssertComRCRet(rc, rc);
10213 }
10214
10215 /* save the devices now. */
10216 rc = saveStorageDevices(pCtl, ctl);
10217 ComAssertComRCRet(rc, rc);
10218
10219 data.llStorageControllers.push_back(ctl);
10220 }
10221
10222 return S_OK;
10223}
10224
10225/**
10226 * Saves the hard disk configuration.
10227 */
10228HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10229 settings::StorageController &data)
10230{
10231 MediaData::AttachmentList atts;
10232
10233 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10234 if (FAILED(rc)) return rc;
10235
10236 data.llAttachedDevices.clear();
10237 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10238 it != atts.end();
10239 ++it)
10240 {
10241 settings::AttachedDevice dev;
10242
10243 MediumAttachment *pAttach = *it;
10244 Medium *pMedium = pAttach->getMedium();
10245
10246 dev.deviceType = pAttach->getType();
10247 dev.lPort = pAttach->getPort();
10248 dev.lDevice = pAttach->getDevice();
10249 if (pMedium)
10250 {
10251 if (pMedium->isHostDrive())
10252 dev.strHostDriveSrc = pMedium->getLocationFull();
10253 else
10254 dev.uuid = pMedium->getId();
10255 dev.fPassThrough = pAttach->getPassthrough();
10256 dev.fTempEject = pAttach->getTempEject();
10257 dev.fNonRotational = pAttach->getNonRotational();
10258 dev.fDiscard = pAttach->getDiscard();
10259 }
10260
10261 dev.strBwGroup = pAttach->getBandwidthGroup();
10262
10263 data.llAttachedDevices.push_back(dev);
10264 }
10265
10266 return S_OK;
10267}
10268
10269/**
10270 * Saves machine state settings as defined by aFlags
10271 * (SaveSTS_* values).
10272 *
10273 * @param aFlags Combination of SaveSTS_* flags.
10274 *
10275 * @note Locks objects for writing.
10276 */
10277HRESULT Machine::saveStateSettings(int aFlags)
10278{
10279 if (aFlags == 0)
10280 return S_OK;
10281
10282 AutoCaller autoCaller(this);
10283 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10284
10285 /* This object's write lock is also necessary to serialize file access
10286 * (prevent concurrent reads and writes) */
10287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10288
10289 HRESULT rc = S_OK;
10290
10291 Assert(mData->pMachineConfigFile);
10292
10293 try
10294 {
10295 if (aFlags & SaveSTS_CurStateModified)
10296 mData->pMachineConfigFile->fCurrentStateModified = true;
10297
10298 if (aFlags & SaveSTS_StateFilePath)
10299 {
10300 if (!mSSData->strStateFilePath.isEmpty())
10301 /* try to make the file name relative to the settings file dir */
10302 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10303 else
10304 mData->pMachineConfigFile->strStateFile.setNull();
10305 }
10306
10307 if (aFlags & SaveSTS_StateTimeStamp)
10308 {
10309 Assert( mData->mMachineState != MachineState_Aborted
10310 || mSSData->strStateFilePath.isEmpty());
10311
10312 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10313
10314 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10315//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10316 }
10317
10318 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10319 }
10320 catch (...)
10321 {
10322 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10323 }
10324
10325 return rc;
10326}
10327
10328/**
10329 * Ensures that the given medium is added to a media registry. If this machine
10330 * was created with 4.0 or later, then the machine registry is used. Otherwise
10331 * the global VirtualBox media registry is used.
10332 *
10333 * Caller must NOT hold machine lock, media tree or any medium locks!
10334 *
10335 * @param pMedium
10336 */
10337void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10338{
10339 /* Paranoia checks: do not hold machine or media tree locks. */
10340 AssertReturnVoid(!isWriteLockOnCurrentThread());
10341 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10342
10343 ComObjPtr<Medium> pBase;
10344 {
10345 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10346 pBase = pMedium->getBase();
10347 }
10348
10349 /* Paranoia checks: do not hold medium locks. */
10350 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10351 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10352
10353 // decide which medium registry to use now that the medium is attached:
10354 Guid uuid;
10355 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10356 // machine XML is VirtualBox 4.0 or higher:
10357 uuid = getId(); // machine UUID
10358 else
10359 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10360
10361 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10362 mParent->markRegistryModified(uuid);
10363
10364 /* For more complex hard disk structures it can happen that the base
10365 * medium isn't yet associated with any medium registry. Do that now. */
10366 if (pMedium != pBase)
10367 {
10368 if (pBase->addRegistry(uuid, true /* fRecurse */))
10369 mParent->markRegistryModified(uuid);
10370 }
10371}
10372
10373/**
10374 * Creates differencing hard disks for all normal hard disks attached to this
10375 * machine and a new set of attachments to refer to created disks.
10376 *
10377 * Used when taking a snapshot or when deleting the current state. Gets called
10378 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10379 *
10380 * This method assumes that mMediaData contains the original hard disk attachments
10381 * it needs to create diffs for. On success, these attachments will be replaced
10382 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10383 * called to delete created diffs which will also rollback mMediaData and restore
10384 * whatever was backed up before calling this method.
10385 *
10386 * Attachments with non-normal hard disks are left as is.
10387 *
10388 * If @a aOnline is @c false then the original hard disks that require implicit
10389 * diffs will be locked for reading. Otherwise it is assumed that they are
10390 * already locked for writing (when the VM was started). Note that in the latter
10391 * case it is responsibility of the caller to lock the newly created diffs for
10392 * writing if this method succeeds.
10393 *
10394 * @param aProgress Progress object to run (must contain at least as
10395 * many operations left as the number of hard disks
10396 * attached).
10397 * @param aOnline Whether the VM was online prior to this operation.
10398 *
10399 * @note The progress object is not marked as completed, neither on success nor
10400 * on failure. This is a responsibility of the caller.
10401 *
10402 * @note Locks this object and the media tree for writing.
10403 */
10404HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10405 ULONG aWeight,
10406 bool aOnline)
10407{
10408 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10409
10410 AutoCaller autoCaller(this);
10411 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10412
10413 AutoMultiWriteLock2 alock(this->lockHandle(),
10414 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10415
10416 /* must be in a protective state because we release the lock below */
10417 AssertReturn( mData->mMachineState == MachineState_Saving
10418 || mData->mMachineState == MachineState_LiveSnapshotting
10419 || mData->mMachineState == MachineState_RestoringSnapshot
10420 || mData->mMachineState == MachineState_DeletingSnapshot
10421 , E_FAIL);
10422
10423 HRESULT rc = S_OK;
10424
10425 // use appropriate locked media map (online or offline)
10426 MediumLockListMap lockedMediaOffline;
10427 MediumLockListMap *lockedMediaMap;
10428 if (aOnline)
10429 lockedMediaMap = &mData->mSession.mLockedMedia;
10430 else
10431 lockedMediaMap = &lockedMediaOffline;
10432
10433 try
10434 {
10435 if (!aOnline)
10436 {
10437 /* lock all attached hard disks early to detect "in use"
10438 * situations before creating actual diffs */
10439 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10440 it != mMediaData->mAttachments.end();
10441 ++it)
10442 {
10443 MediumAttachment* pAtt = *it;
10444 if (pAtt->getType() == DeviceType_HardDisk)
10445 {
10446 Medium* pMedium = pAtt->getMedium();
10447 Assert(pMedium);
10448
10449 MediumLockList *pMediumLockList(new MediumLockList());
10450 alock.release();
10451 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10452 false /* fMediumLockWrite */,
10453 NULL,
10454 *pMediumLockList);
10455 alock.acquire();
10456 if (FAILED(rc))
10457 {
10458 delete pMediumLockList;
10459 throw rc;
10460 }
10461 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10462 if (FAILED(rc))
10463 {
10464 throw setError(rc,
10465 tr("Collecting locking information for all attached media failed"));
10466 }
10467 }
10468 }
10469
10470 /* Now lock all media. If this fails, nothing is locked. */
10471 alock.release();
10472 rc = lockedMediaMap->Lock();
10473 alock.acquire();
10474 if (FAILED(rc))
10475 {
10476 throw setError(rc,
10477 tr("Locking of attached media failed"));
10478 }
10479 }
10480
10481 /* remember the current list (note that we don't use backup() since
10482 * mMediaData may be already backed up) */
10483 MediaData::AttachmentList atts = mMediaData->mAttachments;
10484
10485 /* start from scratch */
10486 mMediaData->mAttachments.clear();
10487
10488 /* go through remembered attachments and create diffs for normal hard
10489 * disks and attach them */
10490 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10491 it != atts.end();
10492 ++it)
10493 {
10494 MediumAttachment* pAtt = *it;
10495
10496 DeviceType_T devType = pAtt->getType();
10497 Medium* pMedium = pAtt->getMedium();
10498
10499 if ( devType != DeviceType_HardDisk
10500 || pMedium == NULL
10501 || pMedium->getType() != MediumType_Normal)
10502 {
10503 /* copy the attachment as is */
10504
10505 /** @todo the progress object created in Console::TakeSnaphot
10506 * only expects operations for hard disks. Later other
10507 * device types need to show up in the progress as well. */
10508 if (devType == DeviceType_HardDisk)
10509 {
10510 if (pMedium == NULL)
10511 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10512 aWeight); // weight
10513 else
10514 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10515 pMedium->getBase()->getName().c_str()).raw(),
10516 aWeight); // weight
10517 }
10518
10519 mMediaData->mAttachments.push_back(pAtt);
10520 continue;
10521 }
10522
10523 /* need a diff */
10524 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10525 pMedium->getBase()->getName().c_str()).raw(),
10526 aWeight); // weight
10527
10528 Utf8Str strFullSnapshotFolder;
10529 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10530
10531 ComObjPtr<Medium> diff;
10532 diff.createObject();
10533 // store the diff in the same registry as the parent
10534 // (this cannot fail here because we can't create implicit diffs for
10535 // unregistered images)
10536 Guid uuidRegistryParent;
10537 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10538 Assert(fInRegistry); NOREF(fInRegistry);
10539 rc = diff->init(mParent,
10540 pMedium->getPreferredDiffFormat(),
10541 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10542 uuidRegistryParent);
10543 if (FAILED(rc)) throw rc;
10544
10545 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10546 * the push_back? Looks like we're going to release medium with the
10547 * wrong kind of lock (general issue with if we fail anywhere at all)
10548 * and an orphaned VDI in the snapshots folder. */
10549
10550 /* update the appropriate lock list */
10551 MediumLockList *pMediumLockList;
10552 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10553 AssertComRCThrowRC(rc);
10554 if (aOnline)
10555 {
10556 alock.release();
10557 /* The currently attached medium will be read-only, change
10558 * the lock type to read. */
10559 rc = pMediumLockList->Update(pMedium, false);
10560 alock.acquire();
10561 AssertComRCThrowRC(rc);
10562 }
10563
10564 /* release the locks before the potentially lengthy operation */
10565 alock.release();
10566 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10567 pMediumLockList,
10568 NULL /* aProgress */,
10569 true /* aWait */);
10570 alock.acquire();
10571 if (FAILED(rc)) throw rc;
10572
10573 rc = lockedMediaMap->Unlock();
10574 AssertComRCThrowRC(rc);
10575 alock.release();
10576 rc = pMediumLockList->Append(diff, true);
10577 alock.acquire();
10578 AssertComRCThrowRC(rc);
10579 alock.release();
10580 rc = lockedMediaMap->Lock();
10581 alock.acquire();
10582 AssertComRCThrowRC(rc);
10583
10584 rc = diff->addBackReference(mData->mUuid);
10585 AssertComRCThrowRC(rc);
10586
10587 /* add a new attachment */
10588 ComObjPtr<MediumAttachment> attachment;
10589 attachment.createObject();
10590 rc = attachment->init(this,
10591 diff,
10592 pAtt->getControllerName(),
10593 pAtt->getPort(),
10594 pAtt->getDevice(),
10595 DeviceType_HardDisk,
10596 true /* aImplicit */,
10597 false /* aPassthrough */,
10598 false /* aTempEject */,
10599 pAtt->getNonRotational(),
10600 pAtt->getDiscard(),
10601 pAtt->getBandwidthGroup());
10602 if (FAILED(rc)) throw rc;
10603
10604 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10605 AssertComRCThrowRC(rc);
10606 mMediaData->mAttachments.push_back(attachment);
10607 }
10608 }
10609 catch (HRESULT aRC) { rc = aRC; }
10610
10611 /* unlock all hard disks we locked when there is no VM */
10612 if (!aOnline)
10613 {
10614 ErrorInfoKeeper eik;
10615
10616 HRESULT rc1 = lockedMediaMap->Clear();
10617 AssertComRC(rc1);
10618 }
10619
10620 return rc;
10621}
10622
10623/**
10624 * Deletes implicit differencing hard disks created either by
10625 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10626 *
10627 * Note that to delete hard disks created by #AttachDevice() this method is
10628 * called from #fixupMedia() when the changes are rolled back.
10629 *
10630 * @note Locks this object and the media tree for writing.
10631 */
10632HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10633{
10634 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10635
10636 AutoCaller autoCaller(this);
10637 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10638
10639 AutoMultiWriteLock2 alock(this->lockHandle(),
10640 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10641
10642 /* We absolutely must have backed up state. */
10643 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10644
10645 /* Check if there are any implicitly created diff images. */
10646 bool fImplicitDiffs = false;
10647 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10648 it != mMediaData->mAttachments.end();
10649 ++it)
10650 {
10651 const ComObjPtr<MediumAttachment> &pAtt = *it;
10652 if (pAtt->isImplicit())
10653 {
10654 fImplicitDiffs = true;
10655 break;
10656 }
10657 }
10658 /* If there is nothing to do, leave early. This saves lots of image locking
10659 * effort. It also avoids a MachineStateChanged event without real reason.
10660 * This is important e.g. when loading a VM config, because there should be
10661 * no events. Otherwise API clients can become thoroughly confused for
10662 * inaccessible VMs (the code for loading VM configs uses this method for
10663 * cleanup if the config makes no sense), as they take such events as an
10664 * indication that the VM is alive, and they would force the VM config to
10665 * be reread, leading to an endless loop. */
10666 if (!fImplicitDiffs)
10667 return S_OK;
10668
10669 HRESULT rc = S_OK;
10670 MachineState_T oldState = mData->mMachineState;
10671
10672 /* will release the lock before the potentially lengthy operation,
10673 * so protect with the special state (unless already protected) */
10674 if ( oldState != MachineState_Saving
10675 && oldState != MachineState_LiveSnapshotting
10676 && oldState != MachineState_RestoringSnapshot
10677 && oldState != MachineState_DeletingSnapshot
10678 && oldState != MachineState_DeletingSnapshotOnline
10679 && oldState != MachineState_DeletingSnapshotPaused
10680 )
10681 setMachineState(MachineState_SettingUp);
10682
10683 // use appropriate locked media map (online or offline)
10684 MediumLockListMap lockedMediaOffline;
10685 MediumLockListMap *lockedMediaMap;
10686 if (aOnline)
10687 lockedMediaMap = &mData->mSession.mLockedMedia;
10688 else
10689 lockedMediaMap = &lockedMediaOffline;
10690
10691 try
10692 {
10693 if (!aOnline)
10694 {
10695 /* lock all attached hard disks early to detect "in use"
10696 * situations before deleting actual diffs */
10697 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10698 it != mMediaData->mAttachments.end();
10699 ++it)
10700 {
10701 MediumAttachment* pAtt = *it;
10702 if (pAtt->getType() == DeviceType_HardDisk)
10703 {
10704 Medium* pMedium = pAtt->getMedium();
10705 Assert(pMedium);
10706
10707 MediumLockList *pMediumLockList(new MediumLockList());
10708 alock.release();
10709 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10710 false /* fMediumLockWrite */,
10711 NULL,
10712 *pMediumLockList);
10713 alock.acquire();
10714
10715 if (FAILED(rc))
10716 {
10717 delete pMediumLockList;
10718 throw rc;
10719 }
10720
10721 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10722 if (FAILED(rc))
10723 throw rc;
10724 }
10725 }
10726
10727 if (FAILED(rc))
10728 throw rc;
10729 } // end of offline
10730
10731 /* Lock lists are now up to date and include implicitly created media */
10732
10733 /* Go through remembered attachments and delete all implicitly created
10734 * diffs and fix up the attachment information */
10735 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10736 MediaData::AttachmentList implicitAtts;
10737 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10738 it != mMediaData->mAttachments.end();
10739 ++it)
10740 {
10741 ComObjPtr<MediumAttachment> pAtt = *it;
10742 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10743 if (pMedium.isNull())
10744 continue;
10745
10746 // Implicit attachments go on the list for deletion and back references are removed.
10747 if (pAtt->isImplicit())
10748 {
10749 /* Deassociate and mark for deletion */
10750 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10751 rc = pMedium->removeBackReference(mData->mUuid);
10752 if (FAILED(rc))
10753 throw rc;
10754 implicitAtts.push_back(pAtt);
10755 continue;
10756 }
10757
10758 /* Was this medium attached before? */
10759 if (!findAttachment(oldAtts, pMedium))
10760 {
10761 /* no: de-associate */
10762 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10763 rc = pMedium->removeBackReference(mData->mUuid);
10764 if (FAILED(rc))
10765 throw rc;
10766 continue;
10767 }
10768 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10769 }
10770
10771 /* If there are implicit attachments to delete, throw away the lock
10772 * map contents (which will unlock all media) since the medium
10773 * attachments will be rolled back. Below we need to completely
10774 * recreate the lock map anyway since it is infinitely complex to
10775 * do this incrementally (would need reconstructing each attachment
10776 * change, which would be extremely hairy). */
10777 if (implicitAtts.size() != 0)
10778 {
10779 ErrorInfoKeeper eik;
10780
10781 HRESULT rc1 = lockedMediaMap->Clear();
10782 AssertComRC(rc1);
10783 }
10784
10785 /* rollback hard disk changes */
10786 mMediaData.rollback();
10787
10788 MultiResult mrc(S_OK);
10789
10790 // Delete unused implicit diffs.
10791 if (implicitAtts.size() != 0)
10792 {
10793 alock.release();
10794
10795 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10796 it != implicitAtts.end();
10797 ++it)
10798 {
10799 // Remove medium associated with this attachment.
10800 ComObjPtr<MediumAttachment> pAtt = *it;
10801 Assert(pAtt);
10802 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10803 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10804 Assert(pMedium);
10805
10806 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10807 // continue on delete failure, just collect error messages
10808 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10809 mrc = rc;
10810 }
10811
10812 alock.acquire();
10813
10814 /* if there is a VM recreate media lock map as mentioned above,
10815 * otherwise it is a waste of time and we leave things unlocked */
10816 if (aOnline)
10817 {
10818 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10819 /* must never be NULL, but better safe than sorry */
10820 if (!pMachine.isNull())
10821 {
10822 alock.release();
10823 rc = mData->mSession.mMachine->lockMedia();
10824 alock.acquire();
10825 if (FAILED(rc))
10826 throw rc;
10827 }
10828 }
10829 }
10830 }
10831 catch (HRESULT aRC) {rc = aRC;}
10832
10833 if (mData->mMachineState == MachineState_SettingUp)
10834 setMachineState(oldState);
10835
10836 /* unlock all hard disks we locked when there is no VM */
10837 if (!aOnline)
10838 {
10839 ErrorInfoKeeper eik;
10840
10841 HRESULT rc1 = lockedMediaMap->Clear();
10842 AssertComRC(rc1);
10843 }
10844
10845 return rc;
10846}
10847
10848
10849/**
10850 * Looks through the given list of media attachments for one with the given parameters
10851 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10852 * can be searched as well if needed.
10853 *
10854 * @param list
10855 * @param aControllerName
10856 * @param aControllerPort
10857 * @param aDevice
10858 * @return
10859 */
10860MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10861 IN_BSTR aControllerName,
10862 LONG aControllerPort,
10863 LONG aDevice)
10864{
10865 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10866 it != ll.end();
10867 ++it)
10868 {
10869 MediumAttachment *pAttach = *it;
10870 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10871 return pAttach;
10872 }
10873
10874 return NULL;
10875}
10876
10877/**
10878 * Looks through the given list of media attachments for one with the given parameters
10879 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10880 * can be searched as well if needed.
10881 *
10882 * @param list
10883 * @param aControllerName
10884 * @param aControllerPort
10885 * @param aDevice
10886 * @return
10887 */
10888MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10889 ComObjPtr<Medium> pMedium)
10890{
10891 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10892 it != ll.end();
10893 ++it)
10894 {
10895 MediumAttachment *pAttach = *it;
10896 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10897 if (pMediumThis == pMedium)
10898 return pAttach;
10899 }
10900
10901 return NULL;
10902}
10903
10904/**
10905 * Looks through the given list of media attachments for one with the given parameters
10906 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10907 * can be searched as well if needed.
10908 *
10909 * @param list
10910 * @param aControllerName
10911 * @param aControllerPort
10912 * @param aDevice
10913 * @return
10914 */
10915MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10916 Guid &id)
10917{
10918 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10919 it != ll.end();
10920 ++it)
10921 {
10922 MediumAttachment *pAttach = *it;
10923 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10924 if (pMediumThis->getId() == id)
10925 return pAttach;
10926 }
10927
10928 return NULL;
10929}
10930
10931/**
10932 * Main implementation for Machine::DetachDevice. This also gets called
10933 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10934 *
10935 * @param pAttach Medium attachment to detach.
10936 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10937 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10938 * @return
10939 */
10940HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10941 AutoWriteLock &writeLock,
10942 Snapshot *pSnapshot)
10943{
10944 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10945 DeviceType_T mediumType = pAttach->getType();
10946
10947 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10948
10949 if (pAttach->isImplicit())
10950 {
10951 /* attempt to implicitly delete the implicitly created diff */
10952
10953 /// @todo move the implicit flag from MediumAttachment to Medium
10954 /// and forbid any hard disk operation when it is implicit. Or maybe
10955 /// a special media state for it to make it even more simple.
10956
10957 Assert(mMediaData.isBackedUp());
10958
10959 /* will release the lock before the potentially lengthy operation, so
10960 * protect with the special state */
10961 MachineState_T oldState = mData->mMachineState;
10962 setMachineState(MachineState_SettingUp);
10963
10964 writeLock.release();
10965
10966 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10967 true /*aWait*/);
10968
10969 writeLock.acquire();
10970
10971 setMachineState(oldState);
10972
10973 if (FAILED(rc)) return rc;
10974 }
10975
10976 setModified(IsModified_Storage);
10977 mMediaData.backup();
10978 mMediaData->mAttachments.remove(pAttach);
10979
10980 if (!oldmedium.isNull())
10981 {
10982 // if this is from a snapshot, do not defer detachment to commitMedia()
10983 if (pSnapshot)
10984 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10985 // else if non-hard disk media, do not defer detachment to commitMedia() either
10986 else if (mediumType != DeviceType_HardDisk)
10987 oldmedium->removeBackReference(mData->mUuid);
10988 }
10989
10990 return S_OK;
10991}
10992
10993/**
10994 * Goes thru all media of the given list and
10995 *
10996 * 1) calls detachDevice() on each of them for this machine and
10997 * 2) adds all Medium objects found in the process to the given list,
10998 * depending on cleanupMode.
10999 *
11000 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11001 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11002 * media to the list.
11003 *
11004 * This gets called from Machine::Unregister, both for the actual Machine and
11005 * the SnapshotMachine objects that might be found in the snapshots.
11006 *
11007 * Requires caller and locking. The machine lock must be passed in because it
11008 * will be passed on to detachDevice which needs it for temporary unlocking.
11009 *
11010 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11011 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11012 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11013 * otherwise no media get added.
11014 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11015 * @return
11016 */
11017HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11018 Snapshot *pSnapshot,
11019 CleanupMode_T cleanupMode,
11020 MediaList &llMedia)
11021{
11022 Assert(isWriteLockOnCurrentThread());
11023
11024 HRESULT rc;
11025
11026 // make a temporary list because detachDevice invalidates iterators into
11027 // mMediaData->mAttachments
11028 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11029
11030 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11031 it != llAttachments2.end();
11032 ++it)
11033 {
11034 ComObjPtr<MediumAttachment> &pAttach = *it;
11035 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11036
11037 if (!pMedium.isNull())
11038 {
11039 AutoCaller mac(pMedium);
11040 if (FAILED(mac.rc())) return mac.rc();
11041 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11042 DeviceType_T devType = pMedium->getDeviceType();
11043 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11044 && devType == DeviceType_HardDisk)
11045 || (cleanupMode == CleanupMode_Full)
11046 )
11047 {
11048 llMedia.push_back(pMedium);
11049 ComObjPtr<Medium> pParent = pMedium->getParent();
11050 /*
11051 * Search for medias which are not attached to any machine, but
11052 * in the chain to an attached disk. Mediums are only consided
11053 * if they are:
11054 * - have only one child
11055 * - no references to any machines
11056 * - are of normal medium type
11057 */
11058 while (!pParent.isNull())
11059 {
11060 AutoCaller mac1(pParent);
11061 if (FAILED(mac1.rc())) return mac1.rc();
11062 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11063 if (pParent->getChildren().size() == 1)
11064 {
11065 if ( pParent->getMachineBackRefCount() == 0
11066 && pParent->getType() == MediumType_Normal
11067 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11068 llMedia.push_back(pParent);
11069 }
11070 else
11071 break;
11072 pParent = pParent->getParent();
11073 }
11074 }
11075 }
11076
11077 // real machine: then we need to use the proper method
11078 rc = detachDevice(pAttach, writeLock, pSnapshot);
11079
11080 if (FAILED(rc))
11081 return rc;
11082 }
11083
11084 return S_OK;
11085}
11086
11087/**
11088 * Perform deferred hard disk detachments.
11089 *
11090 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11091 * backed up).
11092 *
11093 * If @a aOnline is @c true then this method will also unlock the old hard disks
11094 * for which the new implicit diffs were created and will lock these new diffs for
11095 * writing.
11096 *
11097 * @param aOnline Whether the VM was online prior to this operation.
11098 *
11099 * @note Locks this object for writing!
11100 */
11101void Machine::commitMedia(bool aOnline /*= false*/)
11102{
11103 AutoCaller autoCaller(this);
11104 AssertComRCReturnVoid(autoCaller.rc());
11105
11106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11107
11108 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11109
11110 HRESULT rc = S_OK;
11111
11112 /* no attach/detach operations -- nothing to do */
11113 if (!mMediaData.isBackedUp())
11114 return;
11115
11116 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11117 bool fMediaNeedsLocking = false;
11118
11119 /* enumerate new attachments */
11120 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11121 it != mMediaData->mAttachments.end();
11122 ++it)
11123 {
11124 MediumAttachment *pAttach = *it;
11125
11126 pAttach->commit();
11127
11128 Medium* pMedium = pAttach->getMedium();
11129 bool fImplicit = pAttach->isImplicit();
11130
11131 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11132 (pMedium) ? pMedium->getName().c_str() : "NULL",
11133 fImplicit));
11134
11135 /** @todo convert all this Machine-based voodoo to MediumAttachment
11136 * based commit logic. */
11137 if (fImplicit)
11138 {
11139 /* convert implicit attachment to normal */
11140 pAttach->setImplicit(false);
11141
11142 if ( aOnline
11143 && pMedium
11144 && pAttach->getType() == DeviceType_HardDisk
11145 )
11146 {
11147 ComObjPtr<Medium> parent = pMedium->getParent();
11148 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11149
11150 /* update the appropriate lock list */
11151 MediumLockList *pMediumLockList;
11152 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11153 AssertComRC(rc);
11154 if (pMediumLockList)
11155 {
11156 /* unlock if there's a need to change the locking */
11157 if (!fMediaNeedsLocking)
11158 {
11159 rc = mData->mSession.mLockedMedia.Unlock();
11160 AssertComRC(rc);
11161 fMediaNeedsLocking = true;
11162 }
11163 rc = pMediumLockList->Update(parent, false);
11164 AssertComRC(rc);
11165 rc = pMediumLockList->Append(pMedium, true);
11166 AssertComRC(rc);
11167 }
11168 }
11169
11170 continue;
11171 }
11172
11173 if (pMedium)
11174 {
11175 /* was this medium attached before? */
11176 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11177 oldIt != oldAtts.end();
11178 ++oldIt)
11179 {
11180 MediumAttachment *pOldAttach = *oldIt;
11181 if (pOldAttach->getMedium() == pMedium)
11182 {
11183 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11184
11185 /* yes: remove from old to avoid de-association */
11186 oldAtts.erase(oldIt);
11187 break;
11188 }
11189 }
11190 }
11191 }
11192
11193 /* enumerate remaining old attachments and de-associate from the
11194 * current machine state */
11195 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11196 it != oldAtts.end();
11197 ++it)
11198 {
11199 MediumAttachment *pAttach = *it;
11200 Medium* pMedium = pAttach->getMedium();
11201
11202 /* Detach only hard disks, since DVD/floppy media is detached
11203 * instantly in MountMedium. */
11204 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11205 {
11206 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11207
11208 /* now de-associate from the current machine state */
11209 rc = pMedium->removeBackReference(mData->mUuid);
11210 AssertComRC(rc);
11211
11212 if (aOnline)
11213 {
11214 /* unlock since medium is not used anymore */
11215 MediumLockList *pMediumLockList;
11216 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11217 AssertComRC(rc);
11218 if (pMediumLockList)
11219 {
11220 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11221 AssertComRC(rc);
11222 }
11223 }
11224 }
11225 }
11226
11227 /* take media locks again so that the locking state is consistent */
11228 if (fMediaNeedsLocking)
11229 {
11230 Assert(aOnline);
11231 rc = mData->mSession.mLockedMedia.Lock();
11232 AssertComRC(rc);
11233 }
11234
11235 /* commit the hard disk changes */
11236 mMediaData.commit();
11237
11238 if (isSessionMachine())
11239 {
11240 /*
11241 * Update the parent machine to point to the new owner.
11242 * This is necessary because the stored parent will point to the
11243 * session machine otherwise and cause crashes or errors later
11244 * when the session machine gets invalid.
11245 */
11246 /** @todo Change the MediumAttachment class to behave like any other
11247 * class in this regard by creating peer MediumAttachment
11248 * objects for session machines and share the data with the peer
11249 * machine.
11250 */
11251 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11252 it != mMediaData->mAttachments.end();
11253 ++it)
11254 {
11255 (*it)->updateParentMachine(mPeer);
11256 }
11257
11258 /* attach new data to the primary machine and reshare it */
11259 mPeer->mMediaData.attach(mMediaData);
11260 }
11261
11262 return;
11263}
11264
11265/**
11266 * Perform deferred deletion of implicitly created diffs.
11267 *
11268 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11269 * backed up).
11270 *
11271 * @note Locks this object for writing!
11272 */
11273void Machine::rollbackMedia()
11274{
11275 AutoCaller autoCaller(this);
11276 AssertComRCReturnVoid(autoCaller.rc());
11277
11278 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11279 LogFlowThisFunc(("Entering rollbackMedia\n"));
11280
11281 HRESULT rc = S_OK;
11282
11283 /* no attach/detach operations -- nothing to do */
11284 if (!mMediaData.isBackedUp())
11285 return;
11286
11287 /* enumerate new attachments */
11288 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11289 it != mMediaData->mAttachments.end();
11290 ++it)
11291 {
11292 MediumAttachment *pAttach = *it;
11293 /* Fix up the backrefs for DVD/floppy media. */
11294 if (pAttach->getType() != DeviceType_HardDisk)
11295 {
11296 Medium* pMedium = pAttach->getMedium();
11297 if (pMedium)
11298 {
11299 rc = pMedium->removeBackReference(mData->mUuid);
11300 AssertComRC(rc);
11301 }
11302 }
11303
11304 (*it)->rollback();
11305
11306 pAttach = *it;
11307 /* Fix up the backrefs for DVD/floppy media. */
11308 if (pAttach->getType() != DeviceType_HardDisk)
11309 {
11310 Medium* pMedium = pAttach->getMedium();
11311 if (pMedium)
11312 {
11313 rc = pMedium->addBackReference(mData->mUuid);
11314 AssertComRC(rc);
11315 }
11316 }
11317 }
11318
11319 /** @todo convert all this Machine-based voodoo to MediumAttachment
11320 * based rollback logic. */
11321 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11322
11323 return;
11324}
11325
11326/**
11327 * Returns true if the settings file is located in the directory named exactly
11328 * as the machine; this means, among other things, that the machine directory
11329 * should be auto-renamed.
11330 *
11331 * @param aSettingsDir if not NULL, the full machine settings file directory
11332 * name will be assigned there.
11333 *
11334 * @note Doesn't lock anything.
11335 * @note Not thread safe (must be called from this object's lock).
11336 */
11337bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11338{
11339 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11340 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11341 if (aSettingsDir)
11342 *aSettingsDir = strMachineDirName;
11343 strMachineDirName.stripPath(); // vmname
11344 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11345 strConfigFileOnly.stripPath() // vmname.vbox
11346 .stripExt(); // vmname
11347 /** @todo hack, make somehow use of ComposeMachineFilename */
11348 if (mUserData->s.fDirectoryIncludesUUID)
11349 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11350
11351 AssertReturn(!strMachineDirName.isEmpty(), false);
11352 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11353
11354 return strMachineDirName == strConfigFileOnly;
11355}
11356
11357/**
11358 * Discards all changes to machine settings.
11359 *
11360 * @param aNotify Whether to notify the direct session about changes or not.
11361 *
11362 * @note Locks objects for writing!
11363 */
11364void Machine::rollback(bool aNotify)
11365{
11366 AutoCaller autoCaller(this);
11367 AssertComRCReturn(autoCaller.rc(), (void)0);
11368
11369 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11370
11371 if (!mStorageControllers.isNull())
11372 {
11373 if (mStorageControllers.isBackedUp())
11374 {
11375 /* unitialize all new devices (absent in the backed up list). */
11376 StorageControllerList::const_iterator it = mStorageControllers->begin();
11377 StorageControllerList *backedList = mStorageControllers.backedUpData();
11378 while (it != mStorageControllers->end())
11379 {
11380 if ( std::find(backedList->begin(), backedList->end(), *it)
11381 == backedList->end()
11382 )
11383 {
11384 (*it)->uninit();
11385 }
11386 ++it;
11387 }
11388
11389 /* restore the list */
11390 mStorageControllers.rollback();
11391 }
11392
11393 /* rollback any changes to devices after restoring the list */
11394 if (mData->flModifications & IsModified_Storage)
11395 {
11396 StorageControllerList::const_iterator it = mStorageControllers->begin();
11397 while (it != mStorageControllers->end())
11398 {
11399 (*it)->rollback();
11400 ++it;
11401 }
11402 }
11403 }
11404
11405 mUserData.rollback();
11406
11407 mHWData.rollback();
11408
11409 if (mData->flModifications & IsModified_Storage)
11410 rollbackMedia();
11411
11412 if (mBIOSSettings)
11413 mBIOSSettings->rollback();
11414
11415 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11416 mVRDEServer->rollback();
11417
11418 if (mAudioAdapter)
11419 mAudioAdapter->rollback();
11420
11421 if (mUSBController && (mData->flModifications & IsModified_USB))
11422 mUSBController->rollback();
11423
11424 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11425 mBandwidthControl->rollback();
11426
11427 if (!mHWData.isNull())
11428 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11429 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11430 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11431 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11432
11433 if (mData->flModifications & IsModified_NetworkAdapters)
11434 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11435 if ( mNetworkAdapters[slot]
11436 && mNetworkAdapters[slot]->isModified())
11437 {
11438 mNetworkAdapters[slot]->rollback();
11439 networkAdapters[slot] = mNetworkAdapters[slot];
11440 }
11441
11442 if (mData->flModifications & IsModified_SerialPorts)
11443 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11444 if ( mSerialPorts[slot]
11445 && mSerialPorts[slot]->isModified())
11446 {
11447 mSerialPorts[slot]->rollback();
11448 serialPorts[slot] = mSerialPorts[slot];
11449 }
11450
11451 if (mData->flModifications & IsModified_ParallelPorts)
11452 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11453 if ( mParallelPorts[slot]
11454 && mParallelPorts[slot]->isModified())
11455 {
11456 mParallelPorts[slot]->rollback();
11457 parallelPorts[slot] = mParallelPorts[slot];
11458 }
11459
11460 if (aNotify)
11461 {
11462 /* inform the direct session about changes */
11463
11464 ComObjPtr<Machine> that = this;
11465 uint32_t flModifications = mData->flModifications;
11466 alock.release();
11467
11468 if (flModifications & IsModified_SharedFolders)
11469 that->onSharedFolderChange();
11470
11471 if (flModifications & IsModified_VRDEServer)
11472 that->onVRDEServerChange(/* aRestart */ TRUE);
11473 if (flModifications & IsModified_USB)
11474 that->onUSBControllerChange();
11475
11476 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11477 if (networkAdapters[slot])
11478 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11479 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11480 if (serialPorts[slot])
11481 that->onSerialPortChange(serialPorts[slot]);
11482 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11483 if (parallelPorts[slot])
11484 that->onParallelPortChange(parallelPorts[slot]);
11485
11486 if (flModifications & IsModified_Storage)
11487 that->onStorageControllerChange();
11488
11489#if 0
11490 if (flModifications & IsModified_BandwidthControl)
11491 that->onBandwidthControlChange();
11492#endif
11493 }
11494}
11495
11496/**
11497 * Commits all the changes to machine settings.
11498 *
11499 * Note that this operation is supposed to never fail.
11500 *
11501 * @note Locks this object and children for writing.
11502 */
11503void Machine::commit()
11504{
11505 AutoCaller autoCaller(this);
11506 AssertComRCReturnVoid(autoCaller.rc());
11507
11508 AutoCaller peerCaller(mPeer);
11509 AssertComRCReturnVoid(peerCaller.rc());
11510
11511 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11512
11513 /*
11514 * use safe commit to ensure Snapshot machines (that share mUserData)
11515 * will still refer to a valid memory location
11516 */
11517 mUserData.commitCopy();
11518
11519 mHWData.commit();
11520
11521 if (mMediaData.isBackedUp())
11522 commitMedia(Global::IsOnline(mData->mMachineState));
11523
11524 mBIOSSettings->commit();
11525 mVRDEServer->commit();
11526 mAudioAdapter->commit();
11527 mUSBController->commit();
11528 mBandwidthControl->commit();
11529
11530 /* Since mNetworkAdapters is a list which might have been changed (resized)
11531 * without using the Backupable<> template we need to handle the copying
11532 * of the list entries manually, including the creation of peers for the
11533 * new objects. */
11534 bool commitNetworkAdapters = false;
11535 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11536 if (mPeer)
11537 {
11538 /* commit everything, even the ones which will go away */
11539 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11540 mNetworkAdapters[slot]->commit();
11541 /* copy over the new entries, creating a peer and uninit the original */
11542 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11543 for (size_t slot = 0; slot < newSize; slot++)
11544 {
11545 /* look if this adapter has a peer device */
11546 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11547 if (!peer)
11548 {
11549 /* no peer means the adapter is a newly created one;
11550 * create a peer owning data this data share it with */
11551 peer.createObject();
11552 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11553 }
11554 mPeer->mNetworkAdapters[slot] = peer;
11555 }
11556 /* uninit any no longer needed network adapters */
11557 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11558 mNetworkAdapters[slot]->uninit();
11559 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11560 {
11561 if (mPeer->mNetworkAdapters[slot])
11562 mPeer->mNetworkAdapters[slot]->uninit();
11563 }
11564 /* Keep the original network adapter count until this point, so that
11565 * discarding a chipset type change will not lose settings. */
11566 mNetworkAdapters.resize(newSize);
11567 mPeer->mNetworkAdapters.resize(newSize);
11568 }
11569 else
11570 {
11571 /* we have no peer (our parent is the newly created machine);
11572 * just commit changes to the network adapters */
11573 commitNetworkAdapters = true;
11574 }
11575 if (commitNetworkAdapters)
11576 {
11577 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11578 mNetworkAdapters[slot]->commit();
11579 }
11580
11581 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11582 mSerialPorts[slot]->commit();
11583 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11584 mParallelPorts[slot]->commit();
11585
11586 bool commitStorageControllers = false;
11587
11588 if (mStorageControllers.isBackedUp())
11589 {
11590 mStorageControllers.commit();
11591
11592 if (mPeer)
11593 {
11594 /* Commit all changes to new controllers (this will reshare data with
11595 * peers for those who have peers) */
11596 StorageControllerList *newList = new StorageControllerList();
11597 StorageControllerList::const_iterator it = mStorageControllers->begin();
11598 while (it != mStorageControllers->end())
11599 {
11600 (*it)->commit();
11601
11602 /* look if this controller has a peer device */
11603 ComObjPtr<StorageController> peer = (*it)->getPeer();
11604 if (!peer)
11605 {
11606 /* no peer means the device is a newly created one;
11607 * create a peer owning data this device share it with */
11608 peer.createObject();
11609 peer->init(mPeer, *it, true /* aReshare */);
11610 }
11611 else
11612 {
11613 /* remove peer from the old list */
11614 mPeer->mStorageControllers->remove(peer);
11615 }
11616 /* and add it to the new list */
11617 newList->push_back(peer);
11618
11619 ++it;
11620 }
11621
11622 /* uninit old peer's controllers that are left */
11623 it = mPeer->mStorageControllers->begin();
11624 while (it != mPeer->mStorageControllers->end())
11625 {
11626 (*it)->uninit();
11627 ++it;
11628 }
11629
11630 /* attach new list of controllers to our peer */
11631 mPeer->mStorageControllers.attach(newList);
11632 }
11633 else
11634 {
11635 /* we have no peer (our parent is the newly created machine);
11636 * just commit changes to devices */
11637 commitStorageControllers = true;
11638 }
11639 }
11640 else
11641 {
11642 /* the list of controllers itself is not changed,
11643 * just commit changes to controllers themselves */
11644 commitStorageControllers = true;
11645 }
11646
11647 if (commitStorageControllers)
11648 {
11649 StorageControllerList::const_iterator it = mStorageControllers->begin();
11650 while (it != mStorageControllers->end())
11651 {
11652 (*it)->commit();
11653 ++it;
11654 }
11655 }
11656
11657 if (isSessionMachine())
11658 {
11659 /* attach new data to the primary machine and reshare it */
11660 mPeer->mUserData.attach(mUserData);
11661 mPeer->mHWData.attach(mHWData);
11662 /* mMediaData is reshared by fixupMedia */
11663 // mPeer->mMediaData.attach(mMediaData);
11664 Assert(mPeer->mMediaData.data() == mMediaData.data());
11665 }
11666}
11667
11668/**
11669 * Copies all the hardware data from the given machine.
11670 *
11671 * Currently, only called when the VM is being restored from a snapshot. In
11672 * particular, this implies that the VM is not running during this method's
11673 * call.
11674 *
11675 * @note This method must be called from under this object's lock.
11676 *
11677 * @note This method doesn't call #commit(), so all data remains backed up and
11678 * unsaved.
11679 */
11680void Machine::copyFrom(Machine *aThat)
11681{
11682 AssertReturnVoid(!isSnapshotMachine());
11683 AssertReturnVoid(aThat->isSnapshotMachine());
11684
11685 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11686
11687 mHWData.assignCopy(aThat->mHWData);
11688
11689 // create copies of all shared folders (mHWData after attaching a copy
11690 // contains just references to original objects)
11691 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11692 it != mHWData->mSharedFolders.end();
11693 ++it)
11694 {
11695 ComObjPtr<SharedFolder> folder;
11696 folder.createObject();
11697 HRESULT rc = folder->initCopy(getMachine(), *it);
11698 AssertComRC(rc);
11699 *it = folder;
11700 }
11701
11702 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11703 mVRDEServer->copyFrom(aThat->mVRDEServer);
11704 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11705 mUSBController->copyFrom(aThat->mUSBController);
11706 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11707
11708 /* create private copies of all controllers */
11709 mStorageControllers.backup();
11710 mStorageControllers->clear();
11711 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11712 it != aThat->mStorageControllers->end();
11713 ++it)
11714 {
11715 ComObjPtr<StorageController> ctrl;
11716 ctrl.createObject();
11717 ctrl->initCopy(this, *it);
11718 mStorageControllers->push_back(ctrl);
11719 }
11720
11721 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11722 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11723 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11724 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11725 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11726 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11727 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11728}
11729
11730/**
11731 * Returns whether the given storage controller is hotplug capable.
11732 *
11733 * @returns true if the controller supports hotplugging
11734 * false otherwise.
11735 * @param enmCtrlType The controller type to check for.
11736 */
11737bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11738{
11739 switch (enmCtrlType)
11740 {
11741 case StorageControllerType_IntelAhci:
11742 return true;
11743 case StorageControllerType_LsiLogic:
11744 case StorageControllerType_LsiLogicSas:
11745 case StorageControllerType_BusLogic:
11746 case StorageControllerType_PIIX3:
11747 case StorageControllerType_PIIX4:
11748 case StorageControllerType_ICH6:
11749 case StorageControllerType_I82078:
11750 default:
11751 return false;
11752 }
11753}
11754
11755#ifdef VBOX_WITH_RESOURCE_USAGE_API
11756
11757void Machine::getDiskList(MediaList &list)
11758{
11759 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11760 it != mMediaData->mAttachments.end();
11761 ++it)
11762 {
11763 MediumAttachment* pAttach = *it;
11764 /* just in case */
11765 AssertStmt(pAttach, continue);
11766
11767 AutoCaller localAutoCallerA(pAttach);
11768 if (FAILED(localAutoCallerA.rc())) continue;
11769
11770 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11771
11772 if (pAttach->getType() == DeviceType_HardDisk)
11773 list.push_back(pAttach->getMedium());
11774 }
11775}
11776
11777void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11778{
11779 AssertReturnVoid(isWriteLockOnCurrentThread());
11780 AssertPtrReturnVoid(aCollector);
11781
11782 pm::CollectorHAL *hal = aCollector->getHAL();
11783 /* Create sub metrics */
11784 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11785 "Percentage of processor time spent in user mode by the VM process.");
11786 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11787 "Percentage of processor time spent in kernel mode by the VM process.");
11788 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11789 "Size of resident portion of VM process in memory.");
11790 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11791 "Actual size of all VM disks combined.");
11792 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11793 "Network receive rate.");
11794 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11795 "Network transmit rate.");
11796 /* Create and register base metrics */
11797 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11798 cpuLoadUser, cpuLoadKernel);
11799 aCollector->registerBaseMetric(cpuLoad);
11800 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11801 ramUsageUsed);
11802 aCollector->registerBaseMetric(ramUsage);
11803 MediaList disks;
11804 getDiskList(disks);
11805 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11806 diskUsageUsed);
11807 aCollector->registerBaseMetric(diskUsage);
11808
11809 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11810 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11811 new pm::AggregateAvg()));
11812 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11813 new pm::AggregateMin()));
11814 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11815 new pm::AggregateMax()));
11816 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11817 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11818 new pm::AggregateAvg()));
11819 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11820 new pm::AggregateMin()));
11821 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11822 new pm::AggregateMax()));
11823
11824 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11825 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11826 new pm::AggregateAvg()));
11827 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11828 new pm::AggregateMin()));
11829 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11830 new pm::AggregateMax()));
11831
11832 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11833 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11834 new pm::AggregateAvg()));
11835 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11836 new pm::AggregateMin()));
11837 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11838 new pm::AggregateMax()));
11839
11840
11841 /* Guest metrics collector */
11842 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11843 aCollector->registerGuest(mCollectorGuest);
11844 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11845 this, __PRETTY_FUNCTION__, mCollectorGuest));
11846
11847 /* Create sub metrics */
11848 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11849 "Percentage of processor time spent in user mode as seen by the guest.");
11850 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11851 "Percentage of processor time spent in kernel mode as seen by the guest.");
11852 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11853 "Percentage of processor time spent idling as seen by the guest.");
11854
11855 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11856 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11857 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11858 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11859 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11860 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11861
11862 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11863
11864 /* Create and register base metrics */
11865 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11866 machineNetRx, machineNetTx);
11867 aCollector->registerBaseMetric(machineNetRate);
11868
11869 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11870 guestLoadUser, guestLoadKernel, guestLoadIdle);
11871 aCollector->registerBaseMetric(guestCpuLoad);
11872
11873 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11874 guestMemTotal, guestMemFree,
11875 guestMemBalloon, guestMemShared,
11876 guestMemCache, guestPagedTotal);
11877 aCollector->registerBaseMetric(guestCpuMem);
11878
11879 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11880 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11881 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11882 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11883
11884 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11885 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11886 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11887 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11888
11889 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11890 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11891 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11892 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11893
11894 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11895 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11896 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11897 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11898
11899 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11900 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11901 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11902 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11903
11904 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11905 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11906 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11907 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11908
11909 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11910 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11911 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11912 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11913
11914 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11915 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11916 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11917 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11918
11919 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11920 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11921 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11922 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11923
11924 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11925 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11926 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11927 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11928
11929 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11930 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11931 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11932 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11933}
11934
11935void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11936{
11937 AssertReturnVoid(isWriteLockOnCurrentThread());
11938
11939 if (aCollector)
11940 {
11941 aCollector->unregisterMetricsFor(aMachine);
11942 aCollector->unregisterBaseMetricsFor(aMachine);
11943 }
11944}
11945
11946#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11947
11948
11949////////////////////////////////////////////////////////////////////////////////
11950
11951DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11952
11953HRESULT SessionMachine::FinalConstruct()
11954{
11955 LogFlowThisFunc(("\n"));
11956
11957#if defined(RT_OS_WINDOWS)
11958 mIPCSem = NULL;
11959#elif defined(RT_OS_OS2)
11960 mIPCSem = NULLHANDLE;
11961#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11962 mIPCSem = -1;
11963#else
11964# error "Port me!"
11965#endif
11966
11967 return BaseFinalConstruct();
11968}
11969
11970void SessionMachine::FinalRelease()
11971{
11972 LogFlowThisFunc(("\n"));
11973
11974 uninit(Uninit::Unexpected);
11975
11976 BaseFinalRelease();
11977}
11978
11979/**
11980 * @note Must be called only by Machine::openSession() from its own write lock.
11981 */
11982HRESULT SessionMachine::init(Machine *aMachine)
11983{
11984 LogFlowThisFuncEnter();
11985 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11986
11987 AssertReturn(aMachine, E_INVALIDARG);
11988
11989 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11990
11991 /* Enclose the state transition NotReady->InInit->Ready */
11992 AutoInitSpan autoInitSpan(this);
11993 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11994
11995 /* create the interprocess semaphore */
11996#if defined(RT_OS_WINDOWS)
11997 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11998 for (size_t i = 0; i < mIPCSemName.length(); i++)
11999 if (mIPCSemName.raw()[i] == '\\')
12000 mIPCSemName.raw()[i] = '/';
12001 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12002 ComAssertMsgRet(mIPCSem,
12003 ("Cannot create IPC mutex '%ls', err=%d",
12004 mIPCSemName.raw(), ::GetLastError()),
12005 E_FAIL);
12006#elif defined(RT_OS_OS2)
12007 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12008 aMachine->mData->mUuid.raw());
12009 mIPCSemName = ipcSem;
12010 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12011 ComAssertMsgRet(arc == NO_ERROR,
12012 ("Cannot create IPC mutex '%s', arc=%ld",
12013 ipcSem.c_str(), arc),
12014 E_FAIL);
12015#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12016# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12017# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12018 /** @todo Check that this still works correctly. */
12019 AssertCompileSize(key_t, 8);
12020# else
12021 AssertCompileSize(key_t, 4);
12022# endif
12023 key_t key;
12024 mIPCSem = -1;
12025 mIPCKey = "0";
12026 for (uint32_t i = 0; i < 1 << 24; i++)
12027 {
12028 key = ((uint32_t)'V' << 24) | i;
12029 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12030 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12031 {
12032 mIPCSem = sem;
12033 if (sem >= 0)
12034 mIPCKey = BstrFmt("%u", key);
12035 break;
12036 }
12037 }
12038# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12039 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12040 char *pszSemName = NULL;
12041 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12042 key_t key = ::ftok(pszSemName, 'V');
12043 RTStrFree(pszSemName);
12044
12045 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12046# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12047
12048 int errnoSave = errno;
12049 if (mIPCSem < 0 && errnoSave == ENOSYS)
12050 {
12051 setError(E_FAIL,
12052 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12053 "support for SysV IPC. Check the host kernel configuration for "
12054 "CONFIG_SYSVIPC=y"));
12055 return E_FAIL;
12056 }
12057 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12058 * the IPC semaphores */
12059 if (mIPCSem < 0 && errnoSave == ENOSPC)
12060 {
12061#ifdef RT_OS_LINUX
12062 setError(E_FAIL,
12063 tr("Cannot create IPC semaphore because the system limit for the "
12064 "maximum number of semaphore sets (SEMMNI), or the system wide "
12065 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12066 "current set of SysV IPC semaphores can be determined from "
12067 "the file /proc/sysvipc/sem"));
12068#else
12069 setError(E_FAIL,
12070 tr("Cannot create IPC semaphore because the system-imposed limit "
12071 "on the maximum number of allowed semaphores or semaphore "
12072 "identifiers system-wide would be exceeded"));
12073#endif
12074 return E_FAIL;
12075 }
12076 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12077 E_FAIL);
12078 /* set the initial value to 1 */
12079 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12080 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12081 E_FAIL);
12082#else
12083# error "Port me!"
12084#endif
12085
12086 /* memorize the peer Machine */
12087 unconst(mPeer) = aMachine;
12088 /* share the parent pointer */
12089 unconst(mParent) = aMachine->mParent;
12090
12091 /* take the pointers to data to share */
12092 mData.share(aMachine->mData);
12093 mSSData.share(aMachine->mSSData);
12094
12095 mUserData.share(aMachine->mUserData);
12096 mHWData.share(aMachine->mHWData);
12097 mMediaData.share(aMachine->mMediaData);
12098
12099 mStorageControllers.allocate();
12100 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12101 it != aMachine->mStorageControllers->end();
12102 ++it)
12103 {
12104 ComObjPtr<StorageController> ctl;
12105 ctl.createObject();
12106 ctl->init(this, *it);
12107 mStorageControllers->push_back(ctl);
12108 }
12109
12110 unconst(mBIOSSettings).createObject();
12111 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12112 /* create another VRDEServer object that will be mutable */
12113 unconst(mVRDEServer).createObject();
12114 mVRDEServer->init(this, aMachine->mVRDEServer);
12115 /* create another audio adapter object that will be mutable */
12116 unconst(mAudioAdapter).createObject();
12117 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12118 /* create a list of serial ports that will be mutable */
12119 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12120 {
12121 unconst(mSerialPorts[slot]).createObject();
12122 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12123 }
12124 /* create a list of parallel ports that will be mutable */
12125 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12126 {
12127 unconst(mParallelPorts[slot]).createObject();
12128 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12129 }
12130 /* create another USB controller object that will be mutable */
12131 unconst(mUSBController).createObject();
12132 mUSBController->init(this, aMachine->mUSBController);
12133
12134 /* create a list of network adapters that will be mutable */
12135 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12136 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12137 {
12138 unconst(mNetworkAdapters[slot]).createObject();
12139 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12140 }
12141
12142 /* create another bandwidth control object that will be mutable */
12143 unconst(mBandwidthControl).createObject();
12144 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12145
12146 /* default is to delete saved state on Saved -> PoweredOff transition */
12147 mRemoveSavedState = true;
12148
12149 /* Confirm a successful initialization when it's the case */
12150 autoInitSpan.setSucceeded();
12151
12152 LogFlowThisFuncLeave();
12153 return S_OK;
12154}
12155
12156/**
12157 * Uninitializes this session object. If the reason is other than
12158 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12159 *
12160 * @param aReason uninitialization reason
12161 *
12162 * @note Locks mParent + this object for writing.
12163 */
12164void SessionMachine::uninit(Uninit::Reason aReason)
12165{
12166 LogFlowThisFuncEnter();
12167 LogFlowThisFunc(("reason=%d\n", aReason));
12168
12169 /*
12170 * Strongly reference ourselves to prevent this object deletion after
12171 * mData->mSession.mMachine.setNull() below (which can release the last
12172 * reference and call the destructor). Important: this must be done before
12173 * accessing any members (and before AutoUninitSpan that does it as well).
12174 * This self reference will be released as the very last step on return.
12175 */
12176 ComObjPtr<SessionMachine> selfRef = this;
12177
12178 /* Enclose the state transition Ready->InUninit->NotReady */
12179 AutoUninitSpan autoUninitSpan(this);
12180 if (autoUninitSpan.uninitDone())
12181 {
12182 LogFlowThisFunc(("Already uninitialized\n"));
12183 LogFlowThisFuncLeave();
12184 return;
12185 }
12186
12187 if (autoUninitSpan.initFailed())
12188 {
12189 /* We've been called by init() because it's failed. It's not really
12190 * necessary (nor it's safe) to perform the regular uninit sequence
12191 * below, the following is enough.
12192 */
12193 LogFlowThisFunc(("Initialization failed.\n"));
12194#if defined(RT_OS_WINDOWS)
12195 if (mIPCSem)
12196 ::CloseHandle(mIPCSem);
12197 mIPCSem = NULL;
12198#elif defined(RT_OS_OS2)
12199 if (mIPCSem != NULLHANDLE)
12200 ::DosCloseMutexSem(mIPCSem);
12201 mIPCSem = NULLHANDLE;
12202#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12203 if (mIPCSem >= 0)
12204 ::semctl(mIPCSem, 0, IPC_RMID);
12205 mIPCSem = -1;
12206# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12207 mIPCKey = "0";
12208# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12209#else
12210# error "Port me!"
12211#endif
12212 uninitDataAndChildObjects();
12213 mData.free();
12214 unconst(mParent) = NULL;
12215 unconst(mPeer) = NULL;
12216 LogFlowThisFuncLeave();
12217 return;
12218 }
12219
12220 MachineState_T lastState;
12221 {
12222 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12223 lastState = mData->mMachineState;
12224 }
12225 NOREF(lastState);
12226
12227#ifdef VBOX_WITH_USB
12228 // release all captured USB devices, but do this before requesting the locks below
12229 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12230 {
12231 /* Console::captureUSBDevices() is called in the VM process only after
12232 * setting the machine state to Starting or Restoring.
12233 * Console::detachAllUSBDevices() will be called upon successful
12234 * termination. So, we need to release USB devices only if there was
12235 * an abnormal termination of a running VM.
12236 *
12237 * This is identical to SessionMachine::DetachAllUSBDevices except
12238 * for the aAbnormal argument. */
12239 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12240 AssertComRC(rc);
12241 NOREF(rc);
12242
12243 USBProxyService *service = mParent->host()->usbProxyService();
12244 if (service)
12245 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12246 }
12247#endif /* VBOX_WITH_USB */
12248
12249 // we need to lock this object in uninit() because the lock is shared
12250 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12251 // and others need mParent lock, and USB needs host lock.
12252 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12253
12254#if 0
12255 // Trigger async cleanup tasks, avoid doing things here which are not
12256 // vital to be done immediately and maybe need more locks. This calls
12257 // Machine::unregisterMetrics().
12258 mParent->onMachineUninit(mPeer);
12259#else
12260 /*
12261 * It is safe to call Machine::unregisterMetrics() here because
12262 * PerformanceCollector::samplerCallback no longer accesses guest methods
12263 * holding the lock.
12264 */
12265 unregisterMetrics(mParent->performanceCollector(), mPeer);
12266#endif
12267 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12268 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12269 this, __PRETTY_FUNCTION__, mCollectorGuest));
12270 if (mCollectorGuest)
12271 {
12272 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12273 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12274 mCollectorGuest = NULL;
12275 }
12276
12277 if (aReason == Uninit::Abnormal)
12278 {
12279 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12280 Global::IsOnlineOrTransient(lastState)));
12281
12282 /* reset the state to Aborted */
12283 if (mData->mMachineState != MachineState_Aborted)
12284 setMachineState(MachineState_Aborted);
12285 }
12286
12287 // any machine settings modified?
12288 if (mData->flModifications)
12289 {
12290 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12291 rollback(false /* aNotify */);
12292 }
12293
12294 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12295 || !mConsoleTaskData.mSnapshot);
12296 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12297 {
12298 LogWarningThisFunc(("canceling failed save state request!\n"));
12299 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12300 }
12301 else if (!mConsoleTaskData.mSnapshot.isNull())
12302 {
12303 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12304
12305 /* delete all differencing hard disks created (this will also attach
12306 * their parents back by rolling back mMediaData) */
12307 rollbackMedia();
12308
12309 // delete the saved state file (it might have been already created)
12310 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12311 // think it's still in use
12312 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12313 mConsoleTaskData.mSnapshot->uninit();
12314 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12315 }
12316
12317 if (!mData->mSession.mType.isEmpty())
12318 {
12319 /* mType is not null when this machine's process has been started by
12320 * Machine::LaunchVMProcess(), therefore it is our child. We
12321 * need to queue the PID to reap the process (and avoid zombies on
12322 * Linux). */
12323 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12324 mParent->addProcessToReap(mData->mSession.mPID);
12325 }
12326
12327 mData->mSession.mPID = NIL_RTPROCESS;
12328
12329 if (aReason == Uninit::Unexpected)
12330 {
12331 /* Uninitialization didn't come from #checkForDeath(), so tell the
12332 * client watcher thread to update the set of machines that have open
12333 * sessions. */
12334 mParent->updateClientWatcher();
12335 }
12336
12337 /* uninitialize all remote controls */
12338 if (mData->mSession.mRemoteControls.size())
12339 {
12340 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12341 mData->mSession.mRemoteControls.size()));
12342
12343 Data::Session::RemoteControlList::iterator it =
12344 mData->mSession.mRemoteControls.begin();
12345 while (it != mData->mSession.mRemoteControls.end())
12346 {
12347 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12348 HRESULT rc = (*it)->Uninitialize();
12349 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12350 if (FAILED(rc))
12351 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12352 ++it;
12353 }
12354 mData->mSession.mRemoteControls.clear();
12355 }
12356
12357 /*
12358 * An expected uninitialization can come only from #checkForDeath().
12359 * Otherwise it means that something's gone really wrong (for example,
12360 * the Session implementation has released the VirtualBox reference
12361 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12362 * etc). However, it's also possible, that the client releases the IPC
12363 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12364 * but the VirtualBox release event comes first to the server process.
12365 * This case is practically possible, so we should not assert on an
12366 * unexpected uninit, just log a warning.
12367 */
12368
12369 if ((aReason == Uninit::Unexpected))
12370 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12371
12372 if (aReason != Uninit::Normal)
12373 {
12374 mData->mSession.mDirectControl.setNull();
12375 }
12376 else
12377 {
12378 /* this must be null here (see #OnSessionEnd()) */
12379 Assert(mData->mSession.mDirectControl.isNull());
12380 Assert(mData->mSession.mState == SessionState_Unlocking);
12381 Assert(!mData->mSession.mProgress.isNull());
12382 }
12383 if (mData->mSession.mProgress)
12384 {
12385 if (aReason == Uninit::Normal)
12386 mData->mSession.mProgress->notifyComplete(S_OK);
12387 else
12388 mData->mSession.mProgress->notifyComplete(E_FAIL,
12389 COM_IIDOF(ISession),
12390 getComponentName(),
12391 tr("The VM session was aborted"));
12392 mData->mSession.mProgress.setNull();
12393 }
12394
12395 /* remove the association between the peer machine and this session machine */
12396 Assert( (SessionMachine*)mData->mSession.mMachine == this
12397 || aReason == Uninit::Unexpected);
12398
12399 /* reset the rest of session data */
12400 mData->mSession.mMachine.setNull();
12401 mData->mSession.mState = SessionState_Unlocked;
12402 mData->mSession.mType.setNull();
12403
12404 /* close the interprocess semaphore before leaving the exclusive lock */
12405#if defined(RT_OS_WINDOWS)
12406 if (mIPCSem)
12407 ::CloseHandle(mIPCSem);
12408 mIPCSem = NULL;
12409#elif defined(RT_OS_OS2)
12410 if (mIPCSem != NULLHANDLE)
12411 ::DosCloseMutexSem(mIPCSem);
12412 mIPCSem = NULLHANDLE;
12413#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12414 if (mIPCSem >= 0)
12415 ::semctl(mIPCSem, 0, IPC_RMID);
12416 mIPCSem = -1;
12417# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12418 mIPCKey = "0";
12419# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12420#else
12421# error "Port me!"
12422#endif
12423
12424 /* fire an event */
12425 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12426
12427 uninitDataAndChildObjects();
12428
12429 /* free the essential data structure last */
12430 mData.free();
12431
12432 /* release the exclusive lock before setting the below two to NULL */
12433 multilock.release();
12434
12435 unconst(mParent) = NULL;
12436 unconst(mPeer) = NULL;
12437
12438 LogFlowThisFuncLeave();
12439}
12440
12441// util::Lockable interface
12442////////////////////////////////////////////////////////////////////////////////
12443
12444/**
12445 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12446 * with the primary Machine instance (mPeer).
12447 */
12448RWLockHandle *SessionMachine::lockHandle() const
12449{
12450 AssertReturn(mPeer != NULL, NULL);
12451 return mPeer->lockHandle();
12452}
12453
12454// IInternalMachineControl methods
12455////////////////////////////////////////////////////////////////////////////////
12456
12457/**
12458 * Passes collected guest statistics to performance collector object
12459 */
12460STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12461 ULONG aCpuKernel, ULONG aCpuIdle,
12462 ULONG aMemTotal, ULONG aMemFree,
12463 ULONG aMemBalloon, ULONG aMemShared,
12464 ULONG aMemCache, ULONG aPageTotal,
12465 ULONG aAllocVMM, ULONG aFreeVMM,
12466 ULONG aBalloonedVMM, ULONG aSharedVMM,
12467 ULONG aVmNetRx, ULONG aVmNetTx)
12468{
12469 if (mCollectorGuest)
12470 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12471 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12472 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12473 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12474
12475 return S_OK;
12476}
12477
12478/**
12479 * @note Locks this object for writing.
12480 */
12481STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12482{
12483 AutoCaller autoCaller(this);
12484 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12485
12486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12487
12488 mRemoveSavedState = aRemove;
12489
12490 return S_OK;
12491}
12492
12493/**
12494 * @note Locks the same as #setMachineState() does.
12495 */
12496STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12497{
12498 return setMachineState(aMachineState);
12499}
12500
12501/**
12502 * @note Locks this object for reading.
12503 */
12504STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12505{
12506 AutoCaller autoCaller(this);
12507 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12508
12509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12510
12511#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12512 mIPCSemName.cloneTo(aId);
12513 return S_OK;
12514#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12515# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12516 mIPCKey.cloneTo(aId);
12517# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12518 mData->m_strConfigFileFull.cloneTo(aId);
12519# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12520 return S_OK;
12521#else
12522# error "Port me!"
12523#endif
12524}
12525
12526/**
12527 * @note Locks this object for writing.
12528 */
12529STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12530{
12531 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12532 AutoCaller autoCaller(this);
12533 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12534
12535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12536
12537 if (mData->mSession.mState != SessionState_Locked)
12538 return VBOX_E_INVALID_OBJECT_STATE;
12539
12540 if (!mData->mSession.mProgress.isNull())
12541 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12542
12543 LogFlowThisFunc(("returns S_OK.\n"));
12544 return S_OK;
12545}
12546
12547/**
12548 * @note Locks this object for writing.
12549 */
12550STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12551{
12552 AutoCaller autoCaller(this);
12553 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12554
12555 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12556
12557 if (mData->mSession.mState != SessionState_Locked)
12558 return VBOX_E_INVALID_OBJECT_STATE;
12559
12560 /* Finalize the LaunchVMProcess progress object. */
12561 if (mData->mSession.mProgress)
12562 {
12563 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12564 mData->mSession.mProgress.setNull();
12565 }
12566
12567 if (SUCCEEDED((HRESULT)iResult))
12568 {
12569#ifdef VBOX_WITH_RESOURCE_USAGE_API
12570 /* The VM has been powered up successfully, so it makes sense
12571 * now to offer the performance metrics for a running machine
12572 * object. Doing it earlier wouldn't be safe. */
12573 registerMetrics(mParent->performanceCollector(), mPeer,
12574 mData->mSession.mPID);
12575#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12576 }
12577
12578 return S_OK;
12579}
12580
12581/**
12582 * @note Locks this object for writing.
12583 */
12584STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12585{
12586 LogFlowThisFuncEnter();
12587
12588 CheckComArgOutPointerValid(aProgress);
12589
12590 AutoCaller autoCaller(this);
12591 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12592
12593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12594
12595 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12596 E_FAIL);
12597
12598 /* create a progress object to track operation completion */
12599 ComObjPtr<Progress> pProgress;
12600 pProgress.createObject();
12601 pProgress->init(getVirtualBox(),
12602 static_cast<IMachine *>(this) /* aInitiator */,
12603 Bstr(tr("Stopping the virtual machine")).raw(),
12604 FALSE /* aCancelable */);
12605
12606 /* fill in the console task data */
12607 mConsoleTaskData.mLastState = mData->mMachineState;
12608 mConsoleTaskData.mProgress = pProgress;
12609
12610 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12611 setMachineState(MachineState_Stopping);
12612
12613 pProgress.queryInterfaceTo(aProgress);
12614
12615 return S_OK;
12616}
12617
12618/**
12619 * @note Locks this object for writing.
12620 */
12621STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12622{
12623 LogFlowThisFuncEnter();
12624
12625 AutoCaller autoCaller(this);
12626 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12627
12628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12629
12630 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12631 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12632 && mConsoleTaskData.mLastState != MachineState_Null,
12633 E_FAIL);
12634
12635 /*
12636 * On failure, set the state to the state we had when BeginPoweringDown()
12637 * was called (this is expected by Console::PowerDown() and the associated
12638 * task). On success the VM process already changed the state to
12639 * MachineState_PoweredOff, so no need to do anything.
12640 */
12641 if (FAILED(iResult))
12642 setMachineState(mConsoleTaskData.mLastState);
12643
12644 /* notify the progress object about operation completion */
12645 Assert(mConsoleTaskData.mProgress);
12646 if (SUCCEEDED(iResult))
12647 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12648 else
12649 {
12650 Utf8Str strErrMsg(aErrMsg);
12651 if (strErrMsg.length())
12652 mConsoleTaskData.mProgress->notifyComplete(iResult,
12653 COM_IIDOF(ISession),
12654 getComponentName(),
12655 strErrMsg.c_str());
12656 else
12657 mConsoleTaskData.mProgress->notifyComplete(iResult);
12658 }
12659
12660 /* clear out the temporary saved state data */
12661 mConsoleTaskData.mLastState = MachineState_Null;
12662 mConsoleTaskData.mProgress.setNull();
12663
12664 LogFlowThisFuncLeave();
12665 return S_OK;
12666}
12667
12668
12669/**
12670 * Goes through the USB filters of the given machine to see if the given
12671 * device matches any filter or not.
12672 *
12673 * @note Locks the same as USBController::hasMatchingFilter() does.
12674 */
12675STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12676 BOOL *aMatched,
12677 ULONG *aMaskedIfs)
12678{
12679 LogFlowThisFunc(("\n"));
12680
12681 CheckComArgNotNull(aUSBDevice);
12682 CheckComArgOutPointerValid(aMatched);
12683
12684 AutoCaller autoCaller(this);
12685 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12686
12687#ifdef VBOX_WITH_USB
12688 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12689#else
12690 NOREF(aUSBDevice);
12691 NOREF(aMaskedIfs);
12692 *aMatched = FALSE;
12693#endif
12694
12695 return S_OK;
12696}
12697
12698/**
12699 * @note Locks the same as Host::captureUSBDevice() does.
12700 */
12701STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12702{
12703 LogFlowThisFunc(("\n"));
12704
12705 AutoCaller autoCaller(this);
12706 AssertComRCReturnRC(autoCaller.rc());
12707
12708#ifdef VBOX_WITH_USB
12709 /* if captureDeviceForVM() fails, it must have set extended error info */
12710 clearError();
12711 MultiResult rc = mParent->host()->checkUSBProxyService();
12712 if (FAILED(rc)) return rc;
12713
12714 USBProxyService *service = mParent->host()->usbProxyService();
12715 AssertReturn(service, E_FAIL);
12716 return service->captureDeviceForVM(this, Guid(aId).ref());
12717#else
12718 NOREF(aId);
12719 return E_NOTIMPL;
12720#endif
12721}
12722
12723/**
12724 * @note Locks the same as Host::detachUSBDevice() does.
12725 */
12726STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12727{
12728 LogFlowThisFunc(("\n"));
12729
12730 AutoCaller autoCaller(this);
12731 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12732
12733#ifdef VBOX_WITH_USB
12734 USBProxyService *service = mParent->host()->usbProxyService();
12735 AssertReturn(service, E_FAIL);
12736 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12737#else
12738 NOREF(aId);
12739 NOREF(aDone);
12740 return E_NOTIMPL;
12741#endif
12742}
12743
12744/**
12745 * Inserts all machine filters to the USB proxy service and then calls
12746 * Host::autoCaptureUSBDevices().
12747 *
12748 * Called by Console from the VM process upon VM startup.
12749 *
12750 * @note Locks what called methods lock.
12751 */
12752STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12753{
12754 LogFlowThisFunc(("\n"));
12755
12756 AutoCaller autoCaller(this);
12757 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12758
12759#ifdef VBOX_WITH_USB
12760 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12761 AssertComRC(rc);
12762 NOREF(rc);
12763
12764 USBProxyService *service = mParent->host()->usbProxyService();
12765 AssertReturn(service, E_FAIL);
12766 return service->autoCaptureDevicesForVM(this);
12767#else
12768 return S_OK;
12769#endif
12770}
12771
12772/**
12773 * Removes all machine filters from the USB proxy service and then calls
12774 * Host::detachAllUSBDevices().
12775 *
12776 * Called by Console from the VM process upon normal VM termination or by
12777 * SessionMachine::uninit() upon abnormal VM termination (from under the
12778 * Machine/SessionMachine lock).
12779 *
12780 * @note Locks what called methods lock.
12781 */
12782STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12783{
12784 LogFlowThisFunc(("\n"));
12785
12786 AutoCaller autoCaller(this);
12787 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12788
12789#ifdef VBOX_WITH_USB
12790 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12791 AssertComRC(rc);
12792 NOREF(rc);
12793
12794 USBProxyService *service = mParent->host()->usbProxyService();
12795 AssertReturn(service, E_FAIL);
12796 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12797#else
12798 NOREF(aDone);
12799 return S_OK;
12800#endif
12801}
12802
12803/**
12804 * @note Locks this object for writing.
12805 */
12806STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12807 IProgress **aProgress)
12808{
12809 LogFlowThisFuncEnter();
12810
12811 AssertReturn(aSession, E_INVALIDARG);
12812 AssertReturn(aProgress, E_INVALIDARG);
12813
12814 AutoCaller autoCaller(this);
12815
12816 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12817 /*
12818 * We don't assert below because it might happen that a non-direct session
12819 * informs us it is closed right after we've been uninitialized -- it's ok.
12820 */
12821 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12822
12823 /* get IInternalSessionControl interface */
12824 ComPtr<IInternalSessionControl> control(aSession);
12825
12826 ComAssertRet(!control.isNull(), E_INVALIDARG);
12827
12828 /* Creating a Progress object requires the VirtualBox lock, and
12829 * thus locking it here is required by the lock order rules. */
12830 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12831
12832 if (control == mData->mSession.mDirectControl)
12833 {
12834 ComAssertRet(aProgress, E_POINTER);
12835
12836 /* The direct session is being normally closed by the client process
12837 * ----------------------------------------------------------------- */
12838
12839 /* go to the closing state (essential for all open*Session() calls and
12840 * for #checkForDeath()) */
12841 Assert(mData->mSession.mState == SessionState_Locked);
12842 mData->mSession.mState = SessionState_Unlocking;
12843
12844 /* set direct control to NULL to release the remote instance */
12845 mData->mSession.mDirectControl.setNull();
12846 LogFlowThisFunc(("Direct control is set to NULL\n"));
12847
12848 if (mData->mSession.mProgress)
12849 {
12850 /* finalize the progress, someone might wait if a frontend
12851 * closes the session before powering on the VM. */
12852 mData->mSession.mProgress->notifyComplete(E_FAIL,
12853 COM_IIDOF(ISession),
12854 getComponentName(),
12855 tr("The VM session was closed before any attempt to power it on"));
12856 mData->mSession.mProgress.setNull();
12857 }
12858
12859 /* Create the progress object the client will use to wait until
12860 * #checkForDeath() is called to uninitialize this session object after
12861 * it releases the IPC semaphore.
12862 * Note! Because we're "reusing" mProgress here, this must be a proxy
12863 * object just like for LaunchVMProcess. */
12864 Assert(mData->mSession.mProgress.isNull());
12865 ComObjPtr<ProgressProxy> progress;
12866 progress.createObject();
12867 ComPtr<IUnknown> pPeer(mPeer);
12868 progress->init(mParent, pPeer,
12869 Bstr(tr("Closing session")).raw(),
12870 FALSE /* aCancelable */);
12871 progress.queryInterfaceTo(aProgress);
12872 mData->mSession.mProgress = progress;
12873 }
12874 else
12875 {
12876 /* the remote session is being normally closed */
12877 Data::Session::RemoteControlList::iterator it =
12878 mData->mSession.mRemoteControls.begin();
12879 while (it != mData->mSession.mRemoteControls.end())
12880 {
12881 if (control == *it)
12882 break;
12883 ++it;
12884 }
12885 BOOL found = it != mData->mSession.mRemoteControls.end();
12886 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12887 E_INVALIDARG);
12888 // This MUST be erase(it), not remove(*it) as the latter triggers a
12889 // very nasty use after free due to the place where the value "lives".
12890 mData->mSession.mRemoteControls.erase(it);
12891 }
12892
12893 /* signal the client watcher thread, because the client is going away */
12894 mParent->updateClientWatcher();
12895
12896 LogFlowThisFuncLeave();
12897 return S_OK;
12898}
12899
12900/**
12901 * @note Locks this object for writing.
12902 */
12903STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12904{
12905 LogFlowThisFuncEnter();
12906
12907 CheckComArgOutPointerValid(aProgress);
12908 CheckComArgOutPointerValid(aStateFilePath);
12909
12910 AutoCaller autoCaller(this);
12911 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12912
12913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12914
12915 AssertReturn( mData->mMachineState == MachineState_Paused
12916 && mConsoleTaskData.mLastState == MachineState_Null
12917 && mConsoleTaskData.strStateFilePath.isEmpty(),
12918 E_FAIL);
12919
12920 /* create a progress object to track operation completion */
12921 ComObjPtr<Progress> pProgress;
12922 pProgress.createObject();
12923 pProgress->init(getVirtualBox(),
12924 static_cast<IMachine *>(this) /* aInitiator */,
12925 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12926 FALSE /* aCancelable */);
12927
12928 Utf8Str strStateFilePath;
12929 /* stateFilePath is null when the machine is not running */
12930 if (mData->mMachineState == MachineState_Paused)
12931 composeSavedStateFilename(strStateFilePath);
12932
12933 /* fill in the console task data */
12934 mConsoleTaskData.mLastState = mData->mMachineState;
12935 mConsoleTaskData.strStateFilePath = strStateFilePath;
12936 mConsoleTaskData.mProgress = pProgress;
12937
12938 /* set the state to Saving (this is expected by Console::SaveState()) */
12939 setMachineState(MachineState_Saving);
12940
12941 strStateFilePath.cloneTo(aStateFilePath);
12942 pProgress.queryInterfaceTo(aProgress);
12943
12944 return S_OK;
12945}
12946
12947/**
12948 * @note Locks mParent + this object for writing.
12949 */
12950STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12951{
12952 LogFlowThisFunc(("\n"));
12953
12954 AutoCaller autoCaller(this);
12955 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12956
12957 /* endSavingState() need mParent lock */
12958 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12959
12960 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12961 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12962 && mConsoleTaskData.mLastState != MachineState_Null
12963 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12964 E_FAIL);
12965
12966 /*
12967 * On failure, set the state to the state we had when BeginSavingState()
12968 * was called (this is expected by Console::SaveState() and the associated
12969 * task). On success the VM process already changed the state to
12970 * MachineState_Saved, so no need to do anything.
12971 */
12972 if (FAILED(iResult))
12973 setMachineState(mConsoleTaskData.mLastState);
12974
12975 return endSavingState(iResult, aErrMsg);
12976}
12977
12978/**
12979 * @note Locks this object for writing.
12980 */
12981STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12982{
12983 LogFlowThisFunc(("\n"));
12984
12985 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12986
12987 AutoCaller autoCaller(this);
12988 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12989
12990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12991
12992 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12993 || mData->mMachineState == MachineState_Teleported
12994 || mData->mMachineState == MachineState_Aborted
12995 , E_FAIL); /** @todo setError. */
12996
12997 Utf8Str stateFilePathFull = aSavedStateFile;
12998 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12999 if (RT_FAILURE(vrc))
13000 return setError(VBOX_E_FILE_ERROR,
13001 tr("Invalid saved state file path '%ls' (%Rrc)"),
13002 aSavedStateFile,
13003 vrc);
13004
13005 mSSData->strStateFilePath = stateFilePathFull;
13006
13007 /* The below setMachineState() will detect the state transition and will
13008 * update the settings file */
13009
13010 return setMachineState(MachineState_Saved);
13011}
13012
13013STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13014 ComSafeArrayOut(BSTR, aValues),
13015 ComSafeArrayOut(LONG64, aTimestamps),
13016 ComSafeArrayOut(BSTR, aFlags))
13017{
13018 LogFlowThisFunc(("\n"));
13019
13020#ifdef VBOX_WITH_GUEST_PROPS
13021 using namespace guestProp;
13022
13023 AutoCaller autoCaller(this);
13024 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13025
13026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13027
13028 CheckComArgOutSafeArrayPointerValid(aNames);
13029 CheckComArgOutSafeArrayPointerValid(aValues);
13030 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13031 CheckComArgOutSafeArrayPointerValid(aFlags);
13032
13033 size_t cEntries = mHWData->mGuestProperties.size();
13034 com::SafeArray<BSTR> names(cEntries);
13035 com::SafeArray<BSTR> values(cEntries);
13036 com::SafeArray<LONG64> timestamps(cEntries);
13037 com::SafeArray<BSTR> flags(cEntries);
13038 unsigned i = 0;
13039 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13040 it != mHWData->mGuestProperties.end();
13041 ++it)
13042 {
13043 char szFlags[MAX_FLAGS_LEN + 1];
13044 it->first.cloneTo(&names[i]);
13045 it->second.strValue.cloneTo(&values[i]);
13046 timestamps[i] = it->second.mTimestamp;
13047 /* If it is NULL, keep it NULL. */
13048 if (it->second.mFlags)
13049 {
13050 writeFlags(it->second.mFlags, szFlags);
13051 Bstr(szFlags).cloneTo(&flags[i]);
13052 }
13053 else
13054 flags[i] = NULL;
13055 ++i;
13056 }
13057 names.detachTo(ComSafeArrayOutArg(aNames));
13058 values.detachTo(ComSafeArrayOutArg(aValues));
13059 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13060 flags.detachTo(ComSafeArrayOutArg(aFlags));
13061 return S_OK;
13062#else
13063 ReturnComNotImplemented();
13064#endif
13065}
13066
13067STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13068 IN_BSTR aValue,
13069 LONG64 aTimestamp,
13070 IN_BSTR aFlags)
13071{
13072 LogFlowThisFunc(("\n"));
13073
13074#ifdef VBOX_WITH_GUEST_PROPS
13075 using namespace guestProp;
13076
13077 CheckComArgStrNotEmptyOrNull(aName);
13078 CheckComArgNotNull(aValue);
13079 CheckComArgNotNull(aFlags);
13080
13081 try
13082 {
13083 /*
13084 * Convert input up front.
13085 */
13086 Utf8Str utf8Name(aName);
13087 uint32_t fFlags = NILFLAG;
13088 if (aFlags)
13089 {
13090 Utf8Str utf8Flags(aFlags);
13091 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13092 AssertRCReturn(vrc, E_INVALIDARG);
13093 }
13094
13095 /*
13096 * Now grab the object lock, validate the state and do the update.
13097 */
13098 AutoCaller autoCaller(this);
13099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13100
13101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13102
13103 switch (mData->mMachineState)
13104 {
13105 case MachineState_Paused:
13106 case MachineState_Running:
13107 case MachineState_Teleporting:
13108 case MachineState_TeleportingPausedVM:
13109 case MachineState_LiveSnapshotting:
13110 case MachineState_DeletingSnapshotOnline:
13111 case MachineState_DeletingSnapshotPaused:
13112 case MachineState_Saving:
13113 case MachineState_Stopping:
13114 break;
13115
13116 default:
13117 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13118 VBOX_E_INVALID_VM_STATE);
13119 }
13120
13121 setModified(IsModified_MachineData);
13122 mHWData.backup();
13123
13124 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13125 if (it != mHWData->mGuestProperties.end())
13126 {
13127 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
13128 {
13129 it->second.strValue = aValue;
13130 it->second.mFlags = fFlags;
13131 it->second.mTimestamp = aTimestamp;
13132 }
13133 else
13134 mHWData->mGuestProperties.erase(it);
13135
13136 mData->mGuestPropertiesModified = TRUE;
13137 }
13138
13139 /*
13140 * Send a callback notification if appropriate
13141 */
13142 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13143 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13144 RTSTR_MAX,
13145 utf8Name.c_str(),
13146 RTSTR_MAX, NULL)
13147 )
13148 {
13149 alock.release();
13150
13151 mParent->onGuestPropertyChange(mData->mUuid,
13152 aName,
13153 aValue,
13154 aFlags);
13155 }
13156 }
13157 catch (...)
13158 {
13159 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13160 }
13161 return S_OK;
13162#else
13163 ReturnComNotImplemented();
13164#endif
13165}
13166
13167STDMETHODIMP SessionMachine::LockMedia()
13168{
13169 AutoCaller autoCaller(this);
13170 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13171
13172 AutoMultiWriteLock2 alock(this->lockHandle(),
13173 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13174
13175 AssertReturn( mData->mMachineState == MachineState_Starting
13176 || mData->mMachineState == MachineState_Restoring
13177 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13178
13179 clearError();
13180 alock.release();
13181 return lockMedia();
13182}
13183
13184STDMETHODIMP SessionMachine::UnlockMedia()
13185{
13186 unlockMedia();
13187 return S_OK;
13188}
13189
13190STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13191 IMediumAttachment **aNewAttachment)
13192{
13193 CheckComArgNotNull(aAttachment);
13194 CheckComArgOutPointerValid(aNewAttachment);
13195
13196 AutoCaller autoCaller(this);
13197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13198
13199 // request the host lock first, since might be calling Host methods for getting host drives;
13200 // next, protect the media tree all the while we're in here, as well as our member variables
13201 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13202 this->lockHandle(),
13203 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13204
13205 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13206
13207 Bstr ctrlName;
13208 LONG lPort;
13209 LONG lDevice;
13210 bool fTempEject;
13211 {
13212 AutoCaller autoAttachCaller(this);
13213 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13214
13215 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13216
13217 /* Need to query the details first, as the IMediumAttachment reference
13218 * might be to the original settings, which we are going to change. */
13219 ctrlName = pAttach->getControllerName();
13220 lPort = pAttach->getPort();
13221 lDevice = pAttach->getDevice();
13222 fTempEject = pAttach->getTempEject();
13223 }
13224
13225 if (!fTempEject)
13226 {
13227 /* Remember previously mounted medium. The medium before taking the
13228 * backup is not necessarily the same thing. */
13229 ComObjPtr<Medium> oldmedium;
13230 oldmedium = pAttach->getMedium();
13231
13232 setModified(IsModified_Storage);
13233 mMediaData.backup();
13234
13235 // The backup operation makes the pAttach reference point to the
13236 // old settings. Re-get the correct reference.
13237 pAttach = findAttachment(mMediaData->mAttachments,
13238 ctrlName.raw(),
13239 lPort,
13240 lDevice);
13241
13242 {
13243 AutoCaller autoAttachCaller(this);
13244 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13245
13246 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13247 if (!oldmedium.isNull())
13248 oldmedium->removeBackReference(mData->mUuid);
13249
13250 pAttach->updateMedium(NULL);
13251 pAttach->updateEjected();
13252 }
13253
13254 setModified(IsModified_Storage);
13255 }
13256 else
13257 {
13258 {
13259 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13260 pAttach->updateEjected();
13261 }
13262 }
13263
13264 pAttach.queryInterfaceTo(aNewAttachment);
13265
13266 return S_OK;
13267}
13268
13269// public methods only for internal purposes
13270/////////////////////////////////////////////////////////////////////////////
13271
13272/**
13273 * Called from the client watcher thread to check for expected or unexpected
13274 * death of the client process that has a direct session to this machine.
13275 *
13276 * On Win32 and on OS/2, this method is called only when we've got the
13277 * mutex (i.e. the client has either died or terminated normally) so it always
13278 * returns @c true (the client is terminated, the session machine is
13279 * uninitialized).
13280 *
13281 * On other platforms, the method returns @c true if the client process has
13282 * terminated normally or abnormally and the session machine was uninitialized,
13283 * and @c false if the client process is still alive.
13284 *
13285 * @note Locks this object for writing.
13286 */
13287bool SessionMachine::checkForDeath()
13288{
13289 Uninit::Reason reason;
13290 bool terminated = false;
13291
13292 /* Enclose autoCaller with a block because calling uninit() from under it
13293 * will deadlock. */
13294 {
13295 AutoCaller autoCaller(this);
13296 if (!autoCaller.isOk())
13297 {
13298 /* return true if not ready, to cause the client watcher to exclude
13299 * the corresponding session from watching */
13300 LogFlowThisFunc(("Already uninitialized!\n"));
13301 return true;
13302 }
13303
13304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13305
13306 /* Determine the reason of death: if the session state is Closing here,
13307 * everything is fine. Otherwise it means that the client did not call
13308 * OnSessionEnd() before it released the IPC semaphore. This may happen
13309 * either because the client process has abnormally terminated, or
13310 * because it simply forgot to call ISession::Close() before exiting. We
13311 * threat the latter also as an abnormal termination (see
13312 * Session::uninit() for details). */
13313 reason = mData->mSession.mState == SessionState_Unlocking ?
13314 Uninit::Normal :
13315 Uninit::Abnormal;
13316
13317#if defined(RT_OS_WINDOWS)
13318
13319 AssertMsg(mIPCSem, ("semaphore must be created"));
13320
13321 /* release the IPC mutex */
13322 ::ReleaseMutex(mIPCSem);
13323
13324 terminated = true;
13325
13326#elif defined(RT_OS_OS2)
13327
13328 AssertMsg(mIPCSem, ("semaphore must be created"));
13329
13330 /* release the IPC mutex */
13331 ::DosReleaseMutexSem(mIPCSem);
13332
13333 terminated = true;
13334
13335#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13336
13337 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13338
13339 int val = ::semctl(mIPCSem, 0, GETVAL);
13340 if (val > 0)
13341 {
13342 /* the semaphore is signaled, meaning the session is terminated */
13343 terminated = true;
13344 }
13345
13346#else
13347# error "Port me!"
13348#endif
13349
13350 } /* AutoCaller block */
13351
13352 if (terminated)
13353 uninit(reason);
13354
13355 return terminated;
13356}
13357
13358/**
13359 * @note Locks this object for reading.
13360 */
13361HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13362{
13363 LogFlowThisFunc(("\n"));
13364
13365 AutoCaller autoCaller(this);
13366 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13367
13368 ComPtr<IInternalSessionControl> directControl;
13369 {
13370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13371 directControl = mData->mSession.mDirectControl;
13372 }
13373
13374 /* ignore notifications sent after #OnSessionEnd() is called */
13375 if (!directControl)
13376 return S_OK;
13377
13378 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13379}
13380
13381/**
13382 * @note Locks this object for reading.
13383 */
13384HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13385 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13386{
13387 LogFlowThisFunc(("\n"));
13388
13389 AutoCaller autoCaller(this);
13390 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13391
13392 ComPtr<IInternalSessionControl> directControl;
13393 {
13394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13395 directControl = mData->mSession.mDirectControl;
13396 }
13397
13398 /* ignore notifications sent after #OnSessionEnd() is called */
13399 if (!directControl)
13400 return S_OK;
13401 /*
13402 * instead acting like callback we ask IVirtualBox deliver corresponding event
13403 */
13404
13405 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13406 return S_OK;
13407}
13408
13409/**
13410 * @note Locks this object for reading.
13411 */
13412HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13413{
13414 LogFlowThisFunc(("\n"));
13415
13416 AutoCaller autoCaller(this);
13417 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13418
13419 ComPtr<IInternalSessionControl> directControl;
13420 {
13421 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13422 directControl = mData->mSession.mDirectControl;
13423 }
13424
13425 /* ignore notifications sent after #OnSessionEnd() is called */
13426 if (!directControl)
13427 return S_OK;
13428
13429 return directControl->OnSerialPortChange(serialPort);
13430}
13431
13432/**
13433 * @note Locks this object for reading.
13434 */
13435HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13436{
13437 LogFlowThisFunc(("\n"));
13438
13439 AutoCaller autoCaller(this);
13440 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13441
13442 ComPtr<IInternalSessionControl> directControl;
13443 {
13444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13445 directControl = mData->mSession.mDirectControl;
13446 }
13447
13448 /* ignore notifications sent after #OnSessionEnd() is called */
13449 if (!directControl)
13450 return S_OK;
13451
13452 return directControl->OnParallelPortChange(parallelPort);
13453}
13454
13455/**
13456 * @note Locks this object for reading.
13457 */
13458HRESULT SessionMachine::onStorageControllerChange()
13459{
13460 LogFlowThisFunc(("\n"));
13461
13462 AutoCaller autoCaller(this);
13463 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13464
13465 ComPtr<IInternalSessionControl> directControl;
13466 {
13467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13468 directControl = mData->mSession.mDirectControl;
13469 }
13470
13471 /* ignore notifications sent after #OnSessionEnd() is called */
13472 if (!directControl)
13473 return S_OK;
13474
13475 return directControl->OnStorageControllerChange();
13476}
13477
13478/**
13479 * @note Locks this object for reading.
13480 */
13481HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13482{
13483 LogFlowThisFunc(("\n"));
13484
13485 AutoCaller autoCaller(this);
13486 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13487
13488 ComPtr<IInternalSessionControl> directControl;
13489 {
13490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13491 directControl = mData->mSession.mDirectControl;
13492 }
13493
13494 /* ignore notifications sent after #OnSessionEnd() is called */
13495 if (!directControl)
13496 return S_OK;
13497
13498 return directControl->OnMediumChange(aAttachment, aForce);
13499}
13500
13501/**
13502 * @note Locks this object for reading.
13503 */
13504HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13505{
13506 LogFlowThisFunc(("\n"));
13507
13508 AutoCaller autoCaller(this);
13509 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13510
13511 ComPtr<IInternalSessionControl> directControl;
13512 {
13513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13514 directControl = mData->mSession.mDirectControl;
13515 }
13516
13517 /* ignore notifications sent after #OnSessionEnd() is called */
13518 if (!directControl)
13519 return S_OK;
13520
13521 return directControl->OnCPUChange(aCPU, aRemove);
13522}
13523
13524HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13525{
13526 LogFlowThisFunc(("\n"));
13527
13528 AutoCaller autoCaller(this);
13529 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13530
13531 ComPtr<IInternalSessionControl> directControl;
13532 {
13533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13534 directControl = mData->mSession.mDirectControl;
13535 }
13536
13537 /* ignore notifications sent after #OnSessionEnd() is called */
13538 if (!directControl)
13539 return S_OK;
13540
13541 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13542}
13543
13544/**
13545 * @note Locks this object for reading.
13546 */
13547HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13548{
13549 LogFlowThisFunc(("\n"));
13550
13551 AutoCaller autoCaller(this);
13552 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13553
13554 ComPtr<IInternalSessionControl> directControl;
13555 {
13556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13557 directControl = mData->mSession.mDirectControl;
13558 }
13559
13560 /* ignore notifications sent after #OnSessionEnd() is called */
13561 if (!directControl)
13562 return S_OK;
13563
13564 return directControl->OnVRDEServerChange(aRestart);
13565}
13566
13567/**
13568 * @note Locks this object for reading.
13569 */
13570HRESULT SessionMachine::onUSBControllerChange()
13571{
13572 LogFlowThisFunc(("\n"));
13573
13574 AutoCaller autoCaller(this);
13575 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13576
13577 ComPtr<IInternalSessionControl> directControl;
13578 {
13579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13580 directControl = mData->mSession.mDirectControl;
13581 }
13582
13583 /* ignore notifications sent after #OnSessionEnd() is called */
13584 if (!directControl)
13585 return S_OK;
13586
13587 return directControl->OnUSBControllerChange();
13588}
13589
13590/**
13591 * @note Locks this object for reading.
13592 */
13593HRESULT SessionMachine::onSharedFolderChange()
13594{
13595 LogFlowThisFunc(("\n"));
13596
13597 AutoCaller autoCaller(this);
13598 AssertComRCReturnRC(autoCaller.rc());
13599
13600 ComPtr<IInternalSessionControl> directControl;
13601 {
13602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13603 directControl = mData->mSession.mDirectControl;
13604 }
13605
13606 /* ignore notifications sent after #OnSessionEnd() is called */
13607 if (!directControl)
13608 return S_OK;
13609
13610 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13611}
13612
13613/**
13614 * @note Locks this object for reading.
13615 */
13616HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13617{
13618 LogFlowThisFunc(("\n"));
13619
13620 AutoCaller autoCaller(this);
13621 AssertComRCReturnRC(autoCaller.rc());
13622
13623 ComPtr<IInternalSessionControl> directControl;
13624 {
13625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13626 directControl = mData->mSession.mDirectControl;
13627 }
13628
13629 /* ignore notifications sent after #OnSessionEnd() is called */
13630 if (!directControl)
13631 return S_OK;
13632
13633 return directControl->OnClipboardModeChange(aClipboardMode);
13634}
13635
13636/**
13637 * @note Locks this object for reading.
13638 */
13639HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13640{
13641 LogFlowThisFunc(("\n"));
13642
13643 AutoCaller autoCaller(this);
13644 AssertComRCReturnRC(autoCaller.rc());
13645
13646 ComPtr<IInternalSessionControl> directControl;
13647 {
13648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13649 directControl = mData->mSession.mDirectControl;
13650 }
13651
13652 /* ignore notifications sent after #OnSessionEnd() is called */
13653 if (!directControl)
13654 return S_OK;
13655
13656 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13657}
13658
13659/**
13660 * @note Locks this object for reading.
13661 */
13662HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13663{
13664 LogFlowThisFunc(("\n"));
13665
13666 AutoCaller autoCaller(this);
13667 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13668
13669 ComPtr<IInternalSessionControl> directControl;
13670 {
13671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13672 directControl = mData->mSession.mDirectControl;
13673 }
13674
13675 /* ignore notifications sent after #OnSessionEnd() is called */
13676 if (!directControl)
13677 return S_OK;
13678
13679 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13680}
13681
13682/**
13683 * @note Locks this object for reading.
13684 */
13685HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13686{
13687 LogFlowThisFunc(("\n"));
13688
13689 AutoCaller autoCaller(this);
13690 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13691
13692 ComPtr<IInternalSessionControl> directControl;
13693 {
13694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13695 directControl = mData->mSession.mDirectControl;
13696 }
13697
13698 /* ignore notifications sent after #OnSessionEnd() is called */
13699 if (!directControl)
13700 return S_OK;
13701
13702 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13703}
13704
13705/**
13706 * Returns @c true if this machine's USB controller reports it has a matching
13707 * filter for the given USB device and @c false otherwise.
13708 *
13709 * @note locks this object for reading.
13710 */
13711bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13712{
13713 AutoCaller autoCaller(this);
13714 /* silently return if not ready -- this method may be called after the
13715 * direct machine session has been called */
13716 if (!autoCaller.isOk())
13717 return false;
13718
13719#ifdef VBOX_WITH_USB
13720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13721
13722 switch (mData->mMachineState)
13723 {
13724 case MachineState_Starting:
13725 case MachineState_Restoring:
13726 case MachineState_TeleportingIn:
13727 case MachineState_Paused:
13728 case MachineState_Running:
13729 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13730 * elsewhere... */
13731 alock.release();
13732 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13733 default: break;
13734 }
13735#else
13736 NOREF(aDevice);
13737 NOREF(aMaskedIfs);
13738#endif
13739 return false;
13740}
13741
13742/**
13743 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13744 */
13745HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13746 IVirtualBoxErrorInfo *aError,
13747 ULONG aMaskedIfs)
13748{
13749 LogFlowThisFunc(("\n"));
13750
13751 AutoCaller autoCaller(this);
13752
13753 /* This notification may happen after the machine object has been
13754 * uninitialized (the session was closed), so don't assert. */
13755 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13756
13757 ComPtr<IInternalSessionControl> directControl;
13758 {
13759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13760 directControl = mData->mSession.mDirectControl;
13761 }
13762
13763 /* fail on notifications sent after #OnSessionEnd() is called, it is
13764 * expected by the caller */
13765 if (!directControl)
13766 return E_FAIL;
13767
13768 /* No locks should be held at this point. */
13769 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13770 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13771
13772 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13773}
13774
13775/**
13776 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13777 */
13778HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13779 IVirtualBoxErrorInfo *aError)
13780{
13781 LogFlowThisFunc(("\n"));
13782
13783 AutoCaller autoCaller(this);
13784
13785 /* This notification may happen after the machine object has been
13786 * uninitialized (the session was closed), so don't assert. */
13787 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13788
13789 ComPtr<IInternalSessionControl> directControl;
13790 {
13791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13792 directControl = mData->mSession.mDirectControl;
13793 }
13794
13795 /* fail on notifications sent after #OnSessionEnd() is called, it is
13796 * expected by the caller */
13797 if (!directControl)
13798 return E_FAIL;
13799
13800 /* No locks should be held at this point. */
13801 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13802 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13803
13804 return directControl->OnUSBDeviceDetach(aId, aError);
13805}
13806
13807// protected methods
13808/////////////////////////////////////////////////////////////////////////////
13809
13810/**
13811 * Helper method to finalize saving the state.
13812 *
13813 * @note Must be called from under this object's lock.
13814 *
13815 * @param aRc S_OK if the snapshot has been taken successfully
13816 * @param aErrMsg human readable error message for failure
13817 *
13818 * @note Locks mParent + this objects for writing.
13819 */
13820HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13821{
13822 LogFlowThisFuncEnter();
13823
13824 AutoCaller autoCaller(this);
13825 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13826
13827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13828
13829 HRESULT rc = S_OK;
13830
13831 if (SUCCEEDED(aRc))
13832 {
13833 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13834
13835 /* save all VM settings */
13836 rc = saveSettings(NULL);
13837 // no need to check whether VirtualBox.xml needs saving also since
13838 // we can't have a name change pending at this point
13839 }
13840 else
13841 {
13842 // delete the saved state file (it might have been already created);
13843 // we need not check whether this is shared with a snapshot here because
13844 // we certainly created this saved state file here anew
13845 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13846 }
13847
13848 /* notify the progress object about operation completion */
13849 Assert(mConsoleTaskData.mProgress);
13850 if (SUCCEEDED(aRc))
13851 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13852 else
13853 {
13854 if (aErrMsg.length())
13855 mConsoleTaskData.mProgress->notifyComplete(aRc,
13856 COM_IIDOF(ISession),
13857 getComponentName(),
13858 aErrMsg.c_str());
13859 else
13860 mConsoleTaskData.mProgress->notifyComplete(aRc);
13861 }
13862
13863 /* clear out the temporary saved state data */
13864 mConsoleTaskData.mLastState = MachineState_Null;
13865 mConsoleTaskData.strStateFilePath.setNull();
13866 mConsoleTaskData.mProgress.setNull();
13867
13868 LogFlowThisFuncLeave();
13869 return rc;
13870}
13871
13872/**
13873 * Deletes the given file if it is no longer in use by either the current machine state
13874 * (if the machine is "saved") or any of the machine's snapshots.
13875 *
13876 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13877 * but is different for each SnapshotMachine. When calling this, the order of calling this
13878 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13879 * is therefore critical. I know, it's all rather messy.
13880 *
13881 * @param strStateFile
13882 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13883 */
13884void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13885 Snapshot *pSnapshotToIgnore)
13886{
13887 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13888 if ( (strStateFile.isNotEmpty())
13889 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13890 )
13891 // ... and it must also not be shared with other snapshots
13892 if ( !mData->mFirstSnapshot
13893 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13894 // this checks the SnapshotMachine's state file paths
13895 )
13896 RTFileDelete(strStateFile.c_str());
13897}
13898
13899/**
13900 * Locks the attached media.
13901 *
13902 * All attached hard disks are locked for writing and DVD/floppy are locked for
13903 * reading. Parents of attached hard disks (if any) are locked for reading.
13904 *
13905 * This method also performs accessibility check of all media it locks: if some
13906 * media is inaccessible, the method will return a failure and a bunch of
13907 * extended error info objects per each inaccessible medium.
13908 *
13909 * Note that this method is atomic: if it returns a success, all media are
13910 * locked as described above; on failure no media is locked at all (all
13911 * succeeded individual locks will be undone).
13912 *
13913 * The caller is responsible for doing the necessary state sanity checks.
13914 *
13915 * The locks made by this method must be undone by calling #unlockMedia() when
13916 * no more needed.
13917 */
13918HRESULT SessionMachine::lockMedia()
13919{
13920 AutoCaller autoCaller(this);
13921 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13922
13923 AutoMultiWriteLock2 alock(this->lockHandle(),
13924 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13925
13926 /* bail out if trying to lock things with already set up locking */
13927 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13928
13929 MultiResult mrc(S_OK);
13930
13931 /* Collect locking information for all medium objects attached to the VM. */
13932 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13933 it != mMediaData->mAttachments.end();
13934 ++it)
13935 {
13936 MediumAttachment* pAtt = *it;
13937 DeviceType_T devType = pAtt->getType();
13938 Medium *pMedium = pAtt->getMedium();
13939
13940 MediumLockList *pMediumLockList(new MediumLockList());
13941 // There can be attachments without a medium (floppy/dvd), and thus
13942 // it's impossible to create a medium lock list. It still makes sense
13943 // to have the empty medium lock list in the map in case a medium is
13944 // attached later.
13945 if (pMedium != NULL)
13946 {
13947 MediumType_T mediumType = pMedium->getType();
13948 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13949 || mediumType == MediumType_Shareable;
13950 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13951
13952 alock.release();
13953 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13954 !fIsReadOnlyLock /* fMediumLockWrite */,
13955 NULL,
13956 *pMediumLockList);
13957 alock.acquire();
13958 if (FAILED(mrc))
13959 {
13960 delete pMediumLockList;
13961 mData->mSession.mLockedMedia.Clear();
13962 break;
13963 }
13964 }
13965
13966 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13967 if (FAILED(rc))
13968 {
13969 mData->mSession.mLockedMedia.Clear();
13970 mrc = setError(rc,
13971 tr("Collecting locking information for all attached media failed"));
13972 break;
13973 }
13974 }
13975
13976 if (SUCCEEDED(mrc))
13977 {
13978 /* Now lock all media. If this fails, nothing is locked. */
13979 alock.release();
13980 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13981 alock.acquire();
13982 if (FAILED(rc))
13983 {
13984 mrc = setError(rc,
13985 tr("Locking of attached media failed"));
13986 }
13987 }
13988
13989 return mrc;
13990}
13991
13992/**
13993 * Undoes the locks made by by #lockMedia().
13994 */
13995void SessionMachine::unlockMedia()
13996{
13997 AutoCaller autoCaller(this);
13998 AssertComRCReturnVoid(autoCaller.rc());
13999
14000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14001
14002 /* we may be holding important error info on the current thread;
14003 * preserve it */
14004 ErrorInfoKeeper eik;
14005
14006 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14007 AssertComRC(rc);
14008}
14009
14010/**
14011 * Helper to change the machine state (reimplementation).
14012 *
14013 * @note Locks this object for writing.
14014 * @note This method must not call saveSettings or SaveSettings, otherwise
14015 * it can cause crashes in random places due to unexpectedly committing
14016 * the current settings. The caller is responsible for that. The call
14017 * to saveStateSettings is fine, because this method does not commit.
14018 */
14019HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14020{
14021 LogFlowThisFuncEnter();
14022 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14023
14024 AutoCaller autoCaller(this);
14025 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14026
14027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14028
14029 MachineState_T oldMachineState = mData->mMachineState;
14030
14031 AssertMsgReturn(oldMachineState != aMachineState,
14032 ("oldMachineState=%s, aMachineState=%s\n",
14033 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14034 E_FAIL);
14035
14036 HRESULT rc = S_OK;
14037
14038 int stsFlags = 0;
14039 bool deleteSavedState = false;
14040
14041 /* detect some state transitions */
14042
14043 if ( ( oldMachineState == MachineState_Saved
14044 && aMachineState == MachineState_Restoring)
14045 || ( ( oldMachineState == MachineState_PoweredOff
14046 || oldMachineState == MachineState_Teleported
14047 || oldMachineState == MachineState_Aborted
14048 )
14049 && ( aMachineState == MachineState_TeleportingIn
14050 || aMachineState == MachineState_Starting
14051 )
14052 )
14053 )
14054 {
14055 /* The EMT thread is about to start */
14056
14057 /* Nothing to do here for now... */
14058
14059 /// @todo NEWMEDIA don't let mDVDDrive and other children
14060 /// change anything when in the Starting/Restoring state
14061 }
14062 else if ( ( oldMachineState == MachineState_Running
14063 || oldMachineState == MachineState_Paused
14064 || oldMachineState == MachineState_Teleporting
14065 || oldMachineState == MachineState_LiveSnapshotting
14066 || oldMachineState == MachineState_Stuck
14067 || oldMachineState == MachineState_Starting
14068 || oldMachineState == MachineState_Stopping
14069 || oldMachineState == MachineState_Saving
14070 || oldMachineState == MachineState_Restoring
14071 || oldMachineState == MachineState_TeleportingPausedVM
14072 || oldMachineState == MachineState_TeleportingIn
14073 )
14074 && ( aMachineState == MachineState_PoweredOff
14075 || aMachineState == MachineState_Saved
14076 || aMachineState == MachineState_Teleported
14077 || aMachineState == MachineState_Aborted
14078 )
14079 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14080 * snapshot */
14081 && ( mConsoleTaskData.mSnapshot.isNull()
14082 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14083 )
14084 )
14085 {
14086 /* The EMT thread has just stopped, unlock attached media. Note that as
14087 * opposed to locking that is done from Console, we do unlocking here
14088 * because the VM process may have aborted before having a chance to
14089 * properly unlock all media it locked. */
14090
14091 unlockMedia();
14092 }
14093
14094 if (oldMachineState == MachineState_Restoring)
14095 {
14096 if (aMachineState != MachineState_Saved)
14097 {
14098 /*
14099 * delete the saved state file once the machine has finished
14100 * restoring from it (note that Console sets the state from
14101 * Restoring to Saved if the VM couldn't restore successfully,
14102 * to give the user an ability to fix an error and retry --
14103 * we keep the saved state file in this case)
14104 */
14105 deleteSavedState = true;
14106 }
14107 }
14108 else if ( oldMachineState == MachineState_Saved
14109 && ( aMachineState == MachineState_PoweredOff
14110 || aMachineState == MachineState_Aborted
14111 || aMachineState == MachineState_Teleported
14112 )
14113 )
14114 {
14115 /*
14116 * delete the saved state after Console::ForgetSavedState() is called
14117 * or if the VM process (owning a direct VM session) crashed while the
14118 * VM was Saved
14119 */
14120
14121 /// @todo (dmik)
14122 // Not sure that deleting the saved state file just because of the
14123 // client death before it attempted to restore the VM is a good
14124 // thing. But when it crashes we need to go to the Aborted state
14125 // which cannot have the saved state file associated... The only
14126 // way to fix this is to make the Aborted condition not a VM state
14127 // but a bool flag: i.e., when a crash occurs, set it to true and
14128 // change the state to PoweredOff or Saved depending on the
14129 // saved state presence.
14130
14131 deleteSavedState = true;
14132 mData->mCurrentStateModified = TRUE;
14133 stsFlags |= SaveSTS_CurStateModified;
14134 }
14135
14136 if ( aMachineState == MachineState_Starting
14137 || aMachineState == MachineState_Restoring
14138 || aMachineState == MachineState_TeleportingIn
14139 )
14140 {
14141 /* set the current state modified flag to indicate that the current
14142 * state is no more identical to the state in the
14143 * current snapshot */
14144 if (!mData->mCurrentSnapshot.isNull())
14145 {
14146 mData->mCurrentStateModified = TRUE;
14147 stsFlags |= SaveSTS_CurStateModified;
14148 }
14149 }
14150
14151 if (deleteSavedState)
14152 {
14153 if (mRemoveSavedState)
14154 {
14155 Assert(!mSSData->strStateFilePath.isEmpty());
14156
14157 // it is safe to delete the saved state file if ...
14158 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14159 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14160 // ... none of the snapshots share the saved state file
14161 )
14162 RTFileDelete(mSSData->strStateFilePath.c_str());
14163 }
14164
14165 mSSData->strStateFilePath.setNull();
14166 stsFlags |= SaveSTS_StateFilePath;
14167 }
14168
14169 /* redirect to the underlying peer machine */
14170 mPeer->setMachineState(aMachineState);
14171
14172 if ( aMachineState == MachineState_PoweredOff
14173 || aMachineState == MachineState_Teleported
14174 || aMachineState == MachineState_Aborted
14175 || aMachineState == MachineState_Saved)
14176 {
14177 /* the machine has stopped execution
14178 * (or the saved state file was adopted) */
14179 stsFlags |= SaveSTS_StateTimeStamp;
14180 }
14181
14182 if ( ( oldMachineState == MachineState_PoweredOff
14183 || oldMachineState == MachineState_Aborted
14184 || oldMachineState == MachineState_Teleported
14185 )
14186 && aMachineState == MachineState_Saved)
14187 {
14188 /* the saved state file was adopted */
14189 Assert(!mSSData->strStateFilePath.isEmpty());
14190 stsFlags |= SaveSTS_StateFilePath;
14191 }
14192
14193#ifdef VBOX_WITH_GUEST_PROPS
14194 if ( aMachineState == MachineState_PoweredOff
14195 || aMachineState == MachineState_Aborted
14196 || aMachineState == MachineState_Teleported)
14197 {
14198 /* Make sure any transient guest properties get removed from the
14199 * property store on shutdown. */
14200
14201 HWData::GuestPropertyMap::const_iterator it;
14202 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14203 if (!fNeedsSaving)
14204 for (it = mHWData->mGuestProperties.begin();
14205 it != mHWData->mGuestProperties.end(); ++it)
14206 if ( (it->second.mFlags & guestProp::TRANSIENT)
14207 || (it->second.mFlags & guestProp::TRANSRESET))
14208 {
14209 fNeedsSaving = true;
14210 break;
14211 }
14212 if (fNeedsSaving)
14213 {
14214 mData->mCurrentStateModified = TRUE;
14215 stsFlags |= SaveSTS_CurStateModified;
14216 }
14217 }
14218#endif
14219
14220 rc = saveStateSettings(stsFlags);
14221
14222 if ( ( oldMachineState != MachineState_PoweredOff
14223 && oldMachineState != MachineState_Aborted
14224 && oldMachineState != MachineState_Teleported
14225 )
14226 && ( aMachineState == MachineState_PoweredOff
14227 || aMachineState == MachineState_Aborted
14228 || aMachineState == MachineState_Teleported
14229 )
14230 )
14231 {
14232 /* we've been shut down for any reason */
14233 /* no special action so far */
14234 }
14235
14236 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14237 LogFlowThisFuncLeave();
14238 return rc;
14239}
14240
14241/**
14242 * Sends the current machine state value to the VM process.
14243 *
14244 * @note Locks this object for reading, then calls a client process.
14245 */
14246HRESULT SessionMachine::updateMachineStateOnClient()
14247{
14248 AutoCaller autoCaller(this);
14249 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14250
14251 ComPtr<IInternalSessionControl> directControl;
14252 {
14253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14254 AssertReturn(!!mData, E_FAIL);
14255 directControl = mData->mSession.mDirectControl;
14256
14257 /* directControl may be already set to NULL here in #OnSessionEnd()
14258 * called too early by the direct session process while there is still
14259 * some operation (like deleting the snapshot) in progress. The client
14260 * process in this case is waiting inside Session::close() for the
14261 * "end session" process object to complete, while #uninit() called by
14262 * #checkForDeath() on the Watcher thread is waiting for the pending
14263 * operation to complete. For now, we accept this inconsistent behavior
14264 * and simply do nothing here. */
14265
14266 if (mData->mSession.mState == SessionState_Unlocking)
14267 return S_OK;
14268
14269 AssertReturn(!directControl.isNull(), E_FAIL);
14270 }
14271
14272 return directControl->UpdateMachineState(mData->mMachineState);
14273}
Note: See TracBrowser for help on using the repository browser.

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