VirtualBox

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

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

Main: don't create a guest property if it does not exist and should be removed

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 479.0 KB
Line 
1/* $Id: MachineImpl.cpp 45909 2013-05-06 13:26:33Z 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->mVideoCaptureRate = 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 return setError(E_INVALIDARG,
5757 tr("Invalid guest property flag values: '%ls'"),
5758 aFlags);
5759
5760 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
5761 HWData::GuestPropertyMap::iterator it =
5762 mHWData->mGuestProperties.find(utf8Name);
5763
5764 if (it == mHWData->mGuestProperties.end())
5765 {
5766 /* only create the new property if this is really desired */
5767 if (!fDelete)
5768 {
5769 setModified(IsModified_MachineData);
5770 mHWData.backupEx();
5771
5772 RTTIMESPEC time;
5773 HWData::GuestProperty prop;
5774 prop.strValue = aValue;
5775 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5776 prop.mFlags = fFlags;
5777
5778 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5779 }
5780 }
5781 else
5782 {
5783 if (it->second.mFlags & (RDONLYHOST))
5784 {
5785 rc = setError(E_ACCESSDENIED,
5786 tr("The property '%ls' cannot be changed by the host"),
5787 aName);
5788 }
5789 else
5790 {
5791 setModified(IsModified_MachineData);
5792 mHWData.backupEx();
5793
5794 /* The backupEx() operation invalidates our iterator,
5795 * so get a new one. */
5796 it = mHWData->mGuestProperties.find(utf8Name);
5797 Assert(it != mHWData->mGuestProperties.end());
5798
5799 if (!fDelete)
5800 {
5801 RTTIMESPEC time;
5802 it->second.strValue = aValue;
5803 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5804 if (aFlags != NULL)
5805 it->second.mFlags = fFlags;
5806 }
5807 else
5808 {
5809 mHWData->mGuestProperties.erase(it);
5810 }
5811 }
5812 }
5813
5814 if ( SUCCEEDED(rc)
5815 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5816 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5817 RTSTR_MAX,
5818 utf8Name.c_str(),
5819 RTSTR_MAX,
5820 NULL)
5821 )
5822 )
5823 {
5824 alock.release();
5825
5826 mParent->onGuestPropertyChange(mData->mUuid, aName,
5827 aValue ? aValue : Bstr("").raw(),
5828 aFlags ? aFlags : Bstr("").raw());
5829 }
5830 }
5831 catch (std::bad_alloc &)
5832 {
5833 rc = E_OUTOFMEMORY;
5834 }
5835
5836 return rc;
5837}
5838
5839/**
5840 * Set a property on the VM that that property belongs to.
5841 * @returns E_ACCESSDENIED if the VM process is not available or not
5842 * currently handling queries and the setting should then be done in
5843 * VBoxSVC.
5844 */
5845HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5846 IN_BSTR aFlags)
5847{
5848 HRESULT rc;
5849
5850 try
5851 {
5852 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5853
5854 BSTR dummy = NULL; /* will not be changed (setter) */
5855 LONG64 dummy64;
5856 if (!directControl)
5857 rc = E_ACCESSDENIED;
5858 else
5859 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5860 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5861 true /* isSetter */,
5862 &dummy, &dummy64, &dummy);
5863 }
5864 catch (std::bad_alloc &)
5865 {
5866 rc = E_OUTOFMEMORY;
5867 }
5868
5869 return rc;
5870}
5871#endif // VBOX_WITH_GUEST_PROPS
5872
5873STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5874 IN_BSTR aFlags)
5875{
5876#ifndef VBOX_WITH_GUEST_PROPS
5877 ReturnComNotImplemented();
5878#else // VBOX_WITH_GUEST_PROPS
5879 CheckComArgStrNotEmptyOrNull(aName);
5880 CheckComArgMaybeNull(aFlags);
5881 CheckComArgMaybeNull(aValue);
5882
5883 AutoCaller autoCaller(this);
5884 if (FAILED(autoCaller.rc()))
5885 return autoCaller.rc();
5886
5887 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5888 if (rc == E_ACCESSDENIED)
5889 /* The VM is not running or the service is not (yet) accessible */
5890 rc = setGuestPropertyToService(aName, aValue, aFlags);
5891 return rc;
5892#endif // VBOX_WITH_GUEST_PROPS
5893}
5894
5895STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5896{
5897 return SetGuestProperty(aName, aValue, NULL);
5898}
5899
5900STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5901{
5902 return SetGuestProperty(aName, NULL, NULL);
5903}
5904
5905#ifdef VBOX_WITH_GUEST_PROPS
5906/**
5907 * Enumerate the guest properties in VBoxSVC's internal structures.
5908 */
5909HRESULT Machine::enumerateGuestPropertiesInService
5910 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5911 ComSafeArrayOut(BSTR, aValues),
5912 ComSafeArrayOut(LONG64, aTimestamps),
5913 ComSafeArrayOut(BSTR, aFlags))
5914{
5915 using namespace guestProp;
5916
5917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5918 Utf8Str strPatterns(aPatterns);
5919
5920 HWData::GuestPropertyMap propMap;
5921
5922 /*
5923 * Look for matching patterns and build up a list.
5924 */
5925 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5926 while (it != mHWData->mGuestProperties.end())
5927 {
5928 if ( strPatterns.isEmpty()
5929 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5930 RTSTR_MAX,
5931 it->first.c_str(),
5932 RTSTR_MAX,
5933 NULL)
5934 )
5935 {
5936 propMap.insert(*it);
5937 }
5938
5939 it++;
5940 }
5941
5942 alock.release();
5943
5944 /*
5945 * And build up the arrays for returning the property information.
5946 */
5947 size_t cEntries = propMap.size();
5948 SafeArray<BSTR> names(cEntries);
5949 SafeArray<BSTR> values(cEntries);
5950 SafeArray<LONG64> timestamps(cEntries);
5951 SafeArray<BSTR> flags(cEntries);
5952 size_t iProp = 0;
5953
5954 it = propMap.begin();
5955 while (it != propMap.end())
5956 {
5957 char szFlags[MAX_FLAGS_LEN + 1];
5958 it->first.cloneTo(&names[iProp]);
5959 it->second.strValue.cloneTo(&values[iProp]);
5960 timestamps[iProp] = it->second.mTimestamp;
5961 writeFlags(it->second.mFlags, szFlags);
5962 Bstr(szFlags).cloneTo(&flags[iProp++]);
5963 it++;
5964 }
5965 names.detachTo(ComSafeArrayOutArg(aNames));
5966 values.detachTo(ComSafeArrayOutArg(aValues));
5967 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5968 flags.detachTo(ComSafeArrayOutArg(aFlags));
5969 return S_OK;
5970}
5971
5972/**
5973 * Enumerate the properties managed by a VM.
5974 * @returns E_ACCESSDENIED if the VM process is not available or not
5975 * currently handling queries and the setting should then be done in
5976 * VBoxSVC.
5977 */
5978HRESULT Machine::enumerateGuestPropertiesOnVM
5979 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5980 ComSafeArrayOut(BSTR, aValues),
5981 ComSafeArrayOut(LONG64, aTimestamps),
5982 ComSafeArrayOut(BSTR, aFlags))
5983{
5984 HRESULT rc;
5985 ComPtr<IInternalSessionControl> directControl;
5986 directControl = mData->mSession.mDirectControl;
5987
5988 if (!directControl)
5989 rc = E_ACCESSDENIED;
5990 else
5991 rc = directControl->EnumerateGuestProperties
5992 (aPatterns, ComSafeArrayOutArg(aNames),
5993 ComSafeArrayOutArg(aValues),
5994 ComSafeArrayOutArg(aTimestamps),
5995 ComSafeArrayOutArg(aFlags));
5996 return rc;
5997}
5998#endif // VBOX_WITH_GUEST_PROPS
5999
6000STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6001 ComSafeArrayOut(BSTR, aNames),
6002 ComSafeArrayOut(BSTR, aValues),
6003 ComSafeArrayOut(LONG64, aTimestamps),
6004 ComSafeArrayOut(BSTR, aFlags))
6005{
6006#ifndef VBOX_WITH_GUEST_PROPS
6007 ReturnComNotImplemented();
6008#else // VBOX_WITH_GUEST_PROPS
6009 CheckComArgMaybeNull(aPatterns);
6010 CheckComArgOutSafeArrayPointerValid(aNames);
6011 CheckComArgOutSafeArrayPointerValid(aValues);
6012 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6013 CheckComArgOutSafeArrayPointerValid(aFlags);
6014
6015 AutoCaller autoCaller(this);
6016 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6017
6018 HRESULT rc = enumerateGuestPropertiesOnVM
6019 (aPatterns, ComSafeArrayOutArg(aNames),
6020 ComSafeArrayOutArg(aValues),
6021 ComSafeArrayOutArg(aTimestamps),
6022 ComSafeArrayOutArg(aFlags));
6023 if (rc == E_ACCESSDENIED)
6024 /* The VM is not running or the service is not (yet) accessible */
6025 rc = enumerateGuestPropertiesInService
6026 (aPatterns, ComSafeArrayOutArg(aNames),
6027 ComSafeArrayOutArg(aValues),
6028 ComSafeArrayOutArg(aTimestamps),
6029 ComSafeArrayOutArg(aFlags));
6030 return rc;
6031#endif // VBOX_WITH_GUEST_PROPS
6032}
6033
6034STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6035 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6036{
6037 MediaData::AttachmentList atts;
6038
6039 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6040 if (FAILED(rc)) return rc;
6041
6042 SafeIfaceArray<IMediumAttachment> attachments(atts);
6043 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6044
6045 return S_OK;
6046}
6047
6048STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6049 LONG aControllerPort,
6050 LONG aDevice,
6051 IMediumAttachment **aAttachment)
6052{
6053 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6054 aControllerName, aControllerPort, aDevice));
6055
6056 CheckComArgStrNotEmptyOrNull(aControllerName);
6057 CheckComArgOutPointerValid(aAttachment);
6058
6059 AutoCaller autoCaller(this);
6060 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6061
6062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6063
6064 *aAttachment = NULL;
6065
6066 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6067 aControllerName,
6068 aControllerPort,
6069 aDevice);
6070 if (pAttach.isNull())
6071 return setError(VBOX_E_OBJECT_NOT_FOUND,
6072 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6073 aDevice, aControllerPort, aControllerName);
6074
6075 pAttach.queryInterfaceTo(aAttachment);
6076
6077 return S_OK;
6078}
6079
6080STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6081 StorageBus_T aConnectionType,
6082 IStorageController **controller)
6083{
6084 CheckComArgStrNotEmptyOrNull(aName);
6085
6086 if ( (aConnectionType <= StorageBus_Null)
6087 || (aConnectionType > StorageBus_SAS))
6088 return setError(E_INVALIDARG,
6089 tr("Invalid connection type: %d"),
6090 aConnectionType);
6091
6092 AutoCaller autoCaller(this);
6093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6094
6095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6096
6097 HRESULT rc = checkStateDependency(MutableStateDep);
6098 if (FAILED(rc)) return rc;
6099
6100 /* try to find one with the name first. */
6101 ComObjPtr<StorageController> ctrl;
6102
6103 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6104 if (SUCCEEDED(rc))
6105 return setError(VBOX_E_OBJECT_IN_USE,
6106 tr("Storage controller named '%ls' already exists"),
6107 aName);
6108
6109 ctrl.createObject();
6110
6111 /* get a new instance number for the storage controller */
6112 ULONG ulInstance = 0;
6113 bool fBootable = true;
6114 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6115 it != mStorageControllers->end();
6116 ++it)
6117 {
6118 if ((*it)->getStorageBus() == aConnectionType)
6119 {
6120 ULONG ulCurInst = (*it)->getInstance();
6121
6122 if (ulCurInst >= ulInstance)
6123 ulInstance = ulCurInst + 1;
6124
6125 /* Only one controller of each type can be marked as bootable. */
6126 if ((*it)->getBootable())
6127 fBootable = false;
6128 }
6129 }
6130
6131 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6132 if (FAILED(rc)) return rc;
6133
6134 setModified(IsModified_Storage);
6135 mStorageControllers.backup();
6136 mStorageControllers->push_back(ctrl);
6137
6138 ctrl.queryInterfaceTo(controller);
6139
6140 /* inform the direct session if any */
6141 alock.release();
6142 onStorageControllerChange();
6143
6144 return S_OK;
6145}
6146
6147STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6148 IStorageController **aStorageController)
6149{
6150 CheckComArgStrNotEmptyOrNull(aName);
6151
6152 AutoCaller autoCaller(this);
6153 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6154
6155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6156
6157 ComObjPtr<StorageController> ctrl;
6158
6159 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6160 if (SUCCEEDED(rc))
6161 ctrl.queryInterfaceTo(aStorageController);
6162
6163 return rc;
6164}
6165
6166STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6167 IStorageController **aStorageController)
6168{
6169 AutoCaller autoCaller(this);
6170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6171
6172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6173
6174 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6175 it != mStorageControllers->end();
6176 ++it)
6177 {
6178 if ((*it)->getInstance() == aInstance)
6179 {
6180 (*it).queryInterfaceTo(aStorageController);
6181 return S_OK;
6182 }
6183 }
6184
6185 return setError(VBOX_E_OBJECT_NOT_FOUND,
6186 tr("Could not find a storage controller with instance number '%lu'"),
6187 aInstance);
6188}
6189
6190STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6191{
6192 AutoCaller autoCaller(this);
6193 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6194
6195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6196
6197 HRESULT rc = checkStateDependency(MutableStateDep);
6198 if (FAILED(rc)) return rc;
6199
6200 ComObjPtr<StorageController> ctrl;
6201
6202 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6203 if (SUCCEEDED(rc))
6204 {
6205 /* Ensure that only one controller of each type is marked as bootable. */
6206 if (fBootable == TRUE)
6207 {
6208 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6209 it != mStorageControllers->end();
6210 ++it)
6211 {
6212 ComObjPtr<StorageController> aCtrl = (*it);
6213
6214 if ( (aCtrl->getName() != Utf8Str(aName))
6215 && aCtrl->getBootable() == TRUE
6216 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6217 && aCtrl->getControllerType() == ctrl->getControllerType())
6218 {
6219 aCtrl->setBootable(FALSE);
6220 break;
6221 }
6222 }
6223 }
6224
6225 if (SUCCEEDED(rc))
6226 {
6227 ctrl->setBootable(fBootable);
6228 setModified(IsModified_Storage);
6229 }
6230 }
6231
6232 if (SUCCEEDED(rc))
6233 {
6234 /* inform the direct session if any */
6235 alock.release();
6236 onStorageControllerChange();
6237 }
6238
6239 return rc;
6240}
6241
6242STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6243{
6244 CheckComArgStrNotEmptyOrNull(aName);
6245
6246 AutoCaller autoCaller(this);
6247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6248
6249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6250
6251 HRESULT rc = checkStateDependency(MutableStateDep);
6252 if (FAILED(rc)) return rc;
6253
6254 ComObjPtr<StorageController> ctrl;
6255 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6256 if (FAILED(rc)) return rc;
6257
6258 {
6259 /* find all attached devices to the appropriate storage controller and detach them all */
6260 // make a temporary list because detachDevice invalidates iterators into
6261 // mMediaData->mAttachments
6262 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6263
6264 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6265 it != llAttachments2.end();
6266 ++it)
6267 {
6268 MediumAttachment *pAttachTemp = *it;
6269
6270 AutoCaller localAutoCaller(pAttachTemp);
6271 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6272
6273 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6274
6275 if (pAttachTemp->getControllerName() == aName)
6276 {
6277 rc = detachDevice(pAttachTemp, alock, NULL);
6278 if (FAILED(rc)) return rc;
6279 }
6280 }
6281 }
6282
6283 /* We can remove it now. */
6284 setModified(IsModified_Storage);
6285 mStorageControllers.backup();
6286
6287 ctrl->unshare();
6288
6289 mStorageControllers->remove(ctrl);
6290
6291 /* inform the direct session if any */
6292 alock.release();
6293 onStorageControllerChange();
6294
6295 return S_OK;
6296}
6297
6298STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6299 ULONG *puOriginX,
6300 ULONG *puOriginY,
6301 ULONG *puWidth,
6302 ULONG *puHeight,
6303 BOOL *pfEnabled)
6304{
6305 LogFlowThisFunc(("\n"));
6306
6307 CheckComArgNotNull(puOriginX);
6308 CheckComArgNotNull(puOriginY);
6309 CheckComArgNotNull(puWidth);
6310 CheckComArgNotNull(puHeight);
6311 CheckComArgNotNull(pfEnabled);
6312
6313 uint32_t u32OriginX= 0;
6314 uint32_t u32OriginY= 0;
6315 uint32_t u32Width = 0;
6316 uint32_t u32Height = 0;
6317 uint16_t u16Flags = 0;
6318
6319 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6320 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6321 if (RT_FAILURE(vrc))
6322 {
6323#ifdef RT_OS_WINDOWS
6324 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6325 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6326 * So just assign fEnable to TRUE again.
6327 * The right fix would be to change GUI API wrappers to make sure that parameters
6328 * are changed only if API succeeds.
6329 */
6330 *pfEnabled = TRUE;
6331#endif
6332 return setError(VBOX_E_IPRT_ERROR,
6333 tr("Saved guest size is not available (%Rrc)"),
6334 vrc);
6335 }
6336
6337 *puOriginX = u32OriginX;
6338 *puOriginY = u32OriginY;
6339 *puWidth = u32Width;
6340 *puHeight = u32Height;
6341 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6342
6343 return S_OK;
6344}
6345
6346STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6347{
6348 LogFlowThisFunc(("\n"));
6349
6350 CheckComArgNotNull(aSize);
6351 CheckComArgNotNull(aWidth);
6352 CheckComArgNotNull(aHeight);
6353
6354 if (aScreenId != 0)
6355 return E_NOTIMPL;
6356
6357 AutoCaller autoCaller(this);
6358 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6359
6360 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6361
6362 uint8_t *pu8Data = NULL;
6363 uint32_t cbData = 0;
6364 uint32_t u32Width = 0;
6365 uint32_t u32Height = 0;
6366
6367 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6368
6369 if (RT_FAILURE(vrc))
6370 return setError(VBOX_E_IPRT_ERROR,
6371 tr("Saved screenshot data is not available (%Rrc)"),
6372 vrc);
6373
6374 *aSize = cbData;
6375 *aWidth = u32Width;
6376 *aHeight = u32Height;
6377
6378 freeSavedDisplayScreenshot(pu8Data);
6379
6380 return S_OK;
6381}
6382
6383STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6384{
6385 LogFlowThisFunc(("\n"));
6386
6387 CheckComArgNotNull(aWidth);
6388 CheckComArgNotNull(aHeight);
6389 CheckComArgOutSafeArrayPointerValid(aData);
6390
6391 if (aScreenId != 0)
6392 return E_NOTIMPL;
6393
6394 AutoCaller autoCaller(this);
6395 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6396
6397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6398
6399 uint8_t *pu8Data = NULL;
6400 uint32_t cbData = 0;
6401 uint32_t u32Width = 0;
6402 uint32_t u32Height = 0;
6403
6404 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6405
6406 if (RT_FAILURE(vrc))
6407 return setError(VBOX_E_IPRT_ERROR,
6408 tr("Saved screenshot data is not available (%Rrc)"),
6409 vrc);
6410
6411 *aWidth = u32Width;
6412 *aHeight = u32Height;
6413
6414 com::SafeArray<BYTE> bitmap(cbData);
6415 /* Convert pixels to format expected by the API caller. */
6416 if (aBGR)
6417 {
6418 /* [0] B, [1] G, [2] R, [3] A. */
6419 for (unsigned i = 0; i < cbData; i += 4)
6420 {
6421 bitmap[i] = pu8Data[i];
6422 bitmap[i + 1] = pu8Data[i + 1];
6423 bitmap[i + 2] = pu8Data[i + 2];
6424 bitmap[i + 3] = 0xff;
6425 }
6426 }
6427 else
6428 {
6429 /* [0] R, [1] G, [2] B, [3] A. */
6430 for (unsigned i = 0; i < cbData; i += 4)
6431 {
6432 bitmap[i] = pu8Data[i + 2];
6433 bitmap[i + 1] = pu8Data[i + 1];
6434 bitmap[i + 2] = pu8Data[i];
6435 bitmap[i + 3] = 0xff;
6436 }
6437 }
6438 bitmap.detachTo(ComSafeArrayOutArg(aData));
6439
6440 freeSavedDisplayScreenshot(pu8Data);
6441
6442 return S_OK;
6443}
6444
6445
6446STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6447{
6448 LogFlowThisFunc(("\n"));
6449
6450 CheckComArgNotNull(aWidth);
6451 CheckComArgNotNull(aHeight);
6452 CheckComArgOutSafeArrayPointerValid(aData);
6453
6454 if (aScreenId != 0)
6455 return E_NOTIMPL;
6456
6457 AutoCaller autoCaller(this);
6458 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6459
6460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6461
6462 uint8_t *pu8Data = NULL;
6463 uint32_t cbData = 0;
6464 uint32_t u32Width = 0;
6465 uint32_t u32Height = 0;
6466
6467 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6468
6469 if (RT_FAILURE(vrc))
6470 return setError(VBOX_E_IPRT_ERROR,
6471 tr("Saved screenshot data is not available (%Rrc)"),
6472 vrc);
6473
6474 *aWidth = u32Width;
6475 *aHeight = u32Height;
6476
6477 HRESULT rc = S_OK;
6478 uint8_t *pu8PNG = NULL;
6479 uint32_t cbPNG = 0;
6480 uint32_t cxPNG = 0;
6481 uint32_t cyPNG = 0;
6482
6483 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6484
6485 if (RT_SUCCESS(vrc))
6486 {
6487 com::SafeArray<BYTE> screenData(cbPNG);
6488 screenData.initFrom(pu8PNG, cbPNG);
6489 if (pu8PNG)
6490 RTMemFree(pu8PNG);
6491 screenData.detachTo(ComSafeArrayOutArg(aData));
6492 }
6493 else
6494 {
6495 if (pu8PNG)
6496 RTMemFree(pu8PNG);
6497 return setError(VBOX_E_IPRT_ERROR,
6498 tr("Could not convert screenshot to PNG (%Rrc)"),
6499 vrc);
6500 }
6501
6502 freeSavedDisplayScreenshot(pu8Data);
6503
6504 return rc;
6505}
6506
6507STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6508{
6509 LogFlowThisFunc(("\n"));
6510
6511 CheckComArgNotNull(aSize);
6512 CheckComArgNotNull(aWidth);
6513 CheckComArgNotNull(aHeight);
6514
6515 if (aScreenId != 0)
6516 return E_NOTIMPL;
6517
6518 AutoCaller autoCaller(this);
6519 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6520
6521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6522
6523 uint8_t *pu8Data = NULL;
6524 uint32_t cbData = 0;
6525 uint32_t u32Width = 0;
6526 uint32_t u32Height = 0;
6527
6528 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6529
6530 if (RT_FAILURE(vrc))
6531 return setError(VBOX_E_IPRT_ERROR,
6532 tr("Saved screenshot data is not available (%Rrc)"),
6533 vrc);
6534
6535 *aSize = cbData;
6536 *aWidth = u32Width;
6537 *aHeight = u32Height;
6538
6539 freeSavedDisplayScreenshot(pu8Data);
6540
6541 return S_OK;
6542}
6543
6544STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6545{
6546 LogFlowThisFunc(("\n"));
6547
6548 CheckComArgNotNull(aWidth);
6549 CheckComArgNotNull(aHeight);
6550 CheckComArgOutSafeArrayPointerValid(aData);
6551
6552 if (aScreenId != 0)
6553 return E_NOTIMPL;
6554
6555 AutoCaller autoCaller(this);
6556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6557
6558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6559
6560 uint8_t *pu8Data = NULL;
6561 uint32_t cbData = 0;
6562 uint32_t u32Width = 0;
6563 uint32_t u32Height = 0;
6564
6565 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6566
6567 if (RT_FAILURE(vrc))
6568 return setError(VBOX_E_IPRT_ERROR,
6569 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6570 vrc);
6571
6572 *aWidth = u32Width;
6573 *aHeight = u32Height;
6574
6575 com::SafeArray<BYTE> png(cbData);
6576 png.initFrom(pu8Data, cbData);
6577 png.detachTo(ComSafeArrayOutArg(aData));
6578
6579 freeSavedDisplayScreenshot(pu8Data);
6580
6581 return S_OK;
6582}
6583
6584STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6585{
6586 HRESULT rc = S_OK;
6587 LogFlowThisFunc(("\n"));
6588
6589 AutoCaller autoCaller(this);
6590 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6591
6592 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6593
6594 if (!mHWData->mCPUHotPlugEnabled)
6595 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6596
6597 if (aCpu >= mHWData->mCPUCount)
6598 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6599
6600 if (mHWData->mCPUAttached[aCpu])
6601 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6602
6603 alock.release();
6604 rc = onCPUChange(aCpu, false);
6605 alock.acquire();
6606 if (FAILED(rc)) return rc;
6607
6608 setModified(IsModified_MachineData);
6609 mHWData.backup();
6610 mHWData->mCPUAttached[aCpu] = true;
6611
6612 /* Save settings if online */
6613 if (Global::IsOnline(mData->mMachineState))
6614 saveSettings(NULL);
6615
6616 return S_OK;
6617}
6618
6619STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6620{
6621 HRESULT rc = S_OK;
6622 LogFlowThisFunc(("\n"));
6623
6624 AutoCaller autoCaller(this);
6625 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6626
6627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6628
6629 if (!mHWData->mCPUHotPlugEnabled)
6630 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6631
6632 if (aCpu >= SchemaDefs::MaxCPUCount)
6633 return setError(E_INVALIDARG,
6634 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6635 SchemaDefs::MaxCPUCount);
6636
6637 if (!mHWData->mCPUAttached[aCpu])
6638 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6639
6640 /* CPU 0 can't be detached */
6641 if (aCpu == 0)
6642 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6643
6644 alock.release();
6645 rc = onCPUChange(aCpu, true);
6646 alock.acquire();
6647 if (FAILED(rc)) return rc;
6648
6649 setModified(IsModified_MachineData);
6650 mHWData.backup();
6651 mHWData->mCPUAttached[aCpu] = false;
6652
6653 /* Save settings if online */
6654 if (Global::IsOnline(mData->mMachineState))
6655 saveSettings(NULL);
6656
6657 return S_OK;
6658}
6659
6660STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6661{
6662 LogFlowThisFunc(("\n"));
6663
6664 CheckComArgNotNull(aCpuAttached);
6665
6666 *aCpuAttached = false;
6667
6668 AutoCaller autoCaller(this);
6669 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6670
6671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6672
6673 /* If hotplug is enabled the CPU is always enabled. */
6674 if (!mHWData->mCPUHotPlugEnabled)
6675 {
6676 if (aCpu < mHWData->mCPUCount)
6677 *aCpuAttached = true;
6678 }
6679 else
6680 {
6681 if (aCpu < SchemaDefs::MaxCPUCount)
6682 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6683 }
6684
6685 return S_OK;
6686}
6687
6688STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6689{
6690 CheckComArgOutPointerValid(aName);
6691
6692 AutoCaller autoCaller(this);
6693 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6694
6695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6696
6697 Utf8Str log = queryLogFilename(aIdx);
6698 if (!RTFileExists(log.c_str()))
6699 log.setNull();
6700 log.cloneTo(aName);
6701
6702 return S_OK;
6703}
6704
6705STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6706{
6707 LogFlowThisFunc(("\n"));
6708 CheckComArgOutSafeArrayPointerValid(aData);
6709 if (aSize < 0)
6710 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6711
6712 AutoCaller autoCaller(this);
6713 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6714
6715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6716
6717 HRESULT rc = S_OK;
6718 Utf8Str log = queryLogFilename(aIdx);
6719
6720 /* do not unnecessarily hold the lock while doing something which does
6721 * not need the lock and potentially takes a long time. */
6722 alock.release();
6723
6724 /* Limit the chunk size to 32K for now, as that gives better performance
6725 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6726 * One byte expands to approx. 25 bytes of breathtaking XML. */
6727 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6728 com::SafeArray<BYTE> logData(cbData);
6729
6730 RTFILE LogFile;
6731 int vrc = RTFileOpen(&LogFile, log.c_str(),
6732 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6733 if (RT_SUCCESS(vrc))
6734 {
6735 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6736 if (RT_SUCCESS(vrc))
6737 logData.resize(cbData);
6738 else
6739 rc = setError(VBOX_E_IPRT_ERROR,
6740 tr("Could not read log file '%s' (%Rrc)"),
6741 log.c_str(), vrc);
6742 RTFileClose(LogFile);
6743 }
6744 else
6745 rc = setError(VBOX_E_IPRT_ERROR,
6746 tr("Could not open log file '%s' (%Rrc)"),
6747 log.c_str(), vrc);
6748
6749 if (FAILED(rc))
6750 logData.resize(0);
6751 logData.detachTo(ComSafeArrayOutArg(aData));
6752
6753 return rc;
6754}
6755
6756
6757/**
6758 * Currently this method doesn't attach device to the running VM,
6759 * just makes sure it's plugged on next VM start.
6760 */
6761STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6762{
6763 AutoCaller autoCaller(this);
6764 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6765
6766 // lock scope
6767 {
6768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6769
6770 HRESULT rc = checkStateDependency(MutableStateDep);
6771 if (FAILED(rc)) return rc;
6772
6773 ChipsetType_T aChipset = ChipsetType_PIIX3;
6774 COMGETTER(ChipsetType)(&aChipset);
6775
6776 if (aChipset != ChipsetType_ICH9)
6777 {
6778 return setError(E_INVALIDARG,
6779 tr("Host PCI attachment only supported with ICH9 chipset"));
6780 }
6781
6782 // check if device with this host PCI address already attached
6783 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6784 it != mHWData->mPCIDeviceAssignments.end();
6785 ++it)
6786 {
6787 LONG iHostAddress = -1;
6788 ComPtr<PCIDeviceAttachment> pAttach;
6789 pAttach = *it;
6790 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6791 if (iHostAddress == hostAddress)
6792 return setError(E_INVALIDARG,
6793 tr("Device with host PCI address already attached to this VM"));
6794 }
6795
6796 ComObjPtr<PCIDeviceAttachment> pda;
6797 char name[32];
6798
6799 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6800 Bstr bname(name);
6801 pda.createObject();
6802 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6803 setModified(IsModified_MachineData);
6804 mHWData.backup();
6805 mHWData->mPCIDeviceAssignments.push_back(pda);
6806 }
6807
6808 return S_OK;
6809}
6810
6811/**
6812 * Currently this method doesn't detach device from the running VM,
6813 * just makes sure it's not plugged on next VM start.
6814 */
6815STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6816{
6817 AutoCaller autoCaller(this);
6818 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6819
6820 ComObjPtr<PCIDeviceAttachment> pAttach;
6821 bool fRemoved = false;
6822 HRESULT rc;
6823
6824 // lock scope
6825 {
6826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6827
6828 rc = checkStateDependency(MutableStateDep);
6829 if (FAILED(rc)) return rc;
6830
6831 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6832 it != mHWData->mPCIDeviceAssignments.end();
6833 ++it)
6834 {
6835 LONG iHostAddress = -1;
6836 pAttach = *it;
6837 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6838 if (iHostAddress != -1 && iHostAddress == hostAddress)
6839 {
6840 setModified(IsModified_MachineData);
6841 mHWData.backup();
6842 mHWData->mPCIDeviceAssignments.remove(pAttach);
6843 fRemoved = true;
6844 break;
6845 }
6846 }
6847 }
6848
6849
6850 /* Fire event outside of the lock */
6851 if (fRemoved)
6852 {
6853 Assert(!pAttach.isNull());
6854 ComPtr<IEventSource> es;
6855 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6856 Assert(SUCCEEDED(rc));
6857 Bstr mid;
6858 rc = this->COMGETTER(Id)(mid.asOutParam());
6859 Assert(SUCCEEDED(rc));
6860 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6861 }
6862
6863 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6864 tr("No host PCI device %08x attached"),
6865 hostAddress
6866 );
6867}
6868
6869STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6870{
6871 CheckComArgOutSafeArrayPointerValid(aAssignments);
6872
6873 AutoCaller autoCaller(this);
6874 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6875
6876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6877
6878 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6879 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6880
6881 return S_OK;
6882}
6883
6884STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6885{
6886 CheckComArgOutPointerValid(aBandwidthControl);
6887
6888 AutoCaller autoCaller(this);
6889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6890
6891 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6892
6893 return S_OK;
6894}
6895
6896STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6897{
6898 CheckComArgOutPointerValid(pfEnabled);
6899 AutoCaller autoCaller(this);
6900 HRESULT hrc = autoCaller.rc();
6901 if (SUCCEEDED(hrc))
6902 {
6903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6904 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6905 }
6906 return hrc;
6907}
6908
6909STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6910{
6911 AutoCaller autoCaller(this);
6912 HRESULT hrc = autoCaller.rc();
6913 if (SUCCEEDED(hrc))
6914 {
6915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6916 hrc = checkStateDependency(MutableStateDep);
6917 if (SUCCEEDED(hrc))
6918 {
6919 hrc = mHWData.backupEx();
6920 if (SUCCEEDED(hrc))
6921 {
6922 setModified(IsModified_MachineData);
6923 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6924 }
6925 }
6926 }
6927 return hrc;
6928}
6929
6930STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6931{
6932 CheckComArgOutPointerValid(pbstrConfig);
6933 AutoCaller autoCaller(this);
6934 HRESULT hrc = autoCaller.rc();
6935 if (SUCCEEDED(hrc))
6936 {
6937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6938 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6939 }
6940 return hrc;
6941}
6942
6943STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6944{
6945 CheckComArgStr(bstrConfig);
6946 AutoCaller autoCaller(this);
6947 HRESULT hrc = autoCaller.rc();
6948 if (SUCCEEDED(hrc))
6949 {
6950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6951 hrc = checkStateDependency(MutableStateDep);
6952 if (SUCCEEDED(hrc))
6953 {
6954 hrc = mHWData.backupEx();
6955 if (SUCCEEDED(hrc))
6956 {
6957 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6958 if (SUCCEEDED(hrc))
6959 setModified(IsModified_MachineData);
6960 }
6961 }
6962 }
6963 return hrc;
6964
6965}
6966
6967STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6968{
6969 CheckComArgOutPointerValid(pfAllow);
6970 AutoCaller autoCaller(this);
6971 HRESULT hrc = autoCaller.rc();
6972 if (SUCCEEDED(hrc))
6973 {
6974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6975 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6976 }
6977 return hrc;
6978}
6979
6980STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6981{
6982 AutoCaller autoCaller(this);
6983 HRESULT hrc = autoCaller.rc();
6984 if (SUCCEEDED(hrc))
6985 {
6986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6987 hrc = checkStateDependency(MutableStateDep);
6988 if (SUCCEEDED(hrc))
6989 {
6990 hrc = mHWData.backupEx();
6991 if (SUCCEEDED(hrc))
6992 {
6993 setModified(IsModified_MachineData);
6994 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6995 }
6996 }
6997 }
6998 return hrc;
6999}
7000
7001STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7002{
7003 CheckComArgOutPointerValid(pfEnabled);
7004 AutoCaller autoCaller(this);
7005 HRESULT hrc = autoCaller.rc();
7006 if (SUCCEEDED(hrc))
7007 {
7008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7009 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7010 }
7011 return hrc;
7012}
7013
7014STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7015{
7016 AutoCaller autoCaller(this);
7017 HRESULT hrc = autoCaller.rc();
7018 if (SUCCEEDED(hrc))
7019 {
7020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7021 hrc = checkStateDependency(MutableStateDep);
7022 if ( SUCCEEDED(hrc)
7023 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7024 {
7025 AutostartDb *autostartDb = mParent->getAutostartDb();
7026 int vrc;
7027
7028 if (fEnabled)
7029 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7030 else
7031 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7032
7033 if (RT_SUCCESS(vrc))
7034 {
7035 hrc = mHWData.backupEx();
7036 if (SUCCEEDED(hrc))
7037 {
7038 setModified(IsModified_MachineData);
7039 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7040 }
7041 }
7042 else if (vrc == VERR_NOT_SUPPORTED)
7043 hrc = setError(VBOX_E_NOT_SUPPORTED,
7044 tr("The VM autostart feature is not supported on this platform"));
7045 else if (vrc == VERR_PATH_NOT_FOUND)
7046 hrc = setError(E_FAIL,
7047 tr("The path to the autostart database is not set"));
7048 else
7049 hrc = setError(E_UNEXPECTED,
7050 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7051 fEnabled ? "Adding" : "Removing",
7052 mUserData->s.strName.c_str(), vrc);
7053 }
7054 }
7055 return hrc;
7056}
7057
7058STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7059{
7060 CheckComArgOutPointerValid(puDelay);
7061 AutoCaller autoCaller(this);
7062 HRESULT hrc = autoCaller.rc();
7063 if (SUCCEEDED(hrc))
7064 {
7065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7066 *puDelay = mHWData->mAutostart.uAutostartDelay;
7067 }
7068 return hrc;
7069}
7070
7071STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7072{
7073 AutoCaller autoCaller(this);
7074 HRESULT hrc = autoCaller.rc();
7075 if (SUCCEEDED(hrc))
7076 {
7077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7078 hrc = checkStateDependency(MutableStateDep);
7079 if (SUCCEEDED(hrc))
7080 {
7081 hrc = mHWData.backupEx();
7082 if (SUCCEEDED(hrc))
7083 {
7084 setModified(IsModified_MachineData);
7085 mHWData->mAutostart.uAutostartDelay = uDelay;
7086 }
7087 }
7088 }
7089 return hrc;
7090}
7091
7092STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7093{
7094 CheckComArgOutPointerValid(penmAutostopType);
7095 AutoCaller autoCaller(this);
7096 HRESULT hrc = autoCaller.rc();
7097 if (SUCCEEDED(hrc))
7098 {
7099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7100 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7101 }
7102 return hrc;
7103}
7104
7105STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7106{
7107 AutoCaller autoCaller(this);
7108 HRESULT hrc = autoCaller.rc();
7109 if (SUCCEEDED(hrc))
7110 {
7111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7112 hrc = checkStateDependency(MutableStateDep);
7113 if ( SUCCEEDED(hrc)
7114 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7115 {
7116 AutostartDb *autostartDb = mParent->getAutostartDb();
7117 int vrc;
7118
7119 if (enmAutostopType != AutostopType_Disabled)
7120 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7121 else
7122 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7123
7124 if (RT_SUCCESS(vrc))
7125 {
7126 hrc = mHWData.backupEx();
7127 if (SUCCEEDED(hrc))
7128 {
7129 setModified(IsModified_MachineData);
7130 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7131 }
7132 }
7133 else if (vrc == VERR_NOT_SUPPORTED)
7134 hrc = setError(VBOX_E_NOT_SUPPORTED,
7135 tr("The VM autostop feature is not supported on this platform"));
7136 else if (vrc == VERR_PATH_NOT_FOUND)
7137 hrc = setError(E_FAIL,
7138 tr("The path to the autostart database is not set"));
7139 else
7140 hrc = setError(E_UNEXPECTED,
7141 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7142 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7143 mUserData->s.strName.c_str(), vrc);
7144 }
7145 }
7146 return hrc;
7147}
7148
7149STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7150{
7151 CheckComArgOutPointerValid(aDefaultFrontend);
7152 AutoCaller autoCaller(this);
7153 HRESULT hrc = autoCaller.rc();
7154 if (SUCCEEDED(hrc))
7155 {
7156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7157 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7158 }
7159 return hrc;
7160}
7161
7162STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7163{
7164 CheckComArgStr(aDefaultFrontend);
7165 AutoCaller autoCaller(this);
7166 HRESULT hrc = autoCaller.rc();
7167 if (SUCCEEDED(hrc))
7168 {
7169 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7170 hrc = checkStateDependency(MutableOrSavedStateDep);
7171 if (SUCCEEDED(hrc))
7172 {
7173 hrc = mHWData.backupEx();
7174 if (SUCCEEDED(hrc))
7175 {
7176 setModified(IsModified_MachineData);
7177 mHWData->mDefaultFrontend = aDefaultFrontend;
7178 }
7179 }
7180 }
7181 return hrc;
7182}
7183
7184
7185STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7186{
7187 LogFlowFuncEnter();
7188
7189 CheckComArgNotNull(pTarget);
7190 CheckComArgOutPointerValid(pProgress);
7191
7192 /* Convert the options. */
7193 RTCList<CloneOptions_T> optList;
7194 if (options != NULL)
7195 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7196
7197 if (optList.contains(CloneOptions_Link))
7198 {
7199 if (!isSnapshotMachine())
7200 return setError(E_INVALIDARG,
7201 tr("Linked clone can only be created from a snapshot"));
7202 if (mode != CloneMode_MachineState)
7203 return setError(E_INVALIDARG,
7204 tr("Linked clone can only be created for a single machine state"));
7205 }
7206 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7207
7208 AutoCaller autoCaller(this);
7209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7210
7211
7212 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7213
7214 HRESULT rc = pWorker->start(pProgress);
7215
7216 LogFlowFuncLeave();
7217
7218 return rc;
7219}
7220
7221// public methods for internal purposes
7222/////////////////////////////////////////////////////////////////////////////
7223
7224/**
7225 * Adds the given IsModified_* flag to the dirty flags of the machine.
7226 * This must be called either during loadSettings or under the machine write lock.
7227 * @param fl
7228 */
7229void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7230{
7231 mData->flModifications |= fl;
7232 if (fAllowStateModification && isStateModificationAllowed())
7233 mData->mCurrentStateModified = true;
7234}
7235
7236/**
7237 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7238 * care of the write locking.
7239 *
7240 * @param fModifications The flag to add.
7241 */
7242void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7243{
7244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7245 setModified(fModification, fAllowStateModification);
7246}
7247
7248/**
7249 * Saves the registry entry of this machine to the given configuration node.
7250 *
7251 * @param aEntryNode Node to save the registry entry to.
7252 *
7253 * @note locks this object for reading.
7254 */
7255HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7256{
7257 AutoLimitedCaller autoCaller(this);
7258 AssertComRCReturnRC(autoCaller.rc());
7259
7260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7261
7262 data.uuid = mData->mUuid;
7263 data.strSettingsFile = mData->m_strConfigFile;
7264
7265 return S_OK;
7266}
7267
7268/**
7269 * Calculates the absolute path of the given path taking the directory of the
7270 * machine settings file as the current directory.
7271 *
7272 * @param aPath Path to calculate the absolute path for.
7273 * @param aResult Where to put the result (used only on success, can be the
7274 * same Utf8Str instance as passed in @a aPath).
7275 * @return IPRT result.
7276 *
7277 * @note Locks this object for reading.
7278 */
7279int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7280{
7281 AutoCaller autoCaller(this);
7282 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7283
7284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7285
7286 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7287
7288 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7289
7290 strSettingsDir.stripFilename();
7291 char folder[RTPATH_MAX];
7292 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7293 if (RT_SUCCESS(vrc))
7294 aResult = folder;
7295
7296 return vrc;
7297}
7298
7299/**
7300 * Copies strSource to strTarget, making it relative to the machine folder
7301 * if it is a subdirectory thereof, or simply copying it otherwise.
7302 *
7303 * @param strSource Path to evaluate and copy.
7304 * @param strTarget Buffer to receive target path.
7305 *
7306 * @note Locks this object for reading.
7307 */
7308void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7309 Utf8Str &strTarget)
7310{
7311 AutoCaller autoCaller(this);
7312 AssertComRCReturn(autoCaller.rc(), (void)0);
7313
7314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7315
7316 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7317 // use strTarget as a temporary buffer to hold the machine settings dir
7318 strTarget = mData->m_strConfigFileFull;
7319 strTarget.stripFilename();
7320 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7321 {
7322 // is relative: then append what's left
7323 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7324 // for empty paths (only possible for subdirs) use "." to avoid
7325 // triggering default settings for not present config attributes.
7326 if (strTarget.isEmpty())
7327 strTarget = ".";
7328 }
7329 else
7330 // is not relative: then overwrite
7331 strTarget = strSource;
7332}
7333
7334/**
7335 * Returns the full path to the machine's log folder in the
7336 * \a aLogFolder argument.
7337 */
7338void Machine::getLogFolder(Utf8Str &aLogFolder)
7339{
7340 AutoCaller autoCaller(this);
7341 AssertComRCReturnVoid(autoCaller.rc());
7342
7343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7344
7345 char szTmp[RTPATH_MAX];
7346 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7347 if (RT_SUCCESS(vrc))
7348 {
7349 if (szTmp[0] && !mUserData.isNull())
7350 {
7351 char szTmp2[RTPATH_MAX];
7352 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7353 if (RT_SUCCESS(vrc))
7354 aLogFolder = BstrFmt("%s%c%s",
7355 szTmp2,
7356 RTPATH_DELIMITER,
7357 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7358 }
7359 else
7360 vrc = VERR_PATH_IS_RELATIVE;
7361 }
7362
7363 if (RT_FAILURE(vrc))
7364 {
7365 // fallback if VBOX_USER_LOGHOME is not set or invalid
7366 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7367 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7368 aLogFolder.append(RTPATH_DELIMITER);
7369 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7370 }
7371}
7372
7373/**
7374 * Returns the full path to the machine's log file for an given index.
7375 */
7376Utf8Str Machine::queryLogFilename(ULONG idx)
7377{
7378 Utf8Str logFolder;
7379 getLogFolder(logFolder);
7380 Assert(logFolder.length());
7381 Utf8Str log;
7382 if (idx == 0)
7383 log = Utf8StrFmt("%s%cVBox.log",
7384 logFolder.c_str(), RTPATH_DELIMITER);
7385 else
7386 log = Utf8StrFmt("%s%cVBox.log.%d",
7387 logFolder.c_str(), RTPATH_DELIMITER, idx);
7388 return log;
7389}
7390
7391/**
7392 * Composes a unique saved state filename based on the current system time. The filename is
7393 * granular to the second so this will work so long as no more than one snapshot is taken on
7394 * a machine per second.
7395 *
7396 * Before version 4.1, we used this formula for saved state files:
7397 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7398 * which no longer works because saved state files can now be shared between the saved state of the
7399 * "saved" machine and an online snapshot, and the following would cause problems:
7400 * 1) save machine
7401 * 2) create online snapshot from that machine state --> reusing saved state file
7402 * 3) save machine again --> filename would be reused, breaking the online snapshot
7403 *
7404 * So instead we now use a timestamp.
7405 *
7406 * @param str
7407 */
7408void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7409{
7410 AutoCaller autoCaller(this);
7411 AssertComRCReturnVoid(autoCaller.rc());
7412
7413 {
7414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7415 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7416 }
7417
7418 RTTIMESPEC ts;
7419 RTTimeNow(&ts);
7420 RTTIME time;
7421 RTTimeExplode(&time, &ts);
7422
7423 strStateFilePath += RTPATH_DELIMITER;
7424 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7425 time.i32Year, time.u8Month, time.u8MonthDay,
7426 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7427}
7428
7429/**
7430 * @note Locks this object for writing, calls the client process
7431 * (inside the lock).
7432 */
7433HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7434 const Utf8Str &strFrontend,
7435 const Utf8Str &strEnvironment,
7436 ProgressProxy *aProgress)
7437{
7438 LogFlowThisFuncEnter();
7439
7440 AssertReturn(aControl, E_FAIL);
7441 AssertReturn(aProgress, E_FAIL);
7442
7443 AutoCaller autoCaller(this);
7444 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7445
7446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7447
7448 if (!mData->mRegistered)
7449 return setError(E_UNEXPECTED,
7450 tr("The machine '%s' is not registered"),
7451 mUserData->s.strName.c_str());
7452
7453 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7454
7455 if ( mData->mSession.mState == SessionState_Locked
7456 || mData->mSession.mState == SessionState_Spawning
7457 || mData->mSession.mState == SessionState_Unlocking)
7458 return setError(VBOX_E_INVALID_OBJECT_STATE,
7459 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7460 mUserData->s.strName.c_str());
7461
7462 /* may not be busy */
7463 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7464
7465 /* get the path to the executable */
7466 char szPath[RTPATH_MAX];
7467 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7468 size_t sz = strlen(szPath);
7469 szPath[sz++] = RTPATH_DELIMITER;
7470 szPath[sz] = 0;
7471 char *cmd = szPath + sz;
7472 sz = RTPATH_MAX - sz;
7473
7474 int vrc = VINF_SUCCESS;
7475 RTPROCESS pid = NIL_RTPROCESS;
7476
7477 RTENV env = RTENV_DEFAULT;
7478
7479 if (!strEnvironment.isEmpty())
7480 {
7481 char *newEnvStr = NULL;
7482
7483 do
7484 {
7485 /* clone the current environment */
7486 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7487 AssertRCBreakStmt(vrc2, vrc = vrc2);
7488
7489 newEnvStr = RTStrDup(strEnvironment.c_str());
7490 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7491
7492 /* put new variables to the environment
7493 * (ignore empty variable names here since RTEnv API
7494 * intentionally doesn't do that) */
7495 char *var = newEnvStr;
7496 for (char *p = newEnvStr; *p; ++p)
7497 {
7498 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7499 {
7500 *p = '\0';
7501 if (*var)
7502 {
7503 char *val = strchr(var, '=');
7504 if (val)
7505 {
7506 *val++ = '\0';
7507 vrc2 = RTEnvSetEx(env, var, val);
7508 }
7509 else
7510 vrc2 = RTEnvUnsetEx(env, var);
7511 if (RT_FAILURE(vrc2))
7512 break;
7513 }
7514 var = p + 1;
7515 }
7516 }
7517 if (RT_SUCCESS(vrc2) && *var)
7518 vrc2 = RTEnvPutEx(env, var);
7519
7520 AssertRCBreakStmt(vrc2, vrc = vrc2);
7521 }
7522 while (0);
7523
7524 if (newEnvStr != NULL)
7525 RTStrFree(newEnvStr);
7526 }
7527
7528 /* Qt is default */
7529#ifdef VBOX_WITH_QTGUI
7530 if (strFrontend == "gui" || strFrontend == "GUI/Qt" || strFrontend == "")
7531 {
7532# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7533 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7534# else
7535 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7536# endif
7537 Assert(sz >= sizeof(VirtualBox_exe));
7538 strcpy(cmd, VirtualBox_exe);
7539
7540 Utf8Str idStr = mData->mUuid.toString();
7541 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7542 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7543 }
7544#else /* !VBOX_WITH_QTGUI */
7545 if (0)
7546 ;
7547#endif /* VBOX_WITH_QTGUI */
7548
7549 else
7550
7551#ifdef VBOX_WITH_VBOXSDL
7552 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7553 {
7554 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7555 Assert(sz >= sizeof(VBoxSDL_exe));
7556 strcpy(cmd, VBoxSDL_exe);
7557
7558 Utf8Str idStr = mData->mUuid.toString();
7559 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7560 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7561 }
7562#else /* !VBOX_WITH_VBOXSDL */
7563 if (0)
7564 ;
7565#endif /* !VBOX_WITH_VBOXSDL */
7566
7567 else
7568
7569#ifdef VBOX_WITH_HEADLESS
7570 if ( strFrontend == "headless"
7571 || strFrontend == "capture"
7572 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7573 )
7574 {
7575 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7576 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7577 * and a VM works even if the server has not been installed.
7578 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7579 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7580 * differently in 4.0 and 3.x.
7581 */
7582 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7583 Assert(sz >= sizeof(VBoxHeadless_exe));
7584 strcpy(cmd, VBoxHeadless_exe);
7585
7586 Utf8Str idStr = mData->mUuid.toString();
7587 /* Leave space for "--capture" arg. */
7588 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7589 "--startvm", idStr.c_str(),
7590 "--vrde", "config",
7591 0, /* For "--capture". */
7592 0 };
7593 if (strFrontend == "capture")
7594 {
7595 unsigned pos = RT_ELEMENTS(args) - 2;
7596 args[pos] = "--capture";
7597 }
7598 vrc = RTProcCreate(szPath, args, env,
7599#ifdef RT_OS_WINDOWS
7600 RTPROC_FLAGS_NO_WINDOW
7601#else
7602 0
7603#endif
7604 , &pid);
7605 }
7606#else /* !VBOX_WITH_HEADLESS */
7607 if (0)
7608 ;
7609#endif /* !VBOX_WITH_HEADLESS */
7610 else
7611 {
7612 RTEnvDestroy(env);
7613 return setError(E_INVALIDARG,
7614 tr("Invalid frontend name: '%s'"),
7615 strFrontend.c_str());
7616 }
7617
7618 RTEnvDestroy(env);
7619
7620 if (RT_FAILURE(vrc))
7621 return setError(VBOX_E_IPRT_ERROR,
7622 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7623 mUserData->s.strName.c_str(), vrc);
7624
7625 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7626
7627 /*
7628 * Note that we don't release the lock here before calling the client,
7629 * because it doesn't need to call us back if called with a NULL argument.
7630 * Releasing the lock here is dangerous because we didn't prepare the
7631 * launch data yet, but the client we've just started may happen to be
7632 * too fast and call openSession() that will fail (because of PID, etc.),
7633 * so that the Machine will never get out of the Spawning session state.
7634 */
7635
7636 /* inform the session that it will be a remote one */
7637 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7638 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7639 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7640
7641 if (FAILED(rc))
7642 {
7643 /* restore the session state */
7644 mData->mSession.mState = SessionState_Unlocked;
7645 /* The failure may occur w/o any error info (from RPC), so provide one */
7646 return setError(VBOX_E_VM_ERROR,
7647 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7648 }
7649
7650 /* attach launch data to the machine */
7651 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7652 mData->mSession.mRemoteControls.push_back(aControl);
7653 mData->mSession.mProgress = aProgress;
7654 mData->mSession.mPID = pid;
7655 mData->mSession.mState = SessionState_Spawning;
7656 mData->mSession.mType = strFrontend;
7657
7658 LogFlowThisFuncLeave();
7659 return S_OK;
7660}
7661
7662/**
7663 * Returns @c true if the given machine has an open direct session and returns
7664 * the session machine instance and additional session data (on some platforms)
7665 * if so.
7666 *
7667 * Note that when the method returns @c false, the arguments remain unchanged.
7668 *
7669 * @param aMachine Session machine object.
7670 * @param aControl Direct session control object (optional).
7671 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7672 *
7673 * @note locks this object for reading.
7674 */
7675#if defined(RT_OS_WINDOWS)
7676bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7677 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7678 HANDLE *aIPCSem /*= NULL*/,
7679 bool aAllowClosing /*= false*/)
7680#elif defined(RT_OS_OS2)
7681bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7682 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7683 HMTX *aIPCSem /*= NULL*/,
7684 bool aAllowClosing /*= false*/)
7685#else
7686bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7687 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7688 bool aAllowClosing /*= false*/)
7689#endif
7690{
7691 AutoLimitedCaller autoCaller(this);
7692 AssertComRCReturn(autoCaller.rc(), false);
7693
7694 /* just return false for inaccessible machines */
7695 if (autoCaller.state() != Ready)
7696 return false;
7697
7698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7699
7700 if ( mData->mSession.mState == SessionState_Locked
7701 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7702 )
7703 {
7704 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7705
7706 aMachine = mData->mSession.mMachine;
7707
7708 if (aControl != NULL)
7709 *aControl = mData->mSession.mDirectControl;
7710
7711#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7712 /* Additional session data */
7713 if (aIPCSem != NULL)
7714 *aIPCSem = aMachine->mIPCSem;
7715#endif
7716 return true;
7717 }
7718
7719 return false;
7720}
7721
7722/**
7723 * Returns @c true if the given machine has an spawning direct session and
7724 * returns and additional session data (on some platforms) if so.
7725 *
7726 * Note that when the method returns @c false, the arguments remain unchanged.
7727 *
7728 * @param aPID PID of the spawned direct session process.
7729 *
7730 * @note locks this object for reading.
7731 */
7732#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7733bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7734#else
7735bool Machine::isSessionSpawning()
7736#endif
7737{
7738 AutoLimitedCaller autoCaller(this);
7739 AssertComRCReturn(autoCaller.rc(), false);
7740
7741 /* just return false for inaccessible machines */
7742 if (autoCaller.state() != Ready)
7743 return false;
7744
7745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7746
7747 if (mData->mSession.mState == SessionState_Spawning)
7748 {
7749#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7750 /* Additional session data */
7751 if (aPID != NULL)
7752 {
7753 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7754 *aPID = mData->mSession.mPID;
7755 }
7756#endif
7757 return true;
7758 }
7759
7760 return false;
7761}
7762
7763/**
7764 * Called from the client watcher thread to check for unexpected client process
7765 * death during Session_Spawning state (e.g. before it successfully opened a
7766 * direct session).
7767 *
7768 * On Win32 and on OS/2, this method is called only when we've got the
7769 * direct client's process termination notification, so it always returns @c
7770 * true.
7771 *
7772 * On other platforms, this method returns @c true if the client process is
7773 * terminated and @c false if it's still alive.
7774 *
7775 * @note Locks this object for writing.
7776 */
7777bool Machine::checkForSpawnFailure()
7778{
7779 AutoCaller autoCaller(this);
7780 if (!autoCaller.isOk())
7781 {
7782 /* nothing to do */
7783 LogFlowThisFunc(("Already uninitialized!\n"));
7784 return true;
7785 }
7786
7787 /* VirtualBox::addProcessToReap() needs a write lock */
7788 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7789
7790 if (mData->mSession.mState != SessionState_Spawning)
7791 {
7792 /* nothing to do */
7793 LogFlowThisFunc(("Not spawning any more!\n"));
7794 return true;
7795 }
7796
7797 HRESULT rc = S_OK;
7798
7799#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7800
7801 /* the process was already unexpectedly terminated, we just need to set an
7802 * error and finalize session spawning */
7803 rc = setError(E_FAIL,
7804 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7805 getName().c_str());
7806#else
7807
7808 /* PID not yet initialized, skip check. */
7809 if (mData->mSession.mPID == NIL_RTPROCESS)
7810 return false;
7811
7812 RTPROCSTATUS status;
7813 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7814 &status);
7815
7816 if (vrc != VERR_PROCESS_RUNNING)
7817 {
7818 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7819 rc = setError(E_FAIL,
7820 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7821 getName().c_str(), status.iStatus);
7822 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7823 rc = setError(E_FAIL,
7824 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7825 getName().c_str(), status.iStatus);
7826 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7827 rc = setError(E_FAIL,
7828 tr("The virtual machine '%s' has terminated abnormally"),
7829 getName().c_str(), status.iStatus);
7830 else
7831 rc = setError(E_FAIL,
7832 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7833 getName().c_str(), rc);
7834 }
7835
7836#endif
7837
7838 if (FAILED(rc))
7839 {
7840 /* Close the remote session, remove the remote control from the list
7841 * and reset session state to Closed (@note keep the code in sync with
7842 * the relevant part in checkForSpawnFailure()). */
7843
7844 Assert(mData->mSession.mRemoteControls.size() == 1);
7845 if (mData->mSession.mRemoteControls.size() == 1)
7846 {
7847 ErrorInfoKeeper eik;
7848 mData->mSession.mRemoteControls.front()->Uninitialize();
7849 }
7850
7851 mData->mSession.mRemoteControls.clear();
7852 mData->mSession.mState = SessionState_Unlocked;
7853
7854 /* finalize the progress after setting the state */
7855 if (!mData->mSession.mProgress.isNull())
7856 {
7857 mData->mSession.mProgress->notifyComplete(rc);
7858 mData->mSession.mProgress.setNull();
7859 }
7860
7861 mParent->addProcessToReap(mData->mSession.mPID);
7862 mData->mSession.mPID = NIL_RTPROCESS;
7863
7864 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7865 return true;
7866 }
7867
7868 return false;
7869}
7870
7871/**
7872 * Checks whether the machine can be registered. If so, commits and saves
7873 * all settings.
7874 *
7875 * @note Must be called from mParent's write lock. Locks this object and
7876 * children for writing.
7877 */
7878HRESULT Machine::prepareRegister()
7879{
7880 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7881
7882 AutoLimitedCaller autoCaller(this);
7883 AssertComRCReturnRC(autoCaller.rc());
7884
7885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7886
7887 /* wait for state dependents to drop to zero */
7888 ensureNoStateDependencies();
7889
7890 if (!mData->mAccessible)
7891 return setError(VBOX_E_INVALID_OBJECT_STATE,
7892 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7893 mUserData->s.strName.c_str(),
7894 mData->mUuid.toString().c_str());
7895
7896 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7897
7898 if (mData->mRegistered)
7899 return setError(VBOX_E_INVALID_OBJECT_STATE,
7900 tr("The machine '%s' with UUID {%s} is already registered"),
7901 mUserData->s.strName.c_str(),
7902 mData->mUuid.toString().c_str());
7903
7904 HRESULT rc = S_OK;
7905
7906 // Ensure the settings are saved. If we are going to be registered and
7907 // no config file exists yet, create it by calling saveSettings() too.
7908 if ( (mData->flModifications)
7909 || (!mData->pMachineConfigFile->fileExists())
7910 )
7911 {
7912 rc = saveSettings(NULL);
7913 // no need to check whether VirtualBox.xml needs saving too since
7914 // we can't have a machine XML file rename pending
7915 if (FAILED(rc)) return rc;
7916 }
7917
7918 /* more config checking goes here */
7919
7920 if (SUCCEEDED(rc))
7921 {
7922 /* we may have had implicit modifications we want to fix on success */
7923 commit();
7924
7925 mData->mRegistered = true;
7926 }
7927 else
7928 {
7929 /* we may have had implicit modifications we want to cancel on failure*/
7930 rollback(false /* aNotify */);
7931 }
7932
7933 return rc;
7934}
7935
7936/**
7937 * Increases the number of objects dependent on the machine state or on the
7938 * registered state. Guarantees that these two states will not change at least
7939 * until #releaseStateDependency() is called.
7940 *
7941 * Depending on the @a aDepType value, additional state checks may be made.
7942 * These checks will set extended error info on failure. See
7943 * #checkStateDependency() for more info.
7944 *
7945 * If this method returns a failure, the dependency is not added and the caller
7946 * is not allowed to rely on any particular machine state or registration state
7947 * value and may return the failed result code to the upper level.
7948 *
7949 * @param aDepType Dependency type to add.
7950 * @param aState Current machine state (NULL if not interested).
7951 * @param aRegistered Current registered state (NULL if not interested).
7952 *
7953 * @note Locks this object for writing.
7954 */
7955HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7956 MachineState_T *aState /* = NULL */,
7957 BOOL *aRegistered /* = NULL */)
7958{
7959 AutoCaller autoCaller(this);
7960 AssertComRCReturnRC(autoCaller.rc());
7961
7962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7963
7964 HRESULT rc = checkStateDependency(aDepType);
7965 if (FAILED(rc)) return rc;
7966
7967 {
7968 if (mData->mMachineStateChangePending != 0)
7969 {
7970 /* ensureNoStateDependencies() is waiting for state dependencies to
7971 * drop to zero so don't add more. It may make sense to wait a bit
7972 * and retry before reporting an error (since the pending state
7973 * transition should be really quick) but let's just assert for
7974 * now to see if it ever happens on practice. */
7975
7976 AssertFailed();
7977
7978 return setError(E_ACCESSDENIED,
7979 tr("Machine state change is in progress. Please retry the operation later."));
7980 }
7981
7982 ++mData->mMachineStateDeps;
7983 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7984 }
7985
7986 if (aState)
7987 *aState = mData->mMachineState;
7988 if (aRegistered)
7989 *aRegistered = mData->mRegistered;
7990
7991 return S_OK;
7992}
7993
7994/**
7995 * Decreases the number of objects dependent on the machine state.
7996 * Must always complete the #addStateDependency() call after the state
7997 * dependency is no more necessary.
7998 */
7999void Machine::releaseStateDependency()
8000{
8001 AutoCaller autoCaller(this);
8002 AssertComRCReturnVoid(autoCaller.rc());
8003
8004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8005
8006 /* releaseStateDependency() w/o addStateDependency()? */
8007 AssertReturnVoid(mData->mMachineStateDeps != 0);
8008 -- mData->mMachineStateDeps;
8009
8010 if (mData->mMachineStateDeps == 0)
8011 {
8012 /* inform ensureNoStateDependencies() that there are no more deps */
8013 if (mData->mMachineStateChangePending != 0)
8014 {
8015 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8016 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8017 }
8018 }
8019}
8020
8021Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8022{
8023 /* start with nothing found */
8024 Utf8Str strResult("");
8025
8026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8027
8028 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8029 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8030 // found:
8031 strResult = it->second; // source is a Utf8Str
8032
8033 return strResult;
8034}
8035
8036// protected methods
8037/////////////////////////////////////////////////////////////////////////////
8038
8039/**
8040 * Performs machine state checks based on the @a aDepType value. If a check
8041 * fails, this method will set extended error info, otherwise it will return
8042 * S_OK. It is supposed, that on failure, the caller will immediately return
8043 * the return value of this method to the upper level.
8044 *
8045 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8046 *
8047 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8048 * current state of this machine object allows to change settings of the
8049 * machine (i.e. the machine is not registered, or registered but not running
8050 * and not saved). It is useful to call this method from Machine setters
8051 * before performing any change.
8052 *
8053 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8054 * as for MutableStateDep except that if the machine is saved, S_OK is also
8055 * returned. This is useful in setters which allow changing machine
8056 * properties when it is in the saved state.
8057 *
8058 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8059 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8060 * Aborted).
8061 *
8062 * @param aDepType Dependency type to check.
8063 *
8064 * @note Non Machine based classes should use #addStateDependency() and
8065 * #releaseStateDependency() methods or the smart AutoStateDependency
8066 * template.
8067 *
8068 * @note This method must be called from under this object's read or write
8069 * lock.
8070 */
8071HRESULT Machine::checkStateDependency(StateDependency aDepType)
8072{
8073 switch (aDepType)
8074 {
8075 case AnyStateDep:
8076 {
8077 break;
8078 }
8079 case MutableStateDep:
8080 {
8081 if ( mData->mRegistered
8082 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8083 || ( mData->mMachineState != MachineState_Paused
8084 && mData->mMachineState != MachineState_Running
8085 && mData->mMachineState != MachineState_Aborted
8086 && mData->mMachineState != MachineState_Teleported
8087 && mData->mMachineState != MachineState_PoweredOff
8088 )
8089 )
8090 )
8091 return setError(VBOX_E_INVALID_VM_STATE,
8092 tr("The machine is not mutable (state is %s)"),
8093 Global::stringifyMachineState(mData->mMachineState));
8094 break;
8095 }
8096 case MutableOrSavedStateDep:
8097 {
8098 if ( mData->mRegistered
8099 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8100 || ( mData->mMachineState != MachineState_Paused
8101 && mData->mMachineState != MachineState_Running
8102 && mData->mMachineState != MachineState_Aborted
8103 && mData->mMachineState != MachineState_Teleported
8104 && mData->mMachineState != MachineState_Saved
8105 && mData->mMachineState != MachineState_PoweredOff
8106 )
8107 )
8108 )
8109 return setError(VBOX_E_INVALID_VM_STATE,
8110 tr("The machine is not mutable (state is %s)"),
8111 Global::stringifyMachineState(mData->mMachineState));
8112 break;
8113 }
8114 case OfflineStateDep:
8115 {
8116 if ( mData->mRegistered
8117 && ( !isSessionMachine()
8118 || ( mData->mMachineState != MachineState_PoweredOff
8119 && mData->mMachineState != MachineState_Saved
8120 && mData->mMachineState != MachineState_Aborted
8121 && mData->mMachineState != MachineState_Teleported
8122 )
8123 )
8124 )
8125 return setError(VBOX_E_INVALID_VM_STATE,
8126 tr("The machine is not offline (state is %s)"),
8127 Global::stringifyMachineState(mData->mMachineState));
8128 break;
8129 }
8130 }
8131
8132 return S_OK;
8133}
8134
8135/**
8136 * Helper to initialize all associated child objects and allocate data
8137 * structures.
8138 *
8139 * This method must be called as a part of the object's initialization procedure
8140 * (usually done in the #init() method).
8141 *
8142 * @note Must be called only from #init() or from #registeredInit().
8143 */
8144HRESULT Machine::initDataAndChildObjects()
8145{
8146 AutoCaller autoCaller(this);
8147 AssertComRCReturnRC(autoCaller.rc());
8148 AssertComRCReturn(autoCaller.state() == InInit ||
8149 autoCaller.state() == Limited, E_FAIL);
8150
8151 AssertReturn(!mData->mAccessible, E_FAIL);
8152
8153 /* allocate data structures */
8154 mSSData.allocate();
8155 mUserData.allocate();
8156 mHWData.allocate();
8157 mMediaData.allocate();
8158 mStorageControllers.allocate();
8159
8160 /* initialize mOSTypeId */
8161 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8162
8163 /* create associated BIOS settings object */
8164 unconst(mBIOSSettings).createObject();
8165 mBIOSSettings->init(this);
8166
8167 /* create an associated VRDE object (default is disabled) */
8168 unconst(mVRDEServer).createObject();
8169 mVRDEServer->init(this);
8170
8171 /* create associated serial port objects */
8172 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8173 {
8174 unconst(mSerialPorts[slot]).createObject();
8175 mSerialPorts[slot]->init(this, slot);
8176 }
8177
8178 /* create associated parallel port objects */
8179 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8180 {
8181 unconst(mParallelPorts[slot]).createObject();
8182 mParallelPorts[slot]->init(this, slot);
8183 }
8184
8185 /* create the audio adapter object (always present, default is disabled) */
8186 unconst(mAudioAdapter).createObject();
8187 mAudioAdapter->init(this);
8188
8189 /* create the USB controller object (always present, default is disabled) */
8190 unconst(mUSBController).createObject();
8191 mUSBController->init(this);
8192
8193 /* create associated network adapter objects */
8194 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8195 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8196 {
8197 unconst(mNetworkAdapters[slot]).createObject();
8198 mNetworkAdapters[slot]->init(this, slot);
8199 }
8200
8201 /* create the bandwidth control */
8202 unconst(mBandwidthControl).createObject();
8203 mBandwidthControl->init(this);
8204
8205 return S_OK;
8206}
8207
8208/**
8209 * Helper to uninitialize all associated child objects and to free all data
8210 * structures.
8211 *
8212 * This method must be called as a part of the object's uninitialization
8213 * procedure (usually done in the #uninit() method).
8214 *
8215 * @note Must be called only from #uninit() or from #registeredInit().
8216 */
8217void Machine::uninitDataAndChildObjects()
8218{
8219 AutoCaller autoCaller(this);
8220 AssertComRCReturnVoid(autoCaller.rc());
8221 AssertComRCReturnVoid( autoCaller.state() == InUninit
8222 || autoCaller.state() == Limited);
8223
8224 /* tell all our other child objects we've been uninitialized */
8225 if (mBandwidthControl)
8226 {
8227 mBandwidthControl->uninit();
8228 unconst(mBandwidthControl).setNull();
8229 }
8230
8231 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8232 {
8233 if (mNetworkAdapters[slot])
8234 {
8235 mNetworkAdapters[slot]->uninit();
8236 unconst(mNetworkAdapters[slot]).setNull();
8237 }
8238 }
8239
8240 if (mUSBController)
8241 {
8242 mUSBController->uninit();
8243 unconst(mUSBController).setNull();
8244 }
8245
8246 if (mAudioAdapter)
8247 {
8248 mAudioAdapter->uninit();
8249 unconst(mAudioAdapter).setNull();
8250 }
8251
8252 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8253 {
8254 if (mParallelPorts[slot])
8255 {
8256 mParallelPorts[slot]->uninit();
8257 unconst(mParallelPorts[slot]).setNull();
8258 }
8259 }
8260
8261 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8262 {
8263 if (mSerialPorts[slot])
8264 {
8265 mSerialPorts[slot]->uninit();
8266 unconst(mSerialPorts[slot]).setNull();
8267 }
8268 }
8269
8270 if (mVRDEServer)
8271 {
8272 mVRDEServer->uninit();
8273 unconst(mVRDEServer).setNull();
8274 }
8275
8276 if (mBIOSSettings)
8277 {
8278 mBIOSSettings->uninit();
8279 unconst(mBIOSSettings).setNull();
8280 }
8281
8282 /* Deassociate media (only when a real Machine or a SnapshotMachine
8283 * instance is uninitialized; SessionMachine instances refer to real
8284 * Machine media). This is necessary for a clean re-initialization of
8285 * the VM after successfully re-checking the accessibility state. Note
8286 * that in case of normal Machine or SnapshotMachine uninitialization (as
8287 * a result of unregistering or deleting the snapshot), outdated media
8288 * attachments will already be uninitialized and deleted, so this
8289 * code will not affect them. */
8290 if ( !!mMediaData
8291 && (!isSessionMachine())
8292 )
8293 {
8294 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8295 it != mMediaData->mAttachments.end();
8296 ++it)
8297 {
8298 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8299 if (pMedium.isNull())
8300 continue;
8301 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8302 AssertComRC(rc);
8303 }
8304 }
8305
8306 if (!isSessionMachine() && !isSnapshotMachine())
8307 {
8308 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8309 if (mData->mFirstSnapshot)
8310 {
8311 // snapshots tree is protected by machine write lock; strictly
8312 // this isn't necessary here since we're deleting the entire
8313 // machine, but otherwise we assert in Snapshot::uninit()
8314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8315 mData->mFirstSnapshot->uninit();
8316 mData->mFirstSnapshot.setNull();
8317 }
8318
8319 mData->mCurrentSnapshot.setNull();
8320 }
8321
8322 /* free data structures (the essential mData structure is not freed here
8323 * since it may be still in use) */
8324 mMediaData.free();
8325 mStorageControllers.free();
8326 mHWData.free();
8327 mUserData.free();
8328 mSSData.free();
8329}
8330
8331/**
8332 * Returns a pointer to the Machine object for this machine that acts like a
8333 * parent for complex machine data objects such as shared folders, etc.
8334 *
8335 * For primary Machine objects and for SnapshotMachine objects, returns this
8336 * object's pointer itself. For SessionMachine objects, returns the peer
8337 * (primary) machine pointer.
8338 */
8339Machine* Machine::getMachine()
8340{
8341 if (isSessionMachine())
8342 return (Machine*)mPeer;
8343 return this;
8344}
8345
8346/**
8347 * Makes sure that there are no machine state dependents. If necessary, waits
8348 * for the number of dependents to drop to zero.
8349 *
8350 * Make sure this method is called from under this object's write lock to
8351 * guarantee that no new dependents may be added when this method returns
8352 * control to the caller.
8353 *
8354 * @note Locks this object for writing. The lock will be released while waiting
8355 * (if necessary).
8356 *
8357 * @warning To be used only in methods that change the machine state!
8358 */
8359void Machine::ensureNoStateDependencies()
8360{
8361 AssertReturnVoid(isWriteLockOnCurrentThread());
8362
8363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8364
8365 /* Wait for all state dependents if necessary */
8366 if (mData->mMachineStateDeps != 0)
8367 {
8368 /* lazy semaphore creation */
8369 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8370 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8371
8372 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8373 mData->mMachineStateDeps));
8374
8375 ++mData->mMachineStateChangePending;
8376
8377 /* reset the semaphore before waiting, the last dependent will signal
8378 * it */
8379 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8380
8381 alock.release();
8382
8383 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8384
8385 alock.acquire();
8386
8387 -- mData->mMachineStateChangePending;
8388 }
8389}
8390
8391/**
8392 * Changes the machine state and informs callbacks.
8393 *
8394 * This method is not intended to fail so it either returns S_OK or asserts (and
8395 * returns a failure).
8396 *
8397 * @note Locks this object for writing.
8398 */
8399HRESULT Machine::setMachineState(MachineState_T aMachineState)
8400{
8401 LogFlowThisFuncEnter();
8402 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8403
8404 AutoCaller autoCaller(this);
8405 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8406
8407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8408
8409 /* wait for state dependents to drop to zero */
8410 ensureNoStateDependencies();
8411
8412 if (mData->mMachineState != aMachineState)
8413 {
8414 mData->mMachineState = aMachineState;
8415
8416 RTTimeNow(&mData->mLastStateChange);
8417
8418 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8419 }
8420
8421 LogFlowThisFuncLeave();
8422 return S_OK;
8423}
8424
8425/**
8426 * Searches for a shared folder with the given logical name
8427 * in the collection of shared folders.
8428 *
8429 * @param aName logical name of the shared folder
8430 * @param aSharedFolder where to return the found object
8431 * @param aSetError whether to set the error info if the folder is
8432 * not found
8433 * @return
8434 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8435 *
8436 * @note
8437 * must be called from under the object's lock!
8438 */
8439HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8440 ComObjPtr<SharedFolder> &aSharedFolder,
8441 bool aSetError /* = false */)
8442{
8443 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8444 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8445 it != mHWData->mSharedFolders.end();
8446 ++it)
8447 {
8448 SharedFolder *pSF = *it;
8449 AutoCaller autoCaller(pSF);
8450 if (pSF->getName() == aName)
8451 {
8452 aSharedFolder = pSF;
8453 rc = S_OK;
8454 break;
8455 }
8456 }
8457
8458 if (aSetError && FAILED(rc))
8459 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8460
8461 return rc;
8462}
8463
8464/**
8465 * Initializes all machine instance data from the given settings structures
8466 * from XML. The exception is the machine UUID which needs special handling
8467 * depending on the caller's use case, so the caller needs to set that herself.
8468 *
8469 * This gets called in several contexts during machine initialization:
8470 *
8471 * -- When machine XML exists on disk already and needs to be loaded into memory,
8472 * for example, from registeredInit() to load all registered machines on
8473 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8474 * attached to the machine should be part of some media registry already.
8475 *
8476 * -- During OVF import, when a machine config has been constructed from an
8477 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8478 * ensure that the media listed as attachments in the config (which have
8479 * been imported from the OVF) receive the correct registry ID.
8480 *
8481 * -- During VM cloning.
8482 *
8483 * @param config Machine settings from XML.
8484 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8485 * @return
8486 */
8487HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8488 const Guid *puuidRegistry)
8489{
8490 // copy name, description, OS type, teleporter, UTC etc.
8491 mUserData->s = config.machineUserData;
8492
8493 // look up the object by Id to check it is valid
8494 ComPtr<IGuestOSType> guestOSType;
8495 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8496 guestOSType.asOutParam());
8497 if (FAILED(rc)) return rc;
8498
8499 // stateFile (optional)
8500 if (config.strStateFile.isEmpty())
8501 mSSData->strStateFilePath.setNull();
8502 else
8503 {
8504 Utf8Str stateFilePathFull(config.strStateFile);
8505 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8506 if (RT_FAILURE(vrc))
8507 return setError(E_FAIL,
8508 tr("Invalid saved state file path '%s' (%Rrc)"),
8509 config.strStateFile.c_str(),
8510 vrc);
8511 mSSData->strStateFilePath = stateFilePathFull;
8512 }
8513
8514 // snapshot folder needs special processing so set it again
8515 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8516 if (FAILED(rc)) return rc;
8517
8518 /* Copy the extra data items (Not in any case config is already the same as
8519 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8520 * make sure the extra data map is copied). */
8521 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8522
8523 /* currentStateModified (optional, default is true) */
8524 mData->mCurrentStateModified = config.fCurrentStateModified;
8525
8526 mData->mLastStateChange = config.timeLastStateChange;
8527
8528 /*
8529 * note: all mUserData members must be assigned prior this point because
8530 * we need to commit changes in order to let mUserData be shared by all
8531 * snapshot machine instances.
8532 */
8533 mUserData.commitCopy();
8534
8535 // machine registry, if present (must be loaded before snapshots)
8536 if (config.canHaveOwnMediaRegistry())
8537 {
8538 // determine machine folder
8539 Utf8Str strMachineFolder = getSettingsFileFull();
8540 strMachineFolder.stripFilename();
8541 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8542 config.mediaRegistry,
8543 strMachineFolder);
8544 if (FAILED(rc)) return rc;
8545 }
8546
8547 /* Snapshot node (optional) */
8548 size_t cRootSnapshots;
8549 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8550 {
8551 // there must be only one root snapshot
8552 Assert(cRootSnapshots == 1);
8553
8554 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8555
8556 rc = loadSnapshot(snap,
8557 config.uuidCurrentSnapshot,
8558 NULL); // no parent == first snapshot
8559 if (FAILED(rc)) return rc;
8560 }
8561
8562 // hardware data
8563 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8564 if (FAILED(rc)) return rc;
8565
8566 // load storage controllers
8567 rc = loadStorageControllers(config.storageMachine,
8568 puuidRegistry,
8569 NULL /* puuidSnapshot */);
8570 if (FAILED(rc)) return rc;
8571
8572 /*
8573 * NOTE: the assignment below must be the last thing to do,
8574 * otherwise it will be not possible to change the settings
8575 * somewhere in the code above because all setters will be
8576 * blocked by checkStateDependency(MutableStateDep).
8577 */
8578
8579 /* set the machine state to Aborted or Saved when appropriate */
8580 if (config.fAborted)
8581 {
8582 mSSData->strStateFilePath.setNull();
8583
8584 /* no need to use setMachineState() during init() */
8585 mData->mMachineState = MachineState_Aborted;
8586 }
8587 else if (!mSSData->strStateFilePath.isEmpty())
8588 {
8589 /* no need to use setMachineState() during init() */
8590 mData->mMachineState = MachineState_Saved;
8591 }
8592
8593 // after loading settings, we are no longer different from the XML on disk
8594 mData->flModifications = 0;
8595
8596 return S_OK;
8597}
8598
8599/**
8600 * Recursively loads all snapshots starting from the given.
8601 *
8602 * @param aNode <Snapshot> node.
8603 * @param aCurSnapshotId Current snapshot ID from the settings file.
8604 * @param aParentSnapshot Parent snapshot.
8605 */
8606HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8607 const Guid &aCurSnapshotId,
8608 Snapshot *aParentSnapshot)
8609{
8610 AssertReturn(!isSnapshotMachine(), E_FAIL);
8611 AssertReturn(!isSessionMachine(), E_FAIL);
8612
8613 HRESULT rc = S_OK;
8614
8615 Utf8Str strStateFile;
8616 if (!data.strStateFile.isEmpty())
8617 {
8618 /* optional */
8619 strStateFile = data.strStateFile;
8620 int vrc = calculateFullPath(strStateFile, strStateFile);
8621 if (RT_FAILURE(vrc))
8622 return setError(E_FAIL,
8623 tr("Invalid saved state file path '%s' (%Rrc)"),
8624 strStateFile.c_str(),
8625 vrc);
8626 }
8627
8628 /* create a snapshot machine object */
8629 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8630 pSnapshotMachine.createObject();
8631 rc = pSnapshotMachine->initFromSettings(this,
8632 data.hardware,
8633 &data.debugging,
8634 &data.autostart,
8635 data.storage,
8636 data.uuid.ref(),
8637 strStateFile);
8638 if (FAILED(rc)) return rc;
8639
8640 /* create a snapshot object */
8641 ComObjPtr<Snapshot> pSnapshot;
8642 pSnapshot.createObject();
8643 /* initialize the snapshot */
8644 rc = pSnapshot->init(mParent, // VirtualBox object
8645 data.uuid,
8646 data.strName,
8647 data.strDescription,
8648 data.timestamp,
8649 pSnapshotMachine,
8650 aParentSnapshot);
8651 if (FAILED(rc)) return rc;
8652
8653 /* memorize the first snapshot if necessary */
8654 if (!mData->mFirstSnapshot)
8655 mData->mFirstSnapshot = pSnapshot;
8656
8657 /* memorize the current snapshot when appropriate */
8658 if ( !mData->mCurrentSnapshot
8659 && pSnapshot->getId() == aCurSnapshotId
8660 )
8661 mData->mCurrentSnapshot = pSnapshot;
8662
8663 // now create the children
8664 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8665 it != data.llChildSnapshots.end();
8666 ++it)
8667 {
8668 const settings::Snapshot &childData = *it;
8669 // recurse
8670 rc = loadSnapshot(childData,
8671 aCurSnapshotId,
8672 pSnapshot); // parent = the one we created above
8673 if (FAILED(rc)) return rc;
8674 }
8675
8676 return rc;
8677}
8678
8679/**
8680 * Loads settings into mHWData.
8681 *
8682 * @param data Reference to the hardware settings.
8683 * @param pDbg Pointer to the debugging settings.
8684 * @param pAutostart Pointer to the autostart settings.
8685 */
8686HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8687 const settings::Autostart *pAutostart)
8688{
8689 AssertReturn(!isSessionMachine(), E_FAIL);
8690
8691 HRESULT rc = S_OK;
8692
8693 try
8694 {
8695 /* The hardware version attribute (optional). */
8696 mHWData->mHWVersion = data.strVersion;
8697 mHWData->mHardwareUUID = data.uuid;
8698
8699 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8700 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8701 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8702 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8703 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8704 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8705 mHWData->mPAEEnabled = data.fPAE;
8706 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8707 mHWData->mLongMode = data.enmLongMode;
8708 mHWData->mCPUCount = data.cCPUs;
8709 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8710 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8711
8712 // cpu
8713 if (mHWData->mCPUHotPlugEnabled)
8714 {
8715 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8716 it != data.llCpus.end();
8717 ++it)
8718 {
8719 const settings::Cpu &cpu = *it;
8720
8721 mHWData->mCPUAttached[cpu.ulId] = true;
8722 }
8723 }
8724
8725 // cpuid leafs
8726 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8727 it != data.llCpuIdLeafs.end();
8728 ++it)
8729 {
8730 const settings::CpuIdLeaf &leaf = *it;
8731
8732 switch (leaf.ulId)
8733 {
8734 case 0x0:
8735 case 0x1:
8736 case 0x2:
8737 case 0x3:
8738 case 0x4:
8739 case 0x5:
8740 case 0x6:
8741 case 0x7:
8742 case 0x8:
8743 case 0x9:
8744 case 0xA:
8745 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8746 break;
8747
8748 case 0x80000000:
8749 case 0x80000001:
8750 case 0x80000002:
8751 case 0x80000003:
8752 case 0x80000004:
8753 case 0x80000005:
8754 case 0x80000006:
8755 case 0x80000007:
8756 case 0x80000008:
8757 case 0x80000009:
8758 case 0x8000000A:
8759 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8760 break;
8761
8762 default:
8763 /* just ignore */
8764 break;
8765 }
8766 }
8767
8768 mHWData->mMemorySize = data.ulMemorySizeMB;
8769 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8770
8771 // boot order
8772 for (size_t i = 0;
8773 i < RT_ELEMENTS(mHWData->mBootOrder);
8774 i++)
8775 {
8776 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8777 if (it == data.mapBootOrder.end())
8778 mHWData->mBootOrder[i] = DeviceType_Null;
8779 else
8780 mHWData->mBootOrder[i] = it->second;
8781 }
8782
8783 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8784 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8785 mHWData->mMonitorCount = data.cMonitors;
8786 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8787 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8788 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8789 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8790 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8791 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8792 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8793 mHWData->mFirmwareType = data.firmwareType;
8794 mHWData->mPointingHIDType = data.pointingHIDType;
8795 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8796 mHWData->mChipsetType = data.chipsetType;
8797 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8798 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8799 mHWData->mHPETEnabled = data.fHPETEnabled;
8800
8801 /* VRDEServer */
8802 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8803 if (FAILED(rc)) return rc;
8804
8805 /* BIOS */
8806 rc = mBIOSSettings->loadSettings(data.biosSettings);
8807 if (FAILED(rc)) return rc;
8808
8809 // Bandwidth control (must come before network adapters)
8810 rc = mBandwidthControl->loadSettings(data.ioSettings);
8811 if (FAILED(rc)) return rc;
8812
8813 /* USB Controller */
8814 rc = mUSBController->loadSettings(data.usbController);
8815 if (FAILED(rc)) return rc;
8816
8817 // network adapters
8818 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8819 uint32_t oldCount = mNetworkAdapters.size();
8820 if (newCount > oldCount)
8821 {
8822 mNetworkAdapters.resize(newCount);
8823 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8824 {
8825 unconst(mNetworkAdapters[slot]).createObject();
8826 mNetworkAdapters[slot]->init(this, slot);
8827 }
8828 }
8829 else if (newCount < oldCount)
8830 mNetworkAdapters.resize(newCount);
8831 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8832 it != data.llNetworkAdapters.end();
8833 ++it)
8834 {
8835 const settings::NetworkAdapter &nic = *it;
8836
8837 /* slot unicity is guaranteed by XML Schema */
8838 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8839 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8840 if (FAILED(rc)) return rc;
8841 }
8842
8843 // serial ports
8844 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8845 it != data.llSerialPorts.end();
8846 ++it)
8847 {
8848 const settings::SerialPort &s = *it;
8849
8850 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8851 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8852 if (FAILED(rc)) return rc;
8853 }
8854
8855 // parallel ports (optional)
8856 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8857 it != data.llParallelPorts.end();
8858 ++it)
8859 {
8860 const settings::ParallelPort &p = *it;
8861
8862 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8863 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8864 if (FAILED(rc)) return rc;
8865 }
8866
8867 /* AudioAdapter */
8868 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8869 if (FAILED(rc)) return rc;
8870
8871 /* Shared folders */
8872 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8873 it != data.llSharedFolders.end();
8874 ++it)
8875 {
8876 const settings::SharedFolder &sf = *it;
8877
8878 ComObjPtr<SharedFolder> sharedFolder;
8879 /* Check for double entries. Not allowed! */
8880 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8881 if (SUCCEEDED(rc))
8882 return setError(VBOX_E_OBJECT_IN_USE,
8883 tr("Shared folder named '%s' already exists"),
8884 sf.strName.c_str());
8885
8886 /* Create the new shared folder. Don't break on error. This will be
8887 * reported when the machine starts. */
8888 sharedFolder.createObject();
8889 rc = sharedFolder->init(getMachine(),
8890 sf.strName,
8891 sf.strHostPath,
8892 RT_BOOL(sf.fWritable),
8893 RT_BOOL(sf.fAutoMount),
8894 false /* fFailOnError */);
8895 if (FAILED(rc)) return rc;
8896 mHWData->mSharedFolders.push_back(sharedFolder);
8897 }
8898
8899 // Clipboard
8900 mHWData->mClipboardMode = data.clipboardMode;
8901
8902 // drag'n'drop
8903 mHWData->mDragAndDropMode = data.dragAndDropMode;
8904
8905 // guest settings
8906 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8907
8908 // IO settings
8909 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8910 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8911
8912 // Host PCI devices
8913 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8914 it != data.pciAttachments.end();
8915 ++it)
8916 {
8917 const settings::HostPCIDeviceAttachment &hpda = *it;
8918 ComObjPtr<PCIDeviceAttachment> pda;
8919
8920 pda.createObject();
8921 pda->loadSettings(this, hpda);
8922 mHWData->mPCIDeviceAssignments.push_back(pda);
8923 }
8924
8925 /*
8926 * (The following isn't really real hardware, but it lives in HWData
8927 * for reasons of convenience.)
8928 */
8929
8930#ifdef VBOX_WITH_GUEST_PROPS
8931 /* Guest properties (optional) */
8932 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8933 it != data.llGuestProperties.end();
8934 ++it)
8935 {
8936 const settings::GuestProperty &prop = *it;
8937 uint32_t fFlags = guestProp::NILFLAG;
8938 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8939 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8940 mHWData->mGuestProperties[prop.strName] = property;
8941 }
8942
8943 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8944#endif /* VBOX_WITH_GUEST_PROPS defined */
8945
8946 rc = loadDebugging(pDbg);
8947 if (FAILED(rc))
8948 return rc;
8949
8950 mHWData->mAutostart = *pAutostart;
8951
8952 /* default frontend */
8953 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8954 }
8955 catch(std::bad_alloc &)
8956 {
8957 return E_OUTOFMEMORY;
8958 }
8959
8960 AssertComRC(rc);
8961 return rc;
8962}
8963
8964/**
8965 * Called from Machine::loadHardware() to load the debugging settings of the
8966 * machine.
8967 *
8968 * @param pDbg Pointer to the settings.
8969 */
8970HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8971{
8972 mHWData->mDebugging = *pDbg;
8973 /* no more processing currently required, this will probably change. */
8974 return S_OK;
8975}
8976
8977/**
8978 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8979 *
8980 * @param data
8981 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8982 * @param puuidSnapshot
8983 * @return
8984 */
8985HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8986 const Guid *puuidRegistry,
8987 const Guid *puuidSnapshot)
8988{
8989 AssertReturn(!isSessionMachine(), E_FAIL);
8990
8991 HRESULT rc = S_OK;
8992
8993 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8994 it != data.llStorageControllers.end();
8995 ++it)
8996 {
8997 const settings::StorageController &ctlData = *it;
8998
8999 ComObjPtr<StorageController> pCtl;
9000 /* Try to find one with the name first. */
9001 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9002 if (SUCCEEDED(rc))
9003 return setError(VBOX_E_OBJECT_IN_USE,
9004 tr("Storage controller named '%s' already exists"),
9005 ctlData.strName.c_str());
9006
9007 pCtl.createObject();
9008 rc = pCtl->init(this,
9009 ctlData.strName,
9010 ctlData.storageBus,
9011 ctlData.ulInstance,
9012 ctlData.fBootable);
9013 if (FAILED(rc)) return rc;
9014
9015 mStorageControllers->push_back(pCtl);
9016
9017 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9018 if (FAILED(rc)) return rc;
9019
9020 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9021 if (FAILED(rc)) return rc;
9022
9023 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9024 if (FAILED(rc)) return rc;
9025
9026 /* Set IDE emulation settings (only for AHCI controller). */
9027 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9028 {
9029 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9030 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9031 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9032 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9033 )
9034 return rc;
9035 }
9036
9037 /* Load the attached devices now. */
9038 rc = loadStorageDevices(pCtl,
9039 ctlData,
9040 puuidRegistry,
9041 puuidSnapshot);
9042 if (FAILED(rc)) return rc;
9043 }
9044
9045 return S_OK;
9046}
9047
9048/**
9049 * Called from loadStorageControllers for a controller's devices.
9050 *
9051 * @param aStorageController
9052 * @param data
9053 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9054 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9055 * @return
9056 */
9057HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9058 const settings::StorageController &data,
9059 const Guid *puuidRegistry,
9060 const Guid *puuidSnapshot)
9061{
9062 HRESULT rc = S_OK;
9063
9064 /* paranoia: detect duplicate attachments */
9065 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9066 it != data.llAttachedDevices.end();
9067 ++it)
9068 {
9069 const settings::AttachedDevice &ad = *it;
9070
9071 for (settings::AttachedDevicesList::const_iterator it2 = it;
9072 it2 != data.llAttachedDevices.end();
9073 ++it2)
9074 {
9075 if (it == it2)
9076 continue;
9077
9078 const settings::AttachedDevice &ad2 = *it2;
9079
9080 if ( ad.lPort == ad2.lPort
9081 && ad.lDevice == ad2.lDevice)
9082 {
9083 return setError(E_FAIL,
9084 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9085 aStorageController->getName().c_str(),
9086 ad.lPort,
9087 ad.lDevice,
9088 mUserData->s.strName.c_str());
9089 }
9090 }
9091 }
9092
9093 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9094 it != data.llAttachedDevices.end();
9095 ++it)
9096 {
9097 const settings::AttachedDevice &dev = *it;
9098 ComObjPtr<Medium> medium;
9099
9100 switch (dev.deviceType)
9101 {
9102 case DeviceType_Floppy:
9103 case DeviceType_DVD:
9104 if (dev.strHostDriveSrc.isNotEmpty())
9105 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9106 else
9107 rc = mParent->findRemoveableMedium(dev.deviceType,
9108 dev.uuid,
9109 false /* fRefresh */,
9110 false /* aSetError */,
9111 medium);
9112 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9113 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9114 rc = S_OK;
9115 break;
9116
9117 case DeviceType_HardDisk:
9118 {
9119 /* find a hard disk by UUID */
9120 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9121 if (FAILED(rc))
9122 {
9123 if (isSnapshotMachine())
9124 {
9125 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9126 // so the user knows that the bad disk is in a snapshot somewhere
9127 com::ErrorInfo info;
9128 return setError(E_FAIL,
9129 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9130 puuidSnapshot->raw(),
9131 info.getText().raw());
9132 }
9133 else
9134 return rc;
9135 }
9136
9137 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9138
9139 if (medium->getType() == MediumType_Immutable)
9140 {
9141 if (isSnapshotMachine())
9142 return setError(E_FAIL,
9143 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9144 "of the virtual machine '%s' ('%s')"),
9145 medium->getLocationFull().c_str(),
9146 dev.uuid.raw(),
9147 puuidSnapshot->raw(),
9148 mUserData->s.strName.c_str(),
9149 mData->m_strConfigFileFull.c_str());
9150
9151 return setError(E_FAIL,
9152 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9153 medium->getLocationFull().c_str(),
9154 dev.uuid.raw(),
9155 mUserData->s.strName.c_str(),
9156 mData->m_strConfigFileFull.c_str());
9157 }
9158
9159 if (medium->getType() == MediumType_MultiAttach)
9160 {
9161 if (isSnapshotMachine())
9162 return setError(E_FAIL,
9163 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9164 "of the virtual machine '%s' ('%s')"),
9165 medium->getLocationFull().c_str(),
9166 dev.uuid.raw(),
9167 puuidSnapshot->raw(),
9168 mUserData->s.strName.c_str(),
9169 mData->m_strConfigFileFull.c_str());
9170
9171 return setError(E_FAIL,
9172 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9173 medium->getLocationFull().c_str(),
9174 dev.uuid.raw(),
9175 mUserData->s.strName.c_str(),
9176 mData->m_strConfigFileFull.c_str());
9177 }
9178
9179 if ( !isSnapshotMachine()
9180 && medium->getChildren().size() != 0
9181 )
9182 return setError(E_FAIL,
9183 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9184 "because it has %d differencing child hard disks"),
9185 medium->getLocationFull().c_str(),
9186 dev.uuid.raw(),
9187 mUserData->s.strName.c_str(),
9188 mData->m_strConfigFileFull.c_str(),
9189 medium->getChildren().size());
9190
9191 if (findAttachment(mMediaData->mAttachments,
9192 medium))
9193 return setError(E_FAIL,
9194 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9195 medium->getLocationFull().c_str(),
9196 dev.uuid.raw(),
9197 mUserData->s.strName.c_str(),
9198 mData->m_strConfigFileFull.c_str());
9199
9200 break;
9201 }
9202
9203 default:
9204 return setError(E_FAIL,
9205 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9206 medium->getLocationFull().c_str(),
9207 mUserData->s.strName.c_str(),
9208 mData->m_strConfigFileFull.c_str());
9209 }
9210
9211 if (FAILED(rc))
9212 break;
9213
9214 /* Bandwidth groups are loaded at this point. */
9215 ComObjPtr<BandwidthGroup> pBwGroup;
9216
9217 if (!dev.strBwGroup.isEmpty())
9218 {
9219 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9220 if (FAILED(rc))
9221 return setError(E_FAIL,
9222 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9223 medium->getLocationFull().c_str(),
9224 dev.strBwGroup.c_str(),
9225 mUserData->s.strName.c_str(),
9226 mData->m_strConfigFileFull.c_str());
9227 pBwGroup->reference();
9228 }
9229
9230 const Bstr controllerName = aStorageController->getName();
9231 ComObjPtr<MediumAttachment> pAttachment;
9232 pAttachment.createObject();
9233 rc = pAttachment->init(this,
9234 medium,
9235 controllerName,
9236 dev.lPort,
9237 dev.lDevice,
9238 dev.deviceType,
9239 false,
9240 dev.fPassThrough,
9241 dev.fTempEject,
9242 dev.fNonRotational,
9243 dev.fDiscard,
9244 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9245 if (FAILED(rc)) break;
9246
9247 /* associate the medium with this machine and snapshot */
9248 if (!medium.isNull())
9249 {
9250 AutoCaller medCaller(medium);
9251 if (FAILED(medCaller.rc())) return medCaller.rc();
9252 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9253
9254 if (isSnapshotMachine())
9255 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9256 else
9257 rc = medium->addBackReference(mData->mUuid);
9258 /* If the medium->addBackReference fails it sets an appropriate
9259 * error message, so no need to do any guesswork here. */
9260
9261 if (puuidRegistry)
9262 // caller wants registry ID to be set on all attached media (OVF import case)
9263 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9264 }
9265
9266 if (FAILED(rc))
9267 break;
9268
9269 /* back up mMediaData to let registeredInit() properly rollback on failure
9270 * (= limited accessibility) */
9271 setModified(IsModified_Storage);
9272 mMediaData.backup();
9273 mMediaData->mAttachments.push_back(pAttachment);
9274 }
9275
9276 return rc;
9277}
9278
9279/**
9280 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9281 *
9282 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9283 * @param aSnapshot where to return the found snapshot
9284 * @param aSetError true to set extended error info on failure
9285 */
9286HRESULT Machine::findSnapshotById(const Guid &aId,
9287 ComObjPtr<Snapshot> &aSnapshot,
9288 bool aSetError /* = false */)
9289{
9290 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9291
9292 if (!mData->mFirstSnapshot)
9293 {
9294 if (aSetError)
9295 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9296 return E_FAIL;
9297 }
9298
9299 if (aId.isZero())
9300 aSnapshot = mData->mFirstSnapshot;
9301 else
9302 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9303
9304 if (!aSnapshot)
9305 {
9306 if (aSetError)
9307 return setError(E_FAIL,
9308 tr("Could not find a snapshot with UUID {%s}"),
9309 aId.toString().c_str());
9310 return E_FAIL;
9311 }
9312
9313 return S_OK;
9314}
9315
9316/**
9317 * Returns the snapshot with the given name or fails of no such snapshot.
9318 *
9319 * @param aName snapshot name to find
9320 * @param aSnapshot where to return the found snapshot
9321 * @param aSetError true to set extended error info on failure
9322 */
9323HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9324 ComObjPtr<Snapshot> &aSnapshot,
9325 bool aSetError /* = false */)
9326{
9327 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9328
9329 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9330
9331 if (!mData->mFirstSnapshot)
9332 {
9333 if (aSetError)
9334 return setError(VBOX_E_OBJECT_NOT_FOUND,
9335 tr("This machine does not have any snapshots"));
9336 return VBOX_E_OBJECT_NOT_FOUND;
9337 }
9338
9339 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9340
9341 if (!aSnapshot)
9342 {
9343 if (aSetError)
9344 return setError(VBOX_E_OBJECT_NOT_FOUND,
9345 tr("Could not find a snapshot named '%s'"), strName.c_str());
9346 return VBOX_E_OBJECT_NOT_FOUND;
9347 }
9348
9349 return S_OK;
9350}
9351
9352/**
9353 * Returns a storage controller object with the given name.
9354 *
9355 * @param aName storage controller name to find
9356 * @param aStorageController where to return the found storage controller
9357 * @param aSetError true to set extended error info on failure
9358 */
9359HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9360 ComObjPtr<StorageController> &aStorageController,
9361 bool aSetError /* = false */)
9362{
9363 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9364
9365 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9366 it != mStorageControllers->end();
9367 ++it)
9368 {
9369 if ((*it)->getName() == aName)
9370 {
9371 aStorageController = (*it);
9372 return S_OK;
9373 }
9374 }
9375
9376 if (aSetError)
9377 return setError(VBOX_E_OBJECT_NOT_FOUND,
9378 tr("Could not find a storage controller named '%s'"),
9379 aName.c_str());
9380 return VBOX_E_OBJECT_NOT_FOUND;
9381}
9382
9383HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9384 MediaData::AttachmentList &atts)
9385{
9386 AutoCaller autoCaller(this);
9387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9388
9389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9390
9391 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9392 it != mMediaData->mAttachments.end();
9393 ++it)
9394 {
9395 const ComObjPtr<MediumAttachment> &pAtt = *it;
9396
9397 // should never happen, but deal with NULL pointers in the list.
9398 AssertStmt(!pAtt.isNull(), continue);
9399
9400 // getControllerName() needs caller+read lock
9401 AutoCaller autoAttCaller(pAtt);
9402 if (FAILED(autoAttCaller.rc()))
9403 {
9404 atts.clear();
9405 return autoAttCaller.rc();
9406 }
9407 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9408
9409 if (pAtt->getControllerName() == aName)
9410 atts.push_back(pAtt);
9411 }
9412
9413 return S_OK;
9414}
9415
9416/**
9417 * Helper for #saveSettings. Cares about renaming the settings directory and
9418 * file if the machine name was changed and about creating a new settings file
9419 * if this is a new machine.
9420 *
9421 * @note Must be never called directly but only from #saveSettings().
9422 */
9423HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9424{
9425 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9426
9427 HRESULT rc = S_OK;
9428
9429 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9430
9431 /// @todo need to handle primary group change, too
9432
9433 /* attempt to rename the settings file if machine name is changed */
9434 if ( mUserData->s.fNameSync
9435 && mUserData.isBackedUp()
9436 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9437 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9438 )
9439 {
9440 bool dirRenamed = false;
9441 bool fileRenamed = false;
9442
9443 Utf8Str configFile, newConfigFile;
9444 Utf8Str configFilePrev, newConfigFilePrev;
9445 Utf8Str configDir, newConfigDir;
9446
9447 do
9448 {
9449 int vrc = VINF_SUCCESS;
9450
9451 Utf8Str name = mUserData.backedUpData()->s.strName;
9452 Utf8Str newName = mUserData->s.strName;
9453 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9454 if (group == "/")
9455 group.setNull();
9456 Utf8Str newGroup = mUserData->s.llGroups.front();
9457 if (newGroup == "/")
9458 newGroup.setNull();
9459
9460 configFile = mData->m_strConfigFileFull;
9461
9462 /* first, rename the directory if it matches the group and machine name */
9463 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9464 group.c_str(), RTPATH_DELIMITER, name.c_str());
9465 /** @todo hack, make somehow use of ComposeMachineFilename */
9466 if (mUserData->s.fDirectoryIncludesUUID)
9467 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9468 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9469 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9470 /** @todo hack, make somehow use of ComposeMachineFilename */
9471 if (mUserData->s.fDirectoryIncludesUUID)
9472 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9473 configDir = configFile;
9474 configDir.stripFilename();
9475 newConfigDir = configDir;
9476 if ( configDir.length() >= groupPlusName.length()
9477 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9478 {
9479 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9480 Utf8Str newConfigBaseDir(newConfigDir);
9481 newConfigDir.append(newGroupPlusName);
9482 /* consistency: use \ if appropriate on the platform */
9483 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9484 /* new dir and old dir cannot be equal here because of 'if'
9485 * above and because name != newName */
9486 Assert(configDir != newConfigDir);
9487 if (!fSettingsFileIsNew)
9488 {
9489 /* perform real rename only if the machine is not new */
9490 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9491 if ( vrc == VERR_FILE_NOT_FOUND
9492 || vrc == VERR_PATH_NOT_FOUND)
9493 {
9494 /* create the parent directory, then retry renaming */
9495 Utf8Str parent(newConfigDir);
9496 parent.stripFilename();
9497 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9498 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9499 }
9500 if (RT_FAILURE(vrc))
9501 {
9502 rc = setError(E_FAIL,
9503 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9504 configDir.c_str(),
9505 newConfigDir.c_str(),
9506 vrc);
9507 break;
9508 }
9509 /* delete subdirectories which are no longer needed */
9510 Utf8Str dir(configDir);
9511 dir.stripFilename();
9512 while (dir != newConfigBaseDir && dir != ".")
9513 {
9514 vrc = RTDirRemove(dir.c_str());
9515 if (RT_FAILURE(vrc))
9516 break;
9517 dir.stripFilename();
9518 }
9519 dirRenamed = true;
9520 }
9521 }
9522
9523 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9524 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9525
9526 /* then try to rename the settings file itself */
9527 if (newConfigFile != configFile)
9528 {
9529 /* get the path to old settings file in renamed directory */
9530 configFile = Utf8StrFmt("%s%c%s",
9531 newConfigDir.c_str(),
9532 RTPATH_DELIMITER,
9533 RTPathFilename(configFile.c_str()));
9534 if (!fSettingsFileIsNew)
9535 {
9536 /* perform real rename only if the machine is not new */
9537 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9538 if (RT_FAILURE(vrc))
9539 {
9540 rc = setError(E_FAIL,
9541 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9542 configFile.c_str(),
9543 newConfigFile.c_str(),
9544 vrc);
9545 break;
9546 }
9547 fileRenamed = true;
9548 configFilePrev = configFile;
9549 configFilePrev += "-prev";
9550 newConfigFilePrev = newConfigFile;
9551 newConfigFilePrev += "-prev";
9552 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9553 }
9554 }
9555
9556 // update m_strConfigFileFull amd mConfigFile
9557 mData->m_strConfigFileFull = newConfigFile;
9558 // compute the relative path too
9559 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9560
9561 // store the old and new so that VirtualBox::saveSettings() can update
9562 // the media registry
9563 if ( mData->mRegistered
9564 && configDir != newConfigDir)
9565 {
9566 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9567
9568 if (pfNeedsGlobalSaveSettings)
9569 *pfNeedsGlobalSaveSettings = true;
9570 }
9571
9572 // in the saved state file path, replace the old directory with the new directory
9573 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9574 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9575
9576 // and do the same thing for the saved state file paths of all the online snapshots
9577 if (mData->mFirstSnapshot)
9578 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9579 newConfigDir.c_str());
9580 }
9581 while (0);
9582
9583 if (FAILED(rc))
9584 {
9585 /* silently try to rename everything back */
9586 if (fileRenamed)
9587 {
9588 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9589 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9590 }
9591 if (dirRenamed)
9592 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9593 }
9594
9595 if (FAILED(rc)) return rc;
9596 }
9597
9598 if (fSettingsFileIsNew)
9599 {
9600 /* create a virgin config file */
9601 int vrc = VINF_SUCCESS;
9602
9603 /* ensure the settings directory exists */
9604 Utf8Str path(mData->m_strConfigFileFull);
9605 path.stripFilename();
9606 if (!RTDirExists(path.c_str()))
9607 {
9608 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9609 if (RT_FAILURE(vrc))
9610 {
9611 return setError(E_FAIL,
9612 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9613 path.c_str(),
9614 vrc);
9615 }
9616 }
9617
9618 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9619 path = Utf8Str(mData->m_strConfigFileFull);
9620 RTFILE f = NIL_RTFILE;
9621 vrc = RTFileOpen(&f, path.c_str(),
9622 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9623 if (RT_FAILURE(vrc))
9624 return setError(E_FAIL,
9625 tr("Could not create the settings file '%s' (%Rrc)"),
9626 path.c_str(),
9627 vrc);
9628 RTFileClose(f);
9629 }
9630
9631 return rc;
9632}
9633
9634/**
9635 * Saves and commits machine data, user data and hardware data.
9636 *
9637 * Note that on failure, the data remains uncommitted.
9638 *
9639 * @a aFlags may combine the following flags:
9640 *
9641 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9642 * Used when saving settings after an operation that makes them 100%
9643 * correspond to the settings from the current snapshot.
9644 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9645 * #isReallyModified() returns false. This is necessary for cases when we
9646 * change machine data directly, not through the backup()/commit() mechanism.
9647 * - SaveS_Force: settings will be saved without doing a deep compare of the
9648 * settings structures. This is used when this is called because snapshots
9649 * have changed to avoid the overhead of the deep compare.
9650 *
9651 * @note Must be called from under this object's write lock. Locks children for
9652 * writing.
9653 *
9654 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9655 * initialized to false and that will be set to true by this function if
9656 * the caller must invoke VirtualBox::saveSettings() because the global
9657 * settings have changed. This will happen if a machine rename has been
9658 * saved and the global machine and media registries will therefore need
9659 * updating.
9660 */
9661HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9662 int aFlags /*= 0*/)
9663{
9664 LogFlowThisFuncEnter();
9665
9666 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9667
9668 /* make sure child objects are unable to modify the settings while we are
9669 * saving them */
9670 ensureNoStateDependencies();
9671
9672 AssertReturn(!isSnapshotMachine(),
9673 E_FAIL);
9674
9675 HRESULT rc = S_OK;
9676 bool fNeedsWrite = false;
9677
9678 /* First, prepare to save settings. It will care about renaming the
9679 * settings directory and file if the machine name was changed and about
9680 * creating a new settings file if this is a new machine. */
9681 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9682 if (FAILED(rc)) return rc;
9683
9684 // keep a pointer to the current settings structures
9685 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9686 settings::MachineConfigFile *pNewConfig = NULL;
9687
9688 try
9689 {
9690 // make a fresh one to have everyone write stuff into
9691 pNewConfig = new settings::MachineConfigFile(NULL);
9692 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9693
9694 // now go and copy all the settings data from COM to the settings structures
9695 // (this calles saveSettings() on all the COM objects in the machine)
9696 copyMachineDataToSettings(*pNewConfig);
9697
9698 if (aFlags & SaveS_ResetCurStateModified)
9699 {
9700 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9701 mData->mCurrentStateModified = FALSE;
9702 fNeedsWrite = true; // always, no need to compare
9703 }
9704 else if (aFlags & SaveS_Force)
9705 {
9706 fNeedsWrite = true; // always, no need to compare
9707 }
9708 else
9709 {
9710 if (!mData->mCurrentStateModified)
9711 {
9712 // do a deep compare of the settings that we just saved with the settings
9713 // previously stored in the config file; this invokes MachineConfigFile::operator==
9714 // which does a deep compare of all the settings, which is expensive but less expensive
9715 // than writing out XML in vain
9716 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9717
9718 // could still be modified if any settings changed
9719 mData->mCurrentStateModified = fAnySettingsChanged;
9720
9721 fNeedsWrite = fAnySettingsChanged;
9722 }
9723 else
9724 fNeedsWrite = true;
9725 }
9726
9727 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9728
9729 if (fNeedsWrite)
9730 // now spit it all out!
9731 pNewConfig->write(mData->m_strConfigFileFull);
9732
9733 mData->pMachineConfigFile = pNewConfig;
9734 delete pOldConfig;
9735 commit();
9736
9737 // after saving settings, we are no longer different from the XML on disk
9738 mData->flModifications = 0;
9739 }
9740 catch (HRESULT err)
9741 {
9742 // we assume that error info is set by the thrower
9743 rc = err;
9744
9745 // restore old config
9746 delete pNewConfig;
9747 mData->pMachineConfigFile = pOldConfig;
9748 }
9749 catch (...)
9750 {
9751 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9752 }
9753
9754 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9755 {
9756 /* Fire the data change event, even on failure (since we've already
9757 * committed all data). This is done only for SessionMachines because
9758 * mutable Machine instances are always not registered (i.e. private
9759 * to the client process that creates them) and thus don't need to
9760 * inform callbacks. */
9761 if (isSessionMachine())
9762 mParent->onMachineDataChange(mData->mUuid);
9763 }
9764
9765 LogFlowThisFunc(("rc=%08X\n", rc));
9766 LogFlowThisFuncLeave();
9767 return rc;
9768}
9769
9770/**
9771 * Implementation for saving the machine settings into the given
9772 * settings::MachineConfigFile instance. This copies machine extradata
9773 * from the previous machine config file in the instance data, if any.
9774 *
9775 * This gets called from two locations:
9776 *
9777 * -- Machine::saveSettings(), during the regular XML writing;
9778 *
9779 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9780 * exported to OVF and we write the VirtualBox proprietary XML
9781 * into a <vbox:Machine> tag.
9782 *
9783 * This routine fills all the fields in there, including snapshots, *except*
9784 * for the following:
9785 *
9786 * -- fCurrentStateModified. There is some special logic associated with that.
9787 *
9788 * The caller can then call MachineConfigFile::write() or do something else
9789 * with it.
9790 *
9791 * Caller must hold the machine lock!
9792 *
9793 * This throws XML errors and HRESULT, so the caller must have a catch block!
9794 */
9795void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9796{
9797 // deep copy extradata
9798 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9799
9800 config.uuid = mData->mUuid;
9801
9802 // copy name, description, OS type, teleport, UTC etc.
9803 config.machineUserData = mUserData->s;
9804
9805 if ( mData->mMachineState == MachineState_Saved
9806 || mData->mMachineState == MachineState_Restoring
9807 // when deleting a snapshot we may or may not have a saved state in the current state,
9808 // so let's not assert here please
9809 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9810 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9811 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9812 && (!mSSData->strStateFilePath.isEmpty())
9813 )
9814 )
9815 {
9816 Assert(!mSSData->strStateFilePath.isEmpty());
9817 /* try to make the file name relative to the settings file dir */
9818 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9819 }
9820 else
9821 {
9822 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9823 config.strStateFile.setNull();
9824 }
9825
9826 if (mData->mCurrentSnapshot)
9827 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9828 else
9829 config.uuidCurrentSnapshot.clear();
9830
9831 config.timeLastStateChange = mData->mLastStateChange;
9832 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9833 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9834
9835 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9836 if (FAILED(rc)) throw rc;
9837
9838 rc = saveStorageControllers(config.storageMachine);
9839 if (FAILED(rc)) throw rc;
9840
9841 // save machine's media registry if this is VirtualBox 4.0 or later
9842 if (config.canHaveOwnMediaRegistry())
9843 {
9844 // determine machine folder
9845 Utf8Str strMachineFolder = getSettingsFileFull();
9846 strMachineFolder.stripFilename();
9847 mParent->saveMediaRegistry(config.mediaRegistry,
9848 getId(), // only media with registry ID == machine UUID
9849 strMachineFolder);
9850 // this throws HRESULT
9851 }
9852
9853 // save snapshots
9854 rc = saveAllSnapshots(config);
9855 if (FAILED(rc)) throw rc;
9856}
9857
9858/**
9859 * Saves all snapshots of the machine into the given machine config file. Called
9860 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9861 * @param config
9862 * @return
9863 */
9864HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9865{
9866 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9867
9868 HRESULT rc = S_OK;
9869
9870 try
9871 {
9872 config.llFirstSnapshot.clear();
9873
9874 if (mData->mFirstSnapshot)
9875 {
9876 settings::Snapshot snapNew;
9877 config.llFirstSnapshot.push_back(snapNew);
9878
9879 // get reference to the fresh copy of the snapshot on the list and
9880 // work on that copy directly to avoid excessive copying later
9881 settings::Snapshot &snap = config.llFirstSnapshot.front();
9882
9883 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9884 if (FAILED(rc)) throw rc;
9885 }
9886
9887// if (mType == IsSessionMachine)
9888// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9889
9890 }
9891 catch (HRESULT err)
9892 {
9893 /* we assume that error info is set by the thrower */
9894 rc = err;
9895 }
9896 catch (...)
9897 {
9898 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9899 }
9900
9901 return rc;
9902}
9903
9904/**
9905 * Saves the VM hardware configuration. It is assumed that the
9906 * given node is empty.
9907 *
9908 * @param data Reference to the settings object for the hardware config.
9909 * @param pDbg Pointer to the settings object for the debugging config
9910 * which happens to live in mHWData.
9911 * @param pAutostart Pointer to the settings object for the autostart config
9912 * which happens to live in mHWData.
9913 */
9914HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9915 settings::Autostart *pAutostart)
9916{
9917 HRESULT rc = S_OK;
9918
9919 try
9920 {
9921 /* The hardware version attribute (optional).
9922 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9923 if ( mHWData->mHWVersion == "1"
9924 && mSSData->strStateFilePath.isEmpty()
9925 )
9926 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. */
9927
9928 data.strVersion = mHWData->mHWVersion;
9929 data.uuid = mHWData->mHardwareUUID;
9930
9931 // CPU
9932 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9933 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9934 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9935 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9936 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9937 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9938 data.fPAE = !!mHWData->mPAEEnabled;
9939 data.enmLongMode = mHWData->mLongMode;
9940 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9941
9942 /* Standard and Extended CPUID leafs. */
9943 data.llCpuIdLeafs.clear();
9944 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9945 {
9946 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9947 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9948 }
9949 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9950 {
9951 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9952 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9953 }
9954
9955 data.cCPUs = mHWData->mCPUCount;
9956 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9957 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9958
9959 data.llCpus.clear();
9960 if (data.fCpuHotPlug)
9961 {
9962 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9963 {
9964 if (mHWData->mCPUAttached[idx])
9965 {
9966 settings::Cpu cpu;
9967 cpu.ulId = idx;
9968 data.llCpus.push_back(cpu);
9969 }
9970 }
9971 }
9972
9973 // memory
9974 data.ulMemorySizeMB = mHWData->mMemorySize;
9975 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9976
9977 // firmware
9978 data.firmwareType = mHWData->mFirmwareType;
9979
9980 // HID
9981 data.pointingHIDType = mHWData->mPointingHIDType;
9982 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9983
9984 // chipset
9985 data.chipsetType = mHWData->mChipsetType;
9986
9987 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
9988 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9989
9990 // HPET
9991 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9992
9993 // boot order
9994 data.mapBootOrder.clear();
9995 for (size_t i = 0;
9996 i < RT_ELEMENTS(mHWData->mBootOrder);
9997 ++i)
9998 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9999
10000 // display
10001 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10002 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10003 data.cMonitors = mHWData->mMonitorCount;
10004 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10005 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10006 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10007 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10008 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10009 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10010 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
10011
10012 /* VRDEServer settings (optional) */
10013 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10014 if (FAILED(rc)) throw rc;
10015
10016 /* BIOS (required) */
10017 rc = mBIOSSettings->saveSettings(data.biosSettings);
10018 if (FAILED(rc)) throw rc;
10019
10020 /* USB Controller (required) */
10021 rc = mUSBController->saveSettings(data.usbController);
10022 if (FAILED(rc)) throw rc;
10023
10024 /* Network adapters (required) */
10025 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10026 data.llNetworkAdapters.clear();
10027 /* Write out only the nominal number of network adapters for this
10028 * chipset type. Since Machine::commit() hasn't been called there
10029 * may be extra NIC settings in the vector. */
10030 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10031 {
10032 settings::NetworkAdapter nic;
10033 nic.ulSlot = slot;
10034 /* paranoia check... must not be NULL, but must not crash either. */
10035 if (mNetworkAdapters[slot])
10036 {
10037 rc = mNetworkAdapters[slot]->saveSettings(nic);
10038 if (FAILED(rc)) throw rc;
10039
10040 data.llNetworkAdapters.push_back(nic);
10041 }
10042 }
10043
10044 /* Serial ports */
10045 data.llSerialPorts.clear();
10046 for (ULONG slot = 0;
10047 slot < RT_ELEMENTS(mSerialPorts);
10048 ++slot)
10049 {
10050 settings::SerialPort s;
10051 s.ulSlot = slot;
10052 rc = mSerialPorts[slot]->saveSettings(s);
10053 if (FAILED(rc)) return rc;
10054
10055 data.llSerialPorts.push_back(s);
10056 }
10057
10058 /* Parallel ports */
10059 data.llParallelPorts.clear();
10060 for (ULONG slot = 0;
10061 slot < RT_ELEMENTS(mParallelPorts);
10062 ++slot)
10063 {
10064 settings::ParallelPort p;
10065 p.ulSlot = slot;
10066 rc = mParallelPorts[slot]->saveSettings(p);
10067 if (FAILED(rc)) return rc;
10068
10069 data.llParallelPorts.push_back(p);
10070 }
10071
10072 /* Audio adapter */
10073 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10074 if (FAILED(rc)) return rc;
10075
10076 /* Shared folders */
10077 data.llSharedFolders.clear();
10078 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10079 it != mHWData->mSharedFolders.end();
10080 ++it)
10081 {
10082 SharedFolder *pSF = *it;
10083 AutoCaller sfCaller(pSF);
10084 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10085 settings::SharedFolder sf;
10086 sf.strName = pSF->getName();
10087 sf.strHostPath = pSF->getHostPath();
10088 sf.fWritable = !!pSF->isWritable();
10089 sf.fAutoMount = !!pSF->isAutoMounted();
10090
10091 data.llSharedFolders.push_back(sf);
10092 }
10093
10094 // clipboard
10095 data.clipboardMode = mHWData->mClipboardMode;
10096
10097 // drag'n'drop
10098 data.dragAndDropMode = mHWData->mDragAndDropMode;
10099
10100 /* Guest */
10101 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10102
10103 // IO settings
10104 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10105 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10106
10107 /* BandwidthControl (required) */
10108 rc = mBandwidthControl->saveSettings(data.ioSettings);
10109 if (FAILED(rc)) throw rc;
10110
10111 /* Host PCI devices */
10112 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10113 it != mHWData->mPCIDeviceAssignments.end();
10114 ++it)
10115 {
10116 ComObjPtr<PCIDeviceAttachment> pda = *it;
10117 settings::HostPCIDeviceAttachment hpda;
10118
10119 rc = pda->saveSettings(hpda);
10120 if (FAILED(rc)) throw rc;
10121
10122 data.pciAttachments.push_back(hpda);
10123 }
10124
10125
10126 // guest properties
10127 data.llGuestProperties.clear();
10128#ifdef VBOX_WITH_GUEST_PROPS
10129 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10130 it != mHWData->mGuestProperties.end();
10131 ++it)
10132 {
10133 HWData::GuestProperty property = it->second;
10134
10135 /* Remove transient guest properties at shutdown unless we
10136 * are saving state */
10137 if ( ( mData->mMachineState == MachineState_PoweredOff
10138 || mData->mMachineState == MachineState_Aborted
10139 || mData->mMachineState == MachineState_Teleported)
10140 && ( property.mFlags & guestProp::TRANSIENT
10141 || property.mFlags & guestProp::TRANSRESET))
10142 continue;
10143 settings::GuestProperty prop;
10144 prop.strName = it->first;
10145 prop.strValue = property.strValue;
10146 prop.timestamp = property.mTimestamp;
10147 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10148 guestProp::writeFlags(property.mFlags, szFlags);
10149 prop.strFlags = szFlags;
10150
10151 data.llGuestProperties.push_back(prop);
10152 }
10153
10154 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10155 /* I presume this doesn't require a backup(). */
10156 mData->mGuestPropertiesModified = FALSE;
10157#endif /* VBOX_WITH_GUEST_PROPS defined */
10158
10159 *pDbg = mHWData->mDebugging;
10160 *pAutostart = mHWData->mAutostart;
10161
10162 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10163 }
10164 catch(std::bad_alloc &)
10165 {
10166 return E_OUTOFMEMORY;
10167 }
10168
10169 AssertComRC(rc);
10170 return rc;
10171}
10172
10173/**
10174 * Saves the storage controller configuration.
10175 *
10176 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10177 */
10178HRESULT Machine::saveStorageControllers(settings::Storage &data)
10179{
10180 data.llStorageControllers.clear();
10181
10182 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10183 it != mStorageControllers->end();
10184 ++it)
10185 {
10186 HRESULT rc;
10187 ComObjPtr<StorageController> pCtl = *it;
10188
10189 settings::StorageController ctl;
10190 ctl.strName = pCtl->getName();
10191 ctl.controllerType = pCtl->getControllerType();
10192 ctl.storageBus = pCtl->getStorageBus();
10193 ctl.ulInstance = pCtl->getInstance();
10194 ctl.fBootable = pCtl->getBootable();
10195
10196 /* Save the port count. */
10197 ULONG portCount;
10198 rc = pCtl->COMGETTER(PortCount)(&portCount);
10199 ComAssertComRCRet(rc, rc);
10200 ctl.ulPortCount = portCount;
10201
10202 /* Save fUseHostIOCache */
10203 BOOL fUseHostIOCache;
10204 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10205 ComAssertComRCRet(rc, rc);
10206 ctl.fUseHostIOCache = !!fUseHostIOCache;
10207
10208 /* Save IDE emulation settings. */
10209 if (ctl.controllerType == StorageControllerType_IntelAhci)
10210 {
10211 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10212 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10213 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10214 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10215 )
10216 ComAssertComRCRet(rc, rc);
10217 }
10218
10219 /* save the devices now. */
10220 rc = saveStorageDevices(pCtl, ctl);
10221 ComAssertComRCRet(rc, rc);
10222
10223 data.llStorageControllers.push_back(ctl);
10224 }
10225
10226 return S_OK;
10227}
10228
10229/**
10230 * Saves the hard disk configuration.
10231 */
10232HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10233 settings::StorageController &data)
10234{
10235 MediaData::AttachmentList atts;
10236
10237 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10238 if (FAILED(rc)) return rc;
10239
10240 data.llAttachedDevices.clear();
10241 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10242 it != atts.end();
10243 ++it)
10244 {
10245 settings::AttachedDevice dev;
10246
10247 MediumAttachment *pAttach = *it;
10248 Medium *pMedium = pAttach->getMedium();
10249
10250 dev.deviceType = pAttach->getType();
10251 dev.lPort = pAttach->getPort();
10252 dev.lDevice = pAttach->getDevice();
10253 if (pMedium)
10254 {
10255 if (pMedium->isHostDrive())
10256 dev.strHostDriveSrc = pMedium->getLocationFull();
10257 else
10258 dev.uuid = pMedium->getId();
10259 dev.fPassThrough = pAttach->getPassthrough();
10260 dev.fTempEject = pAttach->getTempEject();
10261 dev.fNonRotational = pAttach->getNonRotational();
10262 dev.fDiscard = pAttach->getDiscard();
10263 }
10264
10265 dev.strBwGroup = pAttach->getBandwidthGroup();
10266
10267 data.llAttachedDevices.push_back(dev);
10268 }
10269
10270 return S_OK;
10271}
10272
10273/**
10274 * Saves machine state settings as defined by aFlags
10275 * (SaveSTS_* values).
10276 *
10277 * @param aFlags Combination of SaveSTS_* flags.
10278 *
10279 * @note Locks objects for writing.
10280 */
10281HRESULT Machine::saveStateSettings(int aFlags)
10282{
10283 if (aFlags == 0)
10284 return S_OK;
10285
10286 AutoCaller autoCaller(this);
10287 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10288
10289 /* This object's write lock is also necessary to serialize file access
10290 * (prevent concurrent reads and writes) */
10291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10292
10293 HRESULT rc = S_OK;
10294
10295 Assert(mData->pMachineConfigFile);
10296
10297 try
10298 {
10299 if (aFlags & SaveSTS_CurStateModified)
10300 mData->pMachineConfigFile->fCurrentStateModified = true;
10301
10302 if (aFlags & SaveSTS_StateFilePath)
10303 {
10304 if (!mSSData->strStateFilePath.isEmpty())
10305 /* try to make the file name relative to the settings file dir */
10306 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10307 else
10308 mData->pMachineConfigFile->strStateFile.setNull();
10309 }
10310
10311 if (aFlags & SaveSTS_StateTimeStamp)
10312 {
10313 Assert( mData->mMachineState != MachineState_Aborted
10314 || mSSData->strStateFilePath.isEmpty());
10315
10316 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10317
10318 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10319//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10320 }
10321
10322 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10323 }
10324 catch (...)
10325 {
10326 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10327 }
10328
10329 return rc;
10330}
10331
10332/**
10333 * Ensures that the given medium is added to a media registry. If this machine
10334 * was created with 4.0 or later, then the machine registry is used. Otherwise
10335 * the global VirtualBox media registry is used.
10336 *
10337 * Caller must NOT hold machine lock, media tree or any medium locks!
10338 *
10339 * @param pMedium
10340 */
10341void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10342{
10343 /* Paranoia checks: do not hold machine or media tree locks. */
10344 AssertReturnVoid(!isWriteLockOnCurrentThread());
10345 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10346
10347 ComObjPtr<Medium> pBase;
10348 {
10349 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10350 pBase = pMedium->getBase();
10351 }
10352
10353 /* Paranoia checks: do not hold medium locks. */
10354 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10355 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10356
10357 // decide which medium registry to use now that the medium is attached:
10358 Guid uuid;
10359 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10360 // machine XML is VirtualBox 4.0 or higher:
10361 uuid = getId(); // machine UUID
10362 else
10363 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10364
10365 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10366 mParent->markRegistryModified(uuid);
10367
10368 /* For more complex hard disk structures it can happen that the base
10369 * medium isn't yet associated with any medium registry. Do that now. */
10370 if (pMedium != pBase)
10371 {
10372 if (pBase->addRegistry(uuid, true /* fRecurse */))
10373 mParent->markRegistryModified(uuid);
10374 }
10375}
10376
10377/**
10378 * Creates differencing hard disks for all normal hard disks attached to this
10379 * machine and a new set of attachments to refer to created disks.
10380 *
10381 * Used when taking a snapshot or when deleting the current state. Gets called
10382 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10383 *
10384 * This method assumes that mMediaData contains the original hard disk attachments
10385 * it needs to create diffs for. On success, these attachments will be replaced
10386 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10387 * called to delete created diffs which will also rollback mMediaData and restore
10388 * whatever was backed up before calling this method.
10389 *
10390 * Attachments with non-normal hard disks are left as is.
10391 *
10392 * If @a aOnline is @c false then the original hard disks that require implicit
10393 * diffs will be locked for reading. Otherwise it is assumed that they are
10394 * already locked for writing (when the VM was started). Note that in the latter
10395 * case it is responsibility of the caller to lock the newly created diffs for
10396 * writing if this method succeeds.
10397 *
10398 * @param aProgress Progress object to run (must contain at least as
10399 * many operations left as the number of hard disks
10400 * attached).
10401 * @param aOnline Whether the VM was online prior to this operation.
10402 *
10403 * @note The progress object is not marked as completed, neither on success nor
10404 * on failure. This is a responsibility of the caller.
10405 *
10406 * @note Locks this object and the media tree for writing.
10407 */
10408HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10409 ULONG aWeight,
10410 bool aOnline)
10411{
10412 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10413
10414 AutoCaller autoCaller(this);
10415 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10416
10417 AutoMultiWriteLock2 alock(this->lockHandle(),
10418 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10419
10420 /* must be in a protective state because we release the lock below */
10421 AssertReturn( mData->mMachineState == MachineState_Saving
10422 || mData->mMachineState == MachineState_LiveSnapshotting
10423 || mData->mMachineState == MachineState_RestoringSnapshot
10424 || mData->mMachineState == MachineState_DeletingSnapshot
10425 , E_FAIL);
10426
10427 HRESULT rc = S_OK;
10428
10429 // use appropriate locked media map (online or offline)
10430 MediumLockListMap lockedMediaOffline;
10431 MediumLockListMap *lockedMediaMap;
10432 if (aOnline)
10433 lockedMediaMap = &mData->mSession.mLockedMedia;
10434 else
10435 lockedMediaMap = &lockedMediaOffline;
10436
10437 try
10438 {
10439 if (!aOnline)
10440 {
10441 /* lock all attached hard disks early to detect "in use"
10442 * situations before creating actual diffs */
10443 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10444 it != mMediaData->mAttachments.end();
10445 ++it)
10446 {
10447 MediumAttachment* pAtt = *it;
10448 if (pAtt->getType() == DeviceType_HardDisk)
10449 {
10450 Medium* pMedium = pAtt->getMedium();
10451 Assert(pMedium);
10452
10453 MediumLockList *pMediumLockList(new MediumLockList());
10454 alock.release();
10455 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10456 false /* fMediumLockWrite */,
10457 NULL,
10458 *pMediumLockList);
10459 alock.acquire();
10460 if (FAILED(rc))
10461 {
10462 delete pMediumLockList;
10463 throw rc;
10464 }
10465 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10466 if (FAILED(rc))
10467 {
10468 throw setError(rc,
10469 tr("Collecting locking information for all attached media failed"));
10470 }
10471 }
10472 }
10473
10474 /* Now lock all media. If this fails, nothing is locked. */
10475 alock.release();
10476 rc = lockedMediaMap->Lock();
10477 alock.acquire();
10478 if (FAILED(rc))
10479 {
10480 throw setError(rc,
10481 tr("Locking of attached media failed"));
10482 }
10483 }
10484
10485 /* remember the current list (note that we don't use backup() since
10486 * mMediaData may be already backed up) */
10487 MediaData::AttachmentList atts = mMediaData->mAttachments;
10488
10489 /* start from scratch */
10490 mMediaData->mAttachments.clear();
10491
10492 /* go through remembered attachments and create diffs for normal hard
10493 * disks and attach them */
10494 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10495 it != atts.end();
10496 ++it)
10497 {
10498 MediumAttachment* pAtt = *it;
10499
10500 DeviceType_T devType = pAtt->getType();
10501 Medium* pMedium = pAtt->getMedium();
10502
10503 if ( devType != DeviceType_HardDisk
10504 || pMedium == NULL
10505 || pMedium->getType() != MediumType_Normal)
10506 {
10507 /* copy the attachment as is */
10508
10509 /** @todo the progress object created in Console::TakeSnaphot
10510 * only expects operations for hard disks. Later other
10511 * device types need to show up in the progress as well. */
10512 if (devType == DeviceType_HardDisk)
10513 {
10514 if (pMedium == NULL)
10515 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10516 aWeight); // weight
10517 else
10518 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10519 pMedium->getBase()->getName().c_str()).raw(),
10520 aWeight); // weight
10521 }
10522
10523 mMediaData->mAttachments.push_back(pAtt);
10524 continue;
10525 }
10526
10527 /* need a diff */
10528 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10529 pMedium->getBase()->getName().c_str()).raw(),
10530 aWeight); // weight
10531
10532 Utf8Str strFullSnapshotFolder;
10533 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10534
10535 ComObjPtr<Medium> diff;
10536 diff.createObject();
10537 // store the diff in the same registry as the parent
10538 // (this cannot fail here because we can't create implicit diffs for
10539 // unregistered images)
10540 Guid uuidRegistryParent;
10541 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10542 Assert(fInRegistry); NOREF(fInRegistry);
10543 rc = diff->init(mParent,
10544 pMedium->getPreferredDiffFormat(),
10545 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10546 uuidRegistryParent);
10547 if (FAILED(rc)) throw rc;
10548
10549 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10550 * the push_back? Looks like we're going to release medium with the
10551 * wrong kind of lock (general issue with if we fail anywhere at all)
10552 * and an orphaned VDI in the snapshots folder. */
10553
10554 /* update the appropriate lock list */
10555 MediumLockList *pMediumLockList;
10556 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10557 AssertComRCThrowRC(rc);
10558 if (aOnline)
10559 {
10560 alock.release();
10561 /* The currently attached medium will be read-only, change
10562 * the lock type to read. */
10563 rc = pMediumLockList->Update(pMedium, false);
10564 alock.acquire();
10565 AssertComRCThrowRC(rc);
10566 }
10567
10568 /* release the locks before the potentially lengthy operation */
10569 alock.release();
10570 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10571 pMediumLockList,
10572 NULL /* aProgress */,
10573 true /* aWait */);
10574 alock.acquire();
10575 if (FAILED(rc)) throw rc;
10576
10577 rc = lockedMediaMap->Unlock();
10578 AssertComRCThrowRC(rc);
10579 alock.release();
10580 rc = pMediumLockList->Append(diff, true);
10581 alock.acquire();
10582 AssertComRCThrowRC(rc);
10583 alock.release();
10584 rc = lockedMediaMap->Lock();
10585 alock.acquire();
10586 AssertComRCThrowRC(rc);
10587
10588 rc = diff->addBackReference(mData->mUuid);
10589 AssertComRCThrowRC(rc);
10590
10591 /* add a new attachment */
10592 ComObjPtr<MediumAttachment> attachment;
10593 attachment.createObject();
10594 rc = attachment->init(this,
10595 diff,
10596 pAtt->getControllerName(),
10597 pAtt->getPort(),
10598 pAtt->getDevice(),
10599 DeviceType_HardDisk,
10600 true /* aImplicit */,
10601 false /* aPassthrough */,
10602 false /* aTempEject */,
10603 pAtt->getNonRotational(),
10604 pAtt->getDiscard(),
10605 pAtt->getBandwidthGroup());
10606 if (FAILED(rc)) throw rc;
10607
10608 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10609 AssertComRCThrowRC(rc);
10610 mMediaData->mAttachments.push_back(attachment);
10611 }
10612 }
10613 catch (HRESULT aRC) { rc = aRC; }
10614
10615 /* unlock all hard disks we locked when there is no VM */
10616 if (!aOnline)
10617 {
10618 ErrorInfoKeeper eik;
10619
10620 HRESULT rc1 = lockedMediaMap->Clear();
10621 AssertComRC(rc1);
10622 }
10623
10624 return rc;
10625}
10626
10627/**
10628 * Deletes implicit differencing hard disks created either by
10629 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10630 *
10631 * Note that to delete hard disks created by #AttachDevice() this method is
10632 * called from #fixupMedia() when the changes are rolled back.
10633 *
10634 * @note Locks this object and the media tree for writing.
10635 */
10636HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10637{
10638 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10639
10640 AutoCaller autoCaller(this);
10641 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10642
10643 AutoMultiWriteLock2 alock(this->lockHandle(),
10644 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10645
10646 /* We absolutely must have backed up state. */
10647 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10648
10649 /* Check if there are any implicitly created diff images. */
10650 bool fImplicitDiffs = false;
10651 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10652 it != mMediaData->mAttachments.end();
10653 ++it)
10654 {
10655 const ComObjPtr<MediumAttachment> &pAtt = *it;
10656 if (pAtt->isImplicit())
10657 {
10658 fImplicitDiffs = true;
10659 break;
10660 }
10661 }
10662 /* If there is nothing to do, leave early. This saves lots of image locking
10663 * effort. It also avoids a MachineStateChanged event without real reason.
10664 * This is important e.g. when loading a VM config, because there should be
10665 * no events. Otherwise API clients can become thoroughly confused for
10666 * inaccessible VMs (the code for loading VM configs uses this method for
10667 * cleanup if the config makes no sense), as they take such events as an
10668 * indication that the VM is alive, and they would force the VM config to
10669 * be reread, leading to an endless loop. */
10670 if (!fImplicitDiffs)
10671 return S_OK;
10672
10673 HRESULT rc = S_OK;
10674 MachineState_T oldState = mData->mMachineState;
10675
10676 /* will release the lock before the potentially lengthy operation,
10677 * so protect with the special state (unless already protected) */
10678 if ( oldState != MachineState_Saving
10679 && oldState != MachineState_LiveSnapshotting
10680 && oldState != MachineState_RestoringSnapshot
10681 && oldState != MachineState_DeletingSnapshot
10682 && oldState != MachineState_DeletingSnapshotOnline
10683 && oldState != MachineState_DeletingSnapshotPaused
10684 )
10685 setMachineState(MachineState_SettingUp);
10686
10687 // use appropriate locked media map (online or offline)
10688 MediumLockListMap lockedMediaOffline;
10689 MediumLockListMap *lockedMediaMap;
10690 if (aOnline)
10691 lockedMediaMap = &mData->mSession.mLockedMedia;
10692 else
10693 lockedMediaMap = &lockedMediaOffline;
10694
10695 try
10696 {
10697 if (!aOnline)
10698 {
10699 /* lock all attached hard disks early to detect "in use"
10700 * situations before deleting actual diffs */
10701 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10702 it != mMediaData->mAttachments.end();
10703 ++it)
10704 {
10705 MediumAttachment* pAtt = *it;
10706 if (pAtt->getType() == DeviceType_HardDisk)
10707 {
10708 Medium* pMedium = pAtt->getMedium();
10709 Assert(pMedium);
10710
10711 MediumLockList *pMediumLockList(new MediumLockList());
10712 alock.release();
10713 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10714 false /* fMediumLockWrite */,
10715 NULL,
10716 *pMediumLockList);
10717 alock.acquire();
10718
10719 if (FAILED(rc))
10720 {
10721 delete pMediumLockList;
10722 throw rc;
10723 }
10724
10725 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10726 if (FAILED(rc))
10727 throw rc;
10728 }
10729 }
10730
10731 if (FAILED(rc))
10732 throw rc;
10733 } // end of offline
10734
10735 /* Lock lists are now up to date and include implicitly created media */
10736
10737 /* Go through remembered attachments and delete all implicitly created
10738 * diffs and fix up the attachment information */
10739 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10740 MediaData::AttachmentList implicitAtts;
10741 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10742 it != mMediaData->mAttachments.end();
10743 ++it)
10744 {
10745 ComObjPtr<MediumAttachment> pAtt = *it;
10746 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10747 if (pMedium.isNull())
10748 continue;
10749
10750 // Implicit attachments go on the list for deletion and back references are removed.
10751 if (pAtt->isImplicit())
10752 {
10753 /* Deassociate and mark for deletion */
10754 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10755 rc = pMedium->removeBackReference(mData->mUuid);
10756 if (FAILED(rc))
10757 throw rc;
10758 implicitAtts.push_back(pAtt);
10759 continue;
10760 }
10761
10762 /* Was this medium attached before? */
10763 if (!findAttachment(oldAtts, pMedium))
10764 {
10765 /* no: de-associate */
10766 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10767 rc = pMedium->removeBackReference(mData->mUuid);
10768 if (FAILED(rc))
10769 throw rc;
10770 continue;
10771 }
10772 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10773 }
10774
10775 /* If there are implicit attachments to delete, throw away the lock
10776 * map contents (which will unlock all media) since the medium
10777 * attachments will be rolled back. Below we need to completely
10778 * recreate the lock map anyway since it is infinitely complex to
10779 * do this incrementally (would need reconstructing each attachment
10780 * change, which would be extremely hairy). */
10781 if (implicitAtts.size() != 0)
10782 {
10783 ErrorInfoKeeper eik;
10784
10785 HRESULT rc1 = lockedMediaMap->Clear();
10786 AssertComRC(rc1);
10787 }
10788
10789 /* rollback hard disk changes */
10790 mMediaData.rollback();
10791
10792 MultiResult mrc(S_OK);
10793
10794 // Delete unused implicit diffs.
10795 if (implicitAtts.size() != 0)
10796 {
10797 alock.release();
10798
10799 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10800 it != implicitAtts.end();
10801 ++it)
10802 {
10803 // Remove medium associated with this attachment.
10804 ComObjPtr<MediumAttachment> pAtt = *it;
10805 Assert(pAtt);
10806 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10807 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10808 Assert(pMedium);
10809
10810 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10811 // continue on delete failure, just collect error messages
10812 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10813 mrc = rc;
10814 }
10815
10816 alock.acquire();
10817
10818 /* if there is a VM recreate media lock map as mentioned above,
10819 * otherwise it is a waste of time and we leave things unlocked */
10820 if (aOnline)
10821 {
10822 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10823 /* must never be NULL, but better safe than sorry */
10824 if (!pMachine.isNull())
10825 {
10826 alock.release();
10827 rc = mData->mSession.mMachine->lockMedia();
10828 alock.acquire();
10829 if (FAILED(rc))
10830 throw rc;
10831 }
10832 }
10833 }
10834 }
10835 catch (HRESULT aRC) {rc = aRC;}
10836
10837 if (mData->mMachineState == MachineState_SettingUp)
10838 setMachineState(oldState);
10839
10840 /* unlock all hard disks we locked when there is no VM */
10841 if (!aOnline)
10842 {
10843 ErrorInfoKeeper eik;
10844
10845 HRESULT rc1 = lockedMediaMap->Clear();
10846 AssertComRC(rc1);
10847 }
10848
10849 return rc;
10850}
10851
10852
10853/**
10854 * Looks through the given list of media attachments for one with the given parameters
10855 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10856 * can be searched as well if needed.
10857 *
10858 * @param list
10859 * @param aControllerName
10860 * @param aControllerPort
10861 * @param aDevice
10862 * @return
10863 */
10864MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10865 IN_BSTR aControllerName,
10866 LONG aControllerPort,
10867 LONG aDevice)
10868{
10869 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10870 it != ll.end();
10871 ++it)
10872 {
10873 MediumAttachment *pAttach = *it;
10874 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10875 return pAttach;
10876 }
10877
10878 return NULL;
10879}
10880
10881/**
10882 * Looks through the given list of media attachments for one with the given parameters
10883 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10884 * can be searched as well if needed.
10885 *
10886 * @param list
10887 * @param aControllerName
10888 * @param aControllerPort
10889 * @param aDevice
10890 * @return
10891 */
10892MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10893 ComObjPtr<Medium> pMedium)
10894{
10895 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10896 it != ll.end();
10897 ++it)
10898 {
10899 MediumAttachment *pAttach = *it;
10900 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10901 if (pMediumThis == pMedium)
10902 return pAttach;
10903 }
10904
10905 return NULL;
10906}
10907
10908/**
10909 * Looks through the given list of media attachments for one with the given parameters
10910 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10911 * can be searched as well if needed.
10912 *
10913 * @param list
10914 * @param aControllerName
10915 * @param aControllerPort
10916 * @param aDevice
10917 * @return
10918 */
10919MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10920 Guid &id)
10921{
10922 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10923 it != ll.end();
10924 ++it)
10925 {
10926 MediumAttachment *pAttach = *it;
10927 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10928 if (pMediumThis->getId() == id)
10929 return pAttach;
10930 }
10931
10932 return NULL;
10933}
10934
10935/**
10936 * Main implementation for Machine::DetachDevice. This also gets called
10937 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10938 *
10939 * @param pAttach Medium attachment to detach.
10940 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10941 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10942 * @return
10943 */
10944HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10945 AutoWriteLock &writeLock,
10946 Snapshot *pSnapshot)
10947{
10948 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10949 DeviceType_T mediumType = pAttach->getType();
10950
10951 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10952
10953 if (pAttach->isImplicit())
10954 {
10955 /* attempt to implicitly delete the implicitly created diff */
10956
10957 /// @todo move the implicit flag from MediumAttachment to Medium
10958 /// and forbid any hard disk operation when it is implicit. Or maybe
10959 /// a special media state for it to make it even more simple.
10960
10961 Assert(mMediaData.isBackedUp());
10962
10963 /* will release the lock before the potentially lengthy operation, so
10964 * protect with the special state */
10965 MachineState_T oldState = mData->mMachineState;
10966 setMachineState(MachineState_SettingUp);
10967
10968 writeLock.release();
10969
10970 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10971 true /*aWait*/);
10972
10973 writeLock.acquire();
10974
10975 setMachineState(oldState);
10976
10977 if (FAILED(rc)) return rc;
10978 }
10979
10980 setModified(IsModified_Storage);
10981 mMediaData.backup();
10982 mMediaData->mAttachments.remove(pAttach);
10983
10984 if (!oldmedium.isNull())
10985 {
10986 // if this is from a snapshot, do not defer detachment to commitMedia()
10987 if (pSnapshot)
10988 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10989 // else if non-hard disk media, do not defer detachment to commitMedia() either
10990 else if (mediumType != DeviceType_HardDisk)
10991 oldmedium->removeBackReference(mData->mUuid);
10992 }
10993
10994 return S_OK;
10995}
10996
10997/**
10998 * Goes thru all media of the given list and
10999 *
11000 * 1) calls detachDevice() on each of them for this machine and
11001 * 2) adds all Medium objects found in the process to the given list,
11002 * depending on cleanupMode.
11003 *
11004 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11005 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11006 * media to the list.
11007 *
11008 * This gets called from Machine::Unregister, both for the actual Machine and
11009 * the SnapshotMachine objects that might be found in the snapshots.
11010 *
11011 * Requires caller and locking. The machine lock must be passed in because it
11012 * will be passed on to detachDevice which needs it for temporary unlocking.
11013 *
11014 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11015 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11016 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11017 * otherwise no media get added.
11018 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11019 * @return
11020 */
11021HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11022 Snapshot *pSnapshot,
11023 CleanupMode_T cleanupMode,
11024 MediaList &llMedia)
11025{
11026 Assert(isWriteLockOnCurrentThread());
11027
11028 HRESULT rc;
11029
11030 // make a temporary list because detachDevice invalidates iterators into
11031 // mMediaData->mAttachments
11032 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11033
11034 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11035 it != llAttachments2.end();
11036 ++it)
11037 {
11038 ComObjPtr<MediumAttachment> &pAttach = *it;
11039 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11040
11041 if (!pMedium.isNull())
11042 {
11043 AutoCaller mac(pMedium);
11044 if (FAILED(mac.rc())) return mac.rc();
11045 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11046 DeviceType_T devType = pMedium->getDeviceType();
11047 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11048 && devType == DeviceType_HardDisk)
11049 || (cleanupMode == CleanupMode_Full)
11050 )
11051 {
11052 llMedia.push_back(pMedium);
11053 ComObjPtr<Medium> pParent = pMedium->getParent();
11054 /*
11055 * Search for medias which are not attached to any machine, but
11056 * in the chain to an attached disk. Mediums are only consided
11057 * if they are:
11058 * - have only one child
11059 * - no references to any machines
11060 * - are of normal medium type
11061 */
11062 while (!pParent.isNull())
11063 {
11064 AutoCaller mac1(pParent);
11065 if (FAILED(mac1.rc())) return mac1.rc();
11066 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11067 if (pParent->getChildren().size() == 1)
11068 {
11069 if ( pParent->getMachineBackRefCount() == 0
11070 && pParent->getType() == MediumType_Normal
11071 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11072 llMedia.push_back(pParent);
11073 }
11074 else
11075 break;
11076 pParent = pParent->getParent();
11077 }
11078 }
11079 }
11080
11081 // real machine: then we need to use the proper method
11082 rc = detachDevice(pAttach, writeLock, pSnapshot);
11083
11084 if (FAILED(rc))
11085 return rc;
11086 }
11087
11088 return S_OK;
11089}
11090
11091/**
11092 * Perform deferred hard disk detachments.
11093 *
11094 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11095 * backed up).
11096 *
11097 * If @a aOnline is @c true then this method will also unlock the old hard disks
11098 * for which the new implicit diffs were created and will lock these new diffs for
11099 * writing.
11100 *
11101 * @param aOnline Whether the VM was online prior to this operation.
11102 *
11103 * @note Locks this object for writing!
11104 */
11105void Machine::commitMedia(bool aOnline /*= false*/)
11106{
11107 AutoCaller autoCaller(this);
11108 AssertComRCReturnVoid(autoCaller.rc());
11109
11110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11111
11112 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11113
11114 HRESULT rc = S_OK;
11115
11116 /* no attach/detach operations -- nothing to do */
11117 if (!mMediaData.isBackedUp())
11118 return;
11119
11120 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11121 bool fMediaNeedsLocking = false;
11122
11123 /* enumerate new attachments */
11124 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11125 it != mMediaData->mAttachments.end();
11126 ++it)
11127 {
11128 MediumAttachment *pAttach = *it;
11129
11130 pAttach->commit();
11131
11132 Medium* pMedium = pAttach->getMedium();
11133 bool fImplicit = pAttach->isImplicit();
11134
11135 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11136 (pMedium) ? pMedium->getName().c_str() : "NULL",
11137 fImplicit));
11138
11139 /** @todo convert all this Machine-based voodoo to MediumAttachment
11140 * based commit logic. */
11141 if (fImplicit)
11142 {
11143 /* convert implicit attachment to normal */
11144 pAttach->setImplicit(false);
11145
11146 if ( aOnline
11147 && pMedium
11148 && pAttach->getType() == DeviceType_HardDisk
11149 )
11150 {
11151 ComObjPtr<Medium> parent = pMedium->getParent();
11152 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11153
11154 /* update the appropriate lock list */
11155 MediumLockList *pMediumLockList;
11156 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11157 AssertComRC(rc);
11158 if (pMediumLockList)
11159 {
11160 /* unlock if there's a need to change the locking */
11161 if (!fMediaNeedsLocking)
11162 {
11163 rc = mData->mSession.mLockedMedia.Unlock();
11164 AssertComRC(rc);
11165 fMediaNeedsLocking = true;
11166 }
11167 rc = pMediumLockList->Update(parent, false);
11168 AssertComRC(rc);
11169 rc = pMediumLockList->Append(pMedium, true);
11170 AssertComRC(rc);
11171 }
11172 }
11173
11174 continue;
11175 }
11176
11177 if (pMedium)
11178 {
11179 /* was this medium attached before? */
11180 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11181 oldIt != oldAtts.end();
11182 ++oldIt)
11183 {
11184 MediumAttachment *pOldAttach = *oldIt;
11185 if (pOldAttach->getMedium() == pMedium)
11186 {
11187 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11188
11189 /* yes: remove from old to avoid de-association */
11190 oldAtts.erase(oldIt);
11191 break;
11192 }
11193 }
11194 }
11195 }
11196
11197 /* enumerate remaining old attachments and de-associate from the
11198 * current machine state */
11199 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11200 it != oldAtts.end();
11201 ++it)
11202 {
11203 MediumAttachment *pAttach = *it;
11204 Medium* pMedium = pAttach->getMedium();
11205
11206 /* Detach only hard disks, since DVD/floppy media is detached
11207 * instantly in MountMedium. */
11208 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11209 {
11210 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11211
11212 /* now de-associate from the current machine state */
11213 rc = pMedium->removeBackReference(mData->mUuid);
11214 AssertComRC(rc);
11215
11216 if (aOnline)
11217 {
11218 /* unlock since medium is not used anymore */
11219 MediumLockList *pMediumLockList;
11220 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11221 AssertComRC(rc);
11222 if (pMediumLockList)
11223 {
11224 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11225 AssertComRC(rc);
11226 }
11227 }
11228 }
11229 }
11230
11231 /* take media locks again so that the locking state is consistent */
11232 if (fMediaNeedsLocking)
11233 {
11234 Assert(aOnline);
11235 rc = mData->mSession.mLockedMedia.Lock();
11236 AssertComRC(rc);
11237 }
11238
11239 /* commit the hard disk changes */
11240 mMediaData.commit();
11241
11242 if (isSessionMachine())
11243 {
11244 /*
11245 * Update the parent machine to point to the new owner.
11246 * This is necessary because the stored parent will point to the
11247 * session machine otherwise and cause crashes or errors later
11248 * when the session machine gets invalid.
11249 */
11250 /** @todo Change the MediumAttachment class to behave like any other
11251 * class in this regard by creating peer MediumAttachment
11252 * objects for session machines and share the data with the peer
11253 * machine.
11254 */
11255 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11256 it != mMediaData->mAttachments.end();
11257 ++it)
11258 {
11259 (*it)->updateParentMachine(mPeer);
11260 }
11261
11262 /* attach new data to the primary machine and reshare it */
11263 mPeer->mMediaData.attach(mMediaData);
11264 }
11265
11266 return;
11267}
11268
11269/**
11270 * Perform deferred deletion of implicitly created diffs.
11271 *
11272 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11273 * backed up).
11274 *
11275 * @note Locks this object for writing!
11276 */
11277void Machine::rollbackMedia()
11278{
11279 AutoCaller autoCaller(this);
11280 AssertComRCReturnVoid(autoCaller.rc());
11281
11282 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11283 LogFlowThisFunc(("Entering rollbackMedia\n"));
11284
11285 HRESULT rc = S_OK;
11286
11287 /* no attach/detach operations -- nothing to do */
11288 if (!mMediaData.isBackedUp())
11289 return;
11290
11291 /* enumerate new attachments */
11292 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11293 it != mMediaData->mAttachments.end();
11294 ++it)
11295 {
11296 MediumAttachment *pAttach = *it;
11297 /* Fix up the backrefs for DVD/floppy media. */
11298 if (pAttach->getType() != DeviceType_HardDisk)
11299 {
11300 Medium* pMedium = pAttach->getMedium();
11301 if (pMedium)
11302 {
11303 rc = pMedium->removeBackReference(mData->mUuid);
11304 AssertComRC(rc);
11305 }
11306 }
11307
11308 (*it)->rollback();
11309
11310 pAttach = *it;
11311 /* Fix up the backrefs for DVD/floppy media. */
11312 if (pAttach->getType() != DeviceType_HardDisk)
11313 {
11314 Medium* pMedium = pAttach->getMedium();
11315 if (pMedium)
11316 {
11317 rc = pMedium->addBackReference(mData->mUuid);
11318 AssertComRC(rc);
11319 }
11320 }
11321 }
11322
11323 /** @todo convert all this Machine-based voodoo to MediumAttachment
11324 * based rollback logic. */
11325 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11326
11327 return;
11328}
11329
11330/**
11331 * Returns true if the settings file is located in the directory named exactly
11332 * as the machine; this means, among other things, that the machine directory
11333 * should be auto-renamed.
11334 *
11335 * @param aSettingsDir if not NULL, the full machine settings file directory
11336 * name will be assigned there.
11337 *
11338 * @note Doesn't lock anything.
11339 * @note Not thread safe (must be called from this object's lock).
11340 */
11341bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11342{
11343 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11344 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11345 if (aSettingsDir)
11346 *aSettingsDir = strMachineDirName;
11347 strMachineDirName.stripPath(); // vmname
11348 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11349 strConfigFileOnly.stripPath() // vmname.vbox
11350 .stripExt(); // vmname
11351 /** @todo hack, make somehow use of ComposeMachineFilename */
11352 if (mUserData->s.fDirectoryIncludesUUID)
11353 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11354
11355 AssertReturn(!strMachineDirName.isEmpty(), false);
11356 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11357
11358 return strMachineDirName == strConfigFileOnly;
11359}
11360
11361/**
11362 * Discards all changes to machine settings.
11363 *
11364 * @param aNotify Whether to notify the direct session about changes or not.
11365 *
11366 * @note Locks objects for writing!
11367 */
11368void Machine::rollback(bool aNotify)
11369{
11370 AutoCaller autoCaller(this);
11371 AssertComRCReturn(autoCaller.rc(), (void)0);
11372
11373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11374
11375 if (!mStorageControllers.isNull())
11376 {
11377 if (mStorageControllers.isBackedUp())
11378 {
11379 /* unitialize all new devices (absent in the backed up list). */
11380 StorageControllerList::const_iterator it = mStorageControllers->begin();
11381 StorageControllerList *backedList = mStorageControllers.backedUpData();
11382 while (it != mStorageControllers->end())
11383 {
11384 if ( std::find(backedList->begin(), backedList->end(), *it)
11385 == backedList->end()
11386 )
11387 {
11388 (*it)->uninit();
11389 }
11390 ++it;
11391 }
11392
11393 /* restore the list */
11394 mStorageControllers.rollback();
11395 }
11396
11397 /* rollback any changes to devices after restoring the list */
11398 if (mData->flModifications & IsModified_Storage)
11399 {
11400 StorageControllerList::const_iterator it = mStorageControllers->begin();
11401 while (it != mStorageControllers->end())
11402 {
11403 (*it)->rollback();
11404 ++it;
11405 }
11406 }
11407 }
11408
11409 mUserData.rollback();
11410
11411 mHWData.rollback();
11412
11413 if (mData->flModifications & IsModified_Storage)
11414 rollbackMedia();
11415
11416 if (mBIOSSettings)
11417 mBIOSSettings->rollback();
11418
11419 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11420 mVRDEServer->rollback();
11421
11422 if (mAudioAdapter)
11423 mAudioAdapter->rollback();
11424
11425 if (mUSBController && (mData->flModifications & IsModified_USB))
11426 mUSBController->rollback();
11427
11428 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11429 mBandwidthControl->rollback();
11430
11431 if (!mHWData.isNull())
11432 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11433 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11434 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11435 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11436
11437 if (mData->flModifications & IsModified_NetworkAdapters)
11438 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11439 if ( mNetworkAdapters[slot]
11440 && mNetworkAdapters[slot]->isModified())
11441 {
11442 mNetworkAdapters[slot]->rollback();
11443 networkAdapters[slot] = mNetworkAdapters[slot];
11444 }
11445
11446 if (mData->flModifications & IsModified_SerialPorts)
11447 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11448 if ( mSerialPorts[slot]
11449 && mSerialPorts[slot]->isModified())
11450 {
11451 mSerialPorts[slot]->rollback();
11452 serialPorts[slot] = mSerialPorts[slot];
11453 }
11454
11455 if (mData->flModifications & IsModified_ParallelPorts)
11456 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11457 if ( mParallelPorts[slot]
11458 && mParallelPorts[slot]->isModified())
11459 {
11460 mParallelPorts[slot]->rollback();
11461 parallelPorts[slot] = mParallelPorts[slot];
11462 }
11463
11464 if (aNotify)
11465 {
11466 /* inform the direct session about changes */
11467
11468 ComObjPtr<Machine> that = this;
11469 uint32_t flModifications = mData->flModifications;
11470 alock.release();
11471
11472 if (flModifications & IsModified_SharedFolders)
11473 that->onSharedFolderChange();
11474
11475 if (flModifications & IsModified_VRDEServer)
11476 that->onVRDEServerChange(/* aRestart */ TRUE);
11477 if (flModifications & IsModified_USB)
11478 that->onUSBControllerChange();
11479
11480 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11481 if (networkAdapters[slot])
11482 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11483 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11484 if (serialPorts[slot])
11485 that->onSerialPortChange(serialPorts[slot]);
11486 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11487 if (parallelPorts[slot])
11488 that->onParallelPortChange(parallelPorts[slot]);
11489
11490 if (flModifications & IsModified_Storage)
11491 that->onStorageControllerChange();
11492
11493#if 0
11494 if (flModifications & IsModified_BandwidthControl)
11495 that->onBandwidthControlChange();
11496#endif
11497 }
11498}
11499
11500/**
11501 * Commits all the changes to machine settings.
11502 *
11503 * Note that this operation is supposed to never fail.
11504 *
11505 * @note Locks this object and children for writing.
11506 */
11507void Machine::commit()
11508{
11509 AutoCaller autoCaller(this);
11510 AssertComRCReturnVoid(autoCaller.rc());
11511
11512 AutoCaller peerCaller(mPeer);
11513 AssertComRCReturnVoid(peerCaller.rc());
11514
11515 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11516
11517 /*
11518 * use safe commit to ensure Snapshot machines (that share mUserData)
11519 * will still refer to a valid memory location
11520 */
11521 mUserData.commitCopy();
11522
11523 mHWData.commit();
11524
11525 if (mMediaData.isBackedUp())
11526 commitMedia(Global::IsOnline(mData->mMachineState));
11527
11528 mBIOSSettings->commit();
11529 mVRDEServer->commit();
11530 mAudioAdapter->commit();
11531 mUSBController->commit();
11532 mBandwidthControl->commit();
11533
11534 /* Since mNetworkAdapters is a list which might have been changed (resized)
11535 * without using the Backupable<> template we need to handle the copying
11536 * of the list entries manually, including the creation of peers for the
11537 * new objects. */
11538 bool commitNetworkAdapters = false;
11539 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11540 if (mPeer)
11541 {
11542 /* commit everything, even the ones which will go away */
11543 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11544 mNetworkAdapters[slot]->commit();
11545 /* copy over the new entries, creating a peer and uninit the original */
11546 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11547 for (size_t slot = 0; slot < newSize; slot++)
11548 {
11549 /* look if this adapter has a peer device */
11550 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11551 if (!peer)
11552 {
11553 /* no peer means the adapter is a newly created one;
11554 * create a peer owning data this data share it with */
11555 peer.createObject();
11556 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11557 }
11558 mPeer->mNetworkAdapters[slot] = peer;
11559 }
11560 /* uninit any no longer needed network adapters */
11561 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11562 mNetworkAdapters[slot]->uninit();
11563 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11564 {
11565 if (mPeer->mNetworkAdapters[slot])
11566 mPeer->mNetworkAdapters[slot]->uninit();
11567 }
11568 /* Keep the original network adapter count until this point, so that
11569 * discarding a chipset type change will not lose settings. */
11570 mNetworkAdapters.resize(newSize);
11571 mPeer->mNetworkAdapters.resize(newSize);
11572 }
11573 else
11574 {
11575 /* we have no peer (our parent is the newly created machine);
11576 * just commit changes to the network adapters */
11577 commitNetworkAdapters = true;
11578 }
11579 if (commitNetworkAdapters)
11580 {
11581 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11582 mNetworkAdapters[slot]->commit();
11583 }
11584
11585 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11586 mSerialPorts[slot]->commit();
11587 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11588 mParallelPorts[slot]->commit();
11589
11590 bool commitStorageControllers = false;
11591
11592 if (mStorageControllers.isBackedUp())
11593 {
11594 mStorageControllers.commit();
11595
11596 if (mPeer)
11597 {
11598 /* Commit all changes to new controllers (this will reshare data with
11599 * peers for those who have peers) */
11600 StorageControllerList *newList = new StorageControllerList();
11601 StorageControllerList::const_iterator it = mStorageControllers->begin();
11602 while (it != mStorageControllers->end())
11603 {
11604 (*it)->commit();
11605
11606 /* look if this controller has a peer device */
11607 ComObjPtr<StorageController> peer = (*it)->getPeer();
11608 if (!peer)
11609 {
11610 /* no peer means the device is a newly created one;
11611 * create a peer owning data this device share it with */
11612 peer.createObject();
11613 peer->init(mPeer, *it, true /* aReshare */);
11614 }
11615 else
11616 {
11617 /* remove peer from the old list */
11618 mPeer->mStorageControllers->remove(peer);
11619 }
11620 /* and add it to the new list */
11621 newList->push_back(peer);
11622
11623 ++it;
11624 }
11625
11626 /* uninit old peer's controllers that are left */
11627 it = mPeer->mStorageControllers->begin();
11628 while (it != mPeer->mStorageControllers->end())
11629 {
11630 (*it)->uninit();
11631 ++it;
11632 }
11633
11634 /* attach new list of controllers to our peer */
11635 mPeer->mStorageControllers.attach(newList);
11636 }
11637 else
11638 {
11639 /* we have no peer (our parent is the newly created machine);
11640 * just commit changes to devices */
11641 commitStorageControllers = true;
11642 }
11643 }
11644 else
11645 {
11646 /* the list of controllers itself is not changed,
11647 * just commit changes to controllers themselves */
11648 commitStorageControllers = true;
11649 }
11650
11651 if (commitStorageControllers)
11652 {
11653 StorageControllerList::const_iterator it = mStorageControllers->begin();
11654 while (it != mStorageControllers->end())
11655 {
11656 (*it)->commit();
11657 ++it;
11658 }
11659 }
11660
11661 if (isSessionMachine())
11662 {
11663 /* attach new data to the primary machine and reshare it */
11664 mPeer->mUserData.attach(mUserData);
11665 mPeer->mHWData.attach(mHWData);
11666 /* mMediaData is reshared by fixupMedia */
11667 // mPeer->mMediaData.attach(mMediaData);
11668 Assert(mPeer->mMediaData.data() == mMediaData.data());
11669 }
11670}
11671
11672/**
11673 * Copies all the hardware data from the given machine.
11674 *
11675 * Currently, only called when the VM is being restored from a snapshot. In
11676 * particular, this implies that the VM is not running during this method's
11677 * call.
11678 *
11679 * @note This method must be called from under this object's lock.
11680 *
11681 * @note This method doesn't call #commit(), so all data remains backed up and
11682 * unsaved.
11683 */
11684void Machine::copyFrom(Machine *aThat)
11685{
11686 AssertReturnVoid(!isSnapshotMachine());
11687 AssertReturnVoid(aThat->isSnapshotMachine());
11688
11689 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11690
11691 mHWData.assignCopy(aThat->mHWData);
11692
11693 // create copies of all shared folders (mHWData after attaching a copy
11694 // contains just references to original objects)
11695 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11696 it != mHWData->mSharedFolders.end();
11697 ++it)
11698 {
11699 ComObjPtr<SharedFolder> folder;
11700 folder.createObject();
11701 HRESULT rc = folder->initCopy(getMachine(), *it);
11702 AssertComRC(rc);
11703 *it = folder;
11704 }
11705
11706 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11707 mVRDEServer->copyFrom(aThat->mVRDEServer);
11708 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11709 mUSBController->copyFrom(aThat->mUSBController);
11710 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11711
11712 /* create private copies of all controllers */
11713 mStorageControllers.backup();
11714 mStorageControllers->clear();
11715 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11716 it != aThat->mStorageControllers->end();
11717 ++it)
11718 {
11719 ComObjPtr<StorageController> ctrl;
11720 ctrl.createObject();
11721 ctrl->initCopy(this, *it);
11722 mStorageControllers->push_back(ctrl);
11723 }
11724
11725 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11726 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11727 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11728 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11729 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11730 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11731 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11732}
11733
11734/**
11735 * Returns whether the given storage controller is hotplug capable.
11736 *
11737 * @returns true if the controller supports hotplugging
11738 * false otherwise.
11739 * @param enmCtrlType The controller type to check for.
11740 */
11741bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11742{
11743 switch (enmCtrlType)
11744 {
11745 case StorageControllerType_IntelAhci:
11746 return true;
11747 case StorageControllerType_LsiLogic:
11748 case StorageControllerType_LsiLogicSas:
11749 case StorageControllerType_BusLogic:
11750 case StorageControllerType_PIIX3:
11751 case StorageControllerType_PIIX4:
11752 case StorageControllerType_ICH6:
11753 case StorageControllerType_I82078:
11754 default:
11755 return false;
11756 }
11757}
11758
11759#ifdef VBOX_WITH_RESOURCE_USAGE_API
11760
11761void Machine::getDiskList(MediaList &list)
11762{
11763 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11764 it != mMediaData->mAttachments.end();
11765 ++it)
11766 {
11767 MediumAttachment* pAttach = *it;
11768 /* just in case */
11769 AssertStmt(pAttach, continue);
11770
11771 AutoCaller localAutoCallerA(pAttach);
11772 if (FAILED(localAutoCallerA.rc())) continue;
11773
11774 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11775
11776 if (pAttach->getType() == DeviceType_HardDisk)
11777 list.push_back(pAttach->getMedium());
11778 }
11779}
11780
11781void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11782{
11783 AssertReturnVoid(isWriteLockOnCurrentThread());
11784 AssertPtrReturnVoid(aCollector);
11785
11786 pm::CollectorHAL *hal = aCollector->getHAL();
11787 /* Create sub metrics */
11788 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11789 "Percentage of processor time spent in user mode by the VM process.");
11790 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11791 "Percentage of processor time spent in kernel mode by the VM process.");
11792 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11793 "Size of resident portion of VM process in memory.");
11794 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11795 "Actual size of all VM disks combined.");
11796 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11797 "Network receive rate.");
11798 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11799 "Network transmit rate.");
11800 /* Create and register base metrics */
11801 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11802 cpuLoadUser, cpuLoadKernel);
11803 aCollector->registerBaseMetric(cpuLoad);
11804 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11805 ramUsageUsed);
11806 aCollector->registerBaseMetric(ramUsage);
11807 MediaList disks;
11808 getDiskList(disks);
11809 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11810 diskUsageUsed);
11811 aCollector->registerBaseMetric(diskUsage);
11812
11813 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11814 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11815 new pm::AggregateAvg()));
11816 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11817 new pm::AggregateMin()));
11818 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11819 new pm::AggregateMax()));
11820 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11821 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11822 new pm::AggregateAvg()));
11823 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11824 new pm::AggregateMin()));
11825 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11826 new pm::AggregateMax()));
11827
11828 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11829 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11830 new pm::AggregateAvg()));
11831 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11832 new pm::AggregateMin()));
11833 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11834 new pm::AggregateMax()));
11835
11836 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11837 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11838 new pm::AggregateAvg()));
11839 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11840 new pm::AggregateMin()));
11841 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11842 new pm::AggregateMax()));
11843
11844
11845 /* Guest metrics collector */
11846 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11847 aCollector->registerGuest(mCollectorGuest);
11848 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11849 this, __PRETTY_FUNCTION__, mCollectorGuest));
11850
11851 /* Create sub metrics */
11852 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11853 "Percentage of processor time spent in user mode as seen by the guest.");
11854 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11855 "Percentage of processor time spent in kernel mode as seen by the guest.");
11856 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11857 "Percentage of processor time spent idling as seen by the guest.");
11858
11859 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11860 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11861 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11862 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11863 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11864 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11865
11866 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11867
11868 /* Create and register base metrics */
11869 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11870 machineNetRx, machineNetTx);
11871 aCollector->registerBaseMetric(machineNetRate);
11872
11873 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11874 guestLoadUser, guestLoadKernel, guestLoadIdle);
11875 aCollector->registerBaseMetric(guestCpuLoad);
11876
11877 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11878 guestMemTotal, guestMemFree,
11879 guestMemBalloon, guestMemShared,
11880 guestMemCache, guestPagedTotal);
11881 aCollector->registerBaseMetric(guestCpuMem);
11882
11883 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11884 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11885 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11886 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11887
11888 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11889 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11890 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11891 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11892
11893 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11894 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11895 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11896 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11897
11898 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11899 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11900 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11901 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11902
11903 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11904 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11905 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11906 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11907
11908 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11909 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11910 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11911 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11912
11913 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11914 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11915 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11916 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11917
11918 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11919 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11920 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11921 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11922
11923 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11924 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11925 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11926 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11927
11928 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11929 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11930 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11931 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11932
11933 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11934 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11935 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11936 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11937}
11938
11939void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11940{
11941 AssertReturnVoid(isWriteLockOnCurrentThread());
11942
11943 if (aCollector)
11944 {
11945 aCollector->unregisterMetricsFor(aMachine);
11946 aCollector->unregisterBaseMetricsFor(aMachine);
11947 }
11948}
11949
11950#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11951
11952
11953////////////////////////////////////////////////////////////////////////////////
11954
11955DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11956
11957HRESULT SessionMachine::FinalConstruct()
11958{
11959 LogFlowThisFunc(("\n"));
11960
11961#if defined(RT_OS_WINDOWS)
11962 mIPCSem = NULL;
11963#elif defined(RT_OS_OS2)
11964 mIPCSem = NULLHANDLE;
11965#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11966 mIPCSem = -1;
11967#else
11968# error "Port me!"
11969#endif
11970
11971 return BaseFinalConstruct();
11972}
11973
11974void SessionMachine::FinalRelease()
11975{
11976 LogFlowThisFunc(("\n"));
11977
11978 uninit(Uninit::Unexpected);
11979
11980 BaseFinalRelease();
11981}
11982
11983/**
11984 * @note Must be called only by Machine::openSession() from its own write lock.
11985 */
11986HRESULT SessionMachine::init(Machine *aMachine)
11987{
11988 LogFlowThisFuncEnter();
11989 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11990
11991 AssertReturn(aMachine, E_INVALIDARG);
11992
11993 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11994
11995 /* Enclose the state transition NotReady->InInit->Ready */
11996 AutoInitSpan autoInitSpan(this);
11997 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11998
11999 /* create the interprocess semaphore */
12000#if defined(RT_OS_WINDOWS)
12001 mIPCSemName = aMachine->mData->m_strConfigFileFull;
12002 for (size_t i = 0; i < mIPCSemName.length(); i++)
12003 if (mIPCSemName.raw()[i] == '\\')
12004 mIPCSemName.raw()[i] = '/';
12005 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12006 ComAssertMsgRet(mIPCSem,
12007 ("Cannot create IPC mutex '%ls', err=%d",
12008 mIPCSemName.raw(), ::GetLastError()),
12009 E_FAIL);
12010#elif defined(RT_OS_OS2)
12011 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12012 aMachine->mData->mUuid.raw());
12013 mIPCSemName = ipcSem;
12014 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12015 ComAssertMsgRet(arc == NO_ERROR,
12016 ("Cannot create IPC mutex '%s', arc=%ld",
12017 ipcSem.c_str(), arc),
12018 E_FAIL);
12019#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12020# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12021# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12022 /** @todo Check that this still works correctly. */
12023 AssertCompileSize(key_t, 8);
12024# else
12025 AssertCompileSize(key_t, 4);
12026# endif
12027 key_t key;
12028 mIPCSem = -1;
12029 mIPCKey = "0";
12030 for (uint32_t i = 0; i < 1 << 24; i++)
12031 {
12032 key = ((uint32_t)'V' << 24) | i;
12033 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12034 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12035 {
12036 mIPCSem = sem;
12037 if (sem >= 0)
12038 mIPCKey = BstrFmt("%u", key);
12039 break;
12040 }
12041 }
12042# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12043 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12044 char *pszSemName = NULL;
12045 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12046 key_t key = ::ftok(pszSemName, 'V');
12047 RTStrFree(pszSemName);
12048
12049 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12050# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12051
12052 int errnoSave = errno;
12053 if (mIPCSem < 0 && errnoSave == ENOSYS)
12054 {
12055 setError(E_FAIL,
12056 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12057 "support for SysV IPC. Check the host kernel configuration for "
12058 "CONFIG_SYSVIPC=y"));
12059 return E_FAIL;
12060 }
12061 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12062 * the IPC semaphores */
12063 if (mIPCSem < 0 && errnoSave == ENOSPC)
12064 {
12065#ifdef RT_OS_LINUX
12066 setError(E_FAIL,
12067 tr("Cannot create IPC semaphore because the system limit for the "
12068 "maximum number of semaphore sets (SEMMNI), or the system wide "
12069 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12070 "current set of SysV IPC semaphores can be determined from "
12071 "the file /proc/sysvipc/sem"));
12072#else
12073 setError(E_FAIL,
12074 tr("Cannot create IPC semaphore because the system-imposed limit "
12075 "on the maximum number of allowed semaphores or semaphore "
12076 "identifiers system-wide would be exceeded"));
12077#endif
12078 return E_FAIL;
12079 }
12080 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12081 E_FAIL);
12082 /* set the initial value to 1 */
12083 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12084 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12085 E_FAIL);
12086#else
12087# error "Port me!"
12088#endif
12089
12090 /* memorize the peer Machine */
12091 unconst(mPeer) = aMachine;
12092 /* share the parent pointer */
12093 unconst(mParent) = aMachine->mParent;
12094
12095 /* take the pointers to data to share */
12096 mData.share(aMachine->mData);
12097 mSSData.share(aMachine->mSSData);
12098
12099 mUserData.share(aMachine->mUserData);
12100 mHWData.share(aMachine->mHWData);
12101 mMediaData.share(aMachine->mMediaData);
12102
12103 mStorageControllers.allocate();
12104 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12105 it != aMachine->mStorageControllers->end();
12106 ++it)
12107 {
12108 ComObjPtr<StorageController> ctl;
12109 ctl.createObject();
12110 ctl->init(this, *it);
12111 mStorageControllers->push_back(ctl);
12112 }
12113
12114 unconst(mBIOSSettings).createObject();
12115 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12116 /* create another VRDEServer object that will be mutable */
12117 unconst(mVRDEServer).createObject();
12118 mVRDEServer->init(this, aMachine->mVRDEServer);
12119 /* create another audio adapter object that will be mutable */
12120 unconst(mAudioAdapter).createObject();
12121 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12122 /* create a list of serial ports that will be mutable */
12123 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12124 {
12125 unconst(mSerialPorts[slot]).createObject();
12126 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12127 }
12128 /* create a list of parallel ports that will be mutable */
12129 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12130 {
12131 unconst(mParallelPorts[slot]).createObject();
12132 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12133 }
12134 /* create another USB controller object that will be mutable */
12135 unconst(mUSBController).createObject();
12136 mUSBController->init(this, aMachine->mUSBController);
12137
12138 /* create a list of network adapters that will be mutable */
12139 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12140 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12141 {
12142 unconst(mNetworkAdapters[slot]).createObject();
12143 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12144 }
12145
12146 /* create another bandwidth control object that will be mutable */
12147 unconst(mBandwidthControl).createObject();
12148 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12149
12150 /* default is to delete saved state on Saved -> PoweredOff transition */
12151 mRemoveSavedState = true;
12152
12153 /* Confirm a successful initialization when it's the case */
12154 autoInitSpan.setSucceeded();
12155
12156 LogFlowThisFuncLeave();
12157 return S_OK;
12158}
12159
12160/**
12161 * Uninitializes this session object. If the reason is other than
12162 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12163 *
12164 * @param aReason uninitialization reason
12165 *
12166 * @note Locks mParent + this object for writing.
12167 */
12168void SessionMachine::uninit(Uninit::Reason aReason)
12169{
12170 LogFlowThisFuncEnter();
12171 LogFlowThisFunc(("reason=%d\n", aReason));
12172
12173 /*
12174 * Strongly reference ourselves to prevent this object deletion after
12175 * mData->mSession.mMachine.setNull() below (which can release the last
12176 * reference and call the destructor). Important: this must be done before
12177 * accessing any members (and before AutoUninitSpan that does it as well).
12178 * This self reference will be released as the very last step on return.
12179 */
12180 ComObjPtr<SessionMachine> selfRef = this;
12181
12182 /* Enclose the state transition Ready->InUninit->NotReady */
12183 AutoUninitSpan autoUninitSpan(this);
12184 if (autoUninitSpan.uninitDone())
12185 {
12186 LogFlowThisFunc(("Already uninitialized\n"));
12187 LogFlowThisFuncLeave();
12188 return;
12189 }
12190
12191 if (autoUninitSpan.initFailed())
12192 {
12193 /* We've been called by init() because it's failed. It's not really
12194 * necessary (nor it's safe) to perform the regular uninit sequence
12195 * below, the following is enough.
12196 */
12197 LogFlowThisFunc(("Initialization failed.\n"));
12198#if defined(RT_OS_WINDOWS)
12199 if (mIPCSem)
12200 ::CloseHandle(mIPCSem);
12201 mIPCSem = NULL;
12202#elif defined(RT_OS_OS2)
12203 if (mIPCSem != NULLHANDLE)
12204 ::DosCloseMutexSem(mIPCSem);
12205 mIPCSem = NULLHANDLE;
12206#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12207 if (mIPCSem >= 0)
12208 ::semctl(mIPCSem, 0, IPC_RMID);
12209 mIPCSem = -1;
12210# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12211 mIPCKey = "0";
12212# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12213#else
12214# error "Port me!"
12215#endif
12216 uninitDataAndChildObjects();
12217 mData.free();
12218 unconst(mParent) = NULL;
12219 unconst(mPeer) = NULL;
12220 LogFlowThisFuncLeave();
12221 return;
12222 }
12223
12224 MachineState_T lastState;
12225 {
12226 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12227 lastState = mData->mMachineState;
12228 }
12229 NOREF(lastState);
12230
12231#ifdef VBOX_WITH_USB
12232 // release all captured USB devices, but do this before requesting the locks below
12233 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12234 {
12235 /* Console::captureUSBDevices() is called in the VM process only after
12236 * setting the machine state to Starting or Restoring.
12237 * Console::detachAllUSBDevices() will be called upon successful
12238 * termination. So, we need to release USB devices only if there was
12239 * an abnormal termination of a running VM.
12240 *
12241 * This is identical to SessionMachine::DetachAllUSBDevices except
12242 * for the aAbnormal argument. */
12243 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12244 AssertComRC(rc);
12245 NOREF(rc);
12246
12247 USBProxyService *service = mParent->host()->usbProxyService();
12248 if (service)
12249 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12250 }
12251#endif /* VBOX_WITH_USB */
12252
12253 // we need to lock this object in uninit() because the lock is shared
12254 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12255 // and others need mParent lock, and USB needs host lock.
12256 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12257
12258#if 0
12259 // Trigger async cleanup tasks, avoid doing things here which are not
12260 // vital to be done immediately and maybe need more locks. This calls
12261 // Machine::unregisterMetrics().
12262 mParent->onMachineUninit(mPeer);
12263#else
12264 /*
12265 * It is safe to call Machine::unregisterMetrics() here because
12266 * PerformanceCollector::samplerCallback no longer accesses guest methods
12267 * holding the lock.
12268 */
12269 unregisterMetrics(mParent->performanceCollector(), mPeer);
12270#endif
12271 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12272 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12273 this, __PRETTY_FUNCTION__, mCollectorGuest));
12274 if (mCollectorGuest)
12275 {
12276 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12277 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12278 mCollectorGuest = NULL;
12279 }
12280
12281 if (aReason == Uninit::Abnormal)
12282 {
12283 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12284 Global::IsOnlineOrTransient(lastState)));
12285
12286 /* reset the state to Aborted */
12287 if (mData->mMachineState != MachineState_Aborted)
12288 setMachineState(MachineState_Aborted);
12289 }
12290
12291 // any machine settings modified?
12292 if (mData->flModifications)
12293 {
12294 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12295 rollback(false /* aNotify */);
12296 }
12297
12298 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12299 || !mConsoleTaskData.mSnapshot);
12300 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12301 {
12302 LogWarningThisFunc(("canceling failed save state request!\n"));
12303 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12304 }
12305 else if (!mConsoleTaskData.mSnapshot.isNull())
12306 {
12307 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12308
12309 /* delete all differencing hard disks created (this will also attach
12310 * their parents back by rolling back mMediaData) */
12311 rollbackMedia();
12312
12313 // delete the saved state file (it might have been already created)
12314 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12315 // think it's still in use
12316 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12317 mConsoleTaskData.mSnapshot->uninit();
12318 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12319 }
12320
12321 if (!mData->mSession.mType.isEmpty())
12322 {
12323 /* mType is not null when this machine's process has been started by
12324 * Machine::LaunchVMProcess(), therefore it is our child. We
12325 * need to queue the PID to reap the process (and avoid zombies on
12326 * Linux). */
12327 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12328 mParent->addProcessToReap(mData->mSession.mPID);
12329 }
12330
12331 mData->mSession.mPID = NIL_RTPROCESS;
12332
12333 if (aReason == Uninit::Unexpected)
12334 {
12335 /* Uninitialization didn't come from #checkForDeath(), so tell the
12336 * client watcher thread to update the set of machines that have open
12337 * sessions. */
12338 mParent->updateClientWatcher();
12339 }
12340
12341 /* uninitialize all remote controls */
12342 if (mData->mSession.mRemoteControls.size())
12343 {
12344 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12345 mData->mSession.mRemoteControls.size()));
12346
12347 Data::Session::RemoteControlList::iterator it =
12348 mData->mSession.mRemoteControls.begin();
12349 while (it != mData->mSession.mRemoteControls.end())
12350 {
12351 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12352 HRESULT rc = (*it)->Uninitialize();
12353 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12354 if (FAILED(rc))
12355 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12356 ++it;
12357 }
12358 mData->mSession.mRemoteControls.clear();
12359 }
12360
12361 /*
12362 * An expected uninitialization can come only from #checkForDeath().
12363 * Otherwise it means that something's gone really wrong (for example,
12364 * the Session implementation has released the VirtualBox reference
12365 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12366 * etc). However, it's also possible, that the client releases the IPC
12367 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12368 * but the VirtualBox release event comes first to the server process.
12369 * This case is practically possible, so we should not assert on an
12370 * unexpected uninit, just log a warning.
12371 */
12372
12373 if ((aReason == Uninit::Unexpected))
12374 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12375
12376 if (aReason != Uninit::Normal)
12377 {
12378 mData->mSession.mDirectControl.setNull();
12379 }
12380 else
12381 {
12382 /* this must be null here (see #OnSessionEnd()) */
12383 Assert(mData->mSession.mDirectControl.isNull());
12384 Assert(mData->mSession.mState == SessionState_Unlocking);
12385 Assert(!mData->mSession.mProgress.isNull());
12386 }
12387 if (mData->mSession.mProgress)
12388 {
12389 if (aReason == Uninit::Normal)
12390 mData->mSession.mProgress->notifyComplete(S_OK);
12391 else
12392 mData->mSession.mProgress->notifyComplete(E_FAIL,
12393 COM_IIDOF(ISession),
12394 getComponentName(),
12395 tr("The VM session was aborted"));
12396 mData->mSession.mProgress.setNull();
12397 }
12398
12399 /* remove the association between the peer machine and this session machine */
12400 Assert( (SessionMachine*)mData->mSession.mMachine == this
12401 || aReason == Uninit::Unexpected);
12402
12403 /* reset the rest of session data */
12404 mData->mSession.mMachine.setNull();
12405 mData->mSession.mState = SessionState_Unlocked;
12406 mData->mSession.mType.setNull();
12407
12408 /* close the interprocess semaphore before leaving the exclusive lock */
12409#if defined(RT_OS_WINDOWS)
12410 if (mIPCSem)
12411 ::CloseHandle(mIPCSem);
12412 mIPCSem = NULL;
12413#elif defined(RT_OS_OS2)
12414 if (mIPCSem != NULLHANDLE)
12415 ::DosCloseMutexSem(mIPCSem);
12416 mIPCSem = NULLHANDLE;
12417#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12418 if (mIPCSem >= 0)
12419 ::semctl(mIPCSem, 0, IPC_RMID);
12420 mIPCSem = -1;
12421# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12422 mIPCKey = "0";
12423# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12424#else
12425# error "Port me!"
12426#endif
12427
12428 /* fire an event */
12429 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12430
12431 uninitDataAndChildObjects();
12432
12433 /* free the essential data structure last */
12434 mData.free();
12435
12436 /* release the exclusive lock before setting the below two to NULL */
12437 multilock.release();
12438
12439 unconst(mParent) = NULL;
12440 unconst(mPeer) = NULL;
12441
12442 LogFlowThisFuncLeave();
12443}
12444
12445// util::Lockable interface
12446////////////////////////////////////////////////////////////////////////////////
12447
12448/**
12449 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12450 * with the primary Machine instance (mPeer).
12451 */
12452RWLockHandle *SessionMachine::lockHandle() const
12453{
12454 AssertReturn(mPeer != NULL, NULL);
12455 return mPeer->lockHandle();
12456}
12457
12458// IInternalMachineControl methods
12459////////////////////////////////////////////////////////////////////////////////
12460
12461/**
12462 * Passes collected guest statistics to performance collector object
12463 */
12464STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12465 ULONG aCpuKernel, ULONG aCpuIdle,
12466 ULONG aMemTotal, ULONG aMemFree,
12467 ULONG aMemBalloon, ULONG aMemShared,
12468 ULONG aMemCache, ULONG aPageTotal,
12469 ULONG aAllocVMM, ULONG aFreeVMM,
12470 ULONG aBalloonedVMM, ULONG aSharedVMM,
12471 ULONG aVmNetRx, ULONG aVmNetTx)
12472{
12473 if (mCollectorGuest)
12474 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12475 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12476 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12477 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12478
12479 return S_OK;
12480}
12481
12482/**
12483 * @note Locks this object for writing.
12484 */
12485STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12486{
12487 AutoCaller autoCaller(this);
12488 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12489
12490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12491
12492 mRemoveSavedState = aRemove;
12493
12494 return S_OK;
12495}
12496
12497/**
12498 * @note Locks the same as #setMachineState() does.
12499 */
12500STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12501{
12502 return setMachineState(aMachineState);
12503}
12504
12505/**
12506 * @note Locks this object for reading.
12507 */
12508STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12509{
12510 AutoCaller autoCaller(this);
12511 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12512
12513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12514
12515#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12516 mIPCSemName.cloneTo(aId);
12517 return S_OK;
12518#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12519# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12520 mIPCKey.cloneTo(aId);
12521# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12522 mData->m_strConfigFileFull.cloneTo(aId);
12523# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12524 return S_OK;
12525#else
12526# error "Port me!"
12527#endif
12528}
12529
12530/**
12531 * @note Locks this object for writing.
12532 */
12533STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12534{
12535 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12536 AutoCaller autoCaller(this);
12537 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12538
12539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12540
12541 if (mData->mSession.mState != SessionState_Locked)
12542 return VBOX_E_INVALID_OBJECT_STATE;
12543
12544 if (!mData->mSession.mProgress.isNull())
12545 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12546
12547 LogFlowThisFunc(("returns S_OK.\n"));
12548 return S_OK;
12549}
12550
12551/**
12552 * @note Locks this object for writing.
12553 */
12554STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12555{
12556 AutoCaller autoCaller(this);
12557 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12558
12559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12560
12561 if (mData->mSession.mState != SessionState_Locked)
12562 return VBOX_E_INVALID_OBJECT_STATE;
12563
12564 /* Finalize the LaunchVMProcess progress object. */
12565 if (mData->mSession.mProgress)
12566 {
12567 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12568 mData->mSession.mProgress.setNull();
12569 }
12570
12571 if (SUCCEEDED((HRESULT)iResult))
12572 {
12573#ifdef VBOX_WITH_RESOURCE_USAGE_API
12574 /* The VM has been powered up successfully, so it makes sense
12575 * now to offer the performance metrics for a running machine
12576 * object. Doing it earlier wouldn't be safe. */
12577 registerMetrics(mParent->performanceCollector(), mPeer,
12578 mData->mSession.mPID);
12579#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12580 }
12581
12582 return S_OK;
12583}
12584
12585/**
12586 * @note Locks this object for writing.
12587 */
12588STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12589{
12590 LogFlowThisFuncEnter();
12591
12592 CheckComArgOutPointerValid(aProgress);
12593
12594 AutoCaller autoCaller(this);
12595 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12596
12597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12598
12599 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12600 E_FAIL);
12601
12602 /* create a progress object to track operation completion */
12603 ComObjPtr<Progress> pProgress;
12604 pProgress.createObject();
12605 pProgress->init(getVirtualBox(),
12606 static_cast<IMachine *>(this) /* aInitiator */,
12607 Bstr(tr("Stopping the virtual machine")).raw(),
12608 FALSE /* aCancelable */);
12609
12610 /* fill in the console task data */
12611 mConsoleTaskData.mLastState = mData->mMachineState;
12612 mConsoleTaskData.mProgress = pProgress;
12613
12614 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12615 setMachineState(MachineState_Stopping);
12616
12617 pProgress.queryInterfaceTo(aProgress);
12618
12619 return S_OK;
12620}
12621
12622/**
12623 * @note Locks this object for writing.
12624 */
12625STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12626{
12627 LogFlowThisFuncEnter();
12628
12629 AutoCaller autoCaller(this);
12630 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12631
12632 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12633
12634 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12635 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12636 && mConsoleTaskData.mLastState != MachineState_Null,
12637 E_FAIL);
12638
12639 /*
12640 * On failure, set the state to the state we had when BeginPoweringDown()
12641 * was called (this is expected by Console::PowerDown() and the associated
12642 * task). On success the VM process already changed the state to
12643 * MachineState_PoweredOff, so no need to do anything.
12644 */
12645 if (FAILED(iResult))
12646 setMachineState(mConsoleTaskData.mLastState);
12647
12648 /* notify the progress object about operation completion */
12649 Assert(mConsoleTaskData.mProgress);
12650 if (SUCCEEDED(iResult))
12651 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12652 else
12653 {
12654 Utf8Str strErrMsg(aErrMsg);
12655 if (strErrMsg.length())
12656 mConsoleTaskData.mProgress->notifyComplete(iResult,
12657 COM_IIDOF(ISession),
12658 getComponentName(),
12659 strErrMsg.c_str());
12660 else
12661 mConsoleTaskData.mProgress->notifyComplete(iResult);
12662 }
12663
12664 /* clear out the temporary saved state data */
12665 mConsoleTaskData.mLastState = MachineState_Null;
12666 mConsoleTaskData.mProgress.setNull();
12667
12668 LogFlowThisFuncLeave();
12669 return S_OK;
12670}
12671
12672
12673/**
12674 * Goes through the USB filters of the given machine to see if the given
12675 * device matches any filter or not.
12676 *
12677 * @note Locks the same as USBController::hasMatchingFilter() does.
12678 */
12679STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12680 BOOL *aMatched,
12681 ULONG *aMaskedIfs)
12682{
12683 LogFlowThisFunc(("\n"));
12684
12685 CheckComArgNotNull(aUSBDevice);
12686 CheckComArgOutPointerValid(aMatched);
12687
12688 AutoCaller autoCaller(this);
12689 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12690
12691#ifdef VBOX_WITH_USB
12692 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12693#else
12694 NOREF(aUSBDevice);
12695 NOREF(aMaskedIfs);
12696 *aMatched = FALSE;
12697#endif
12698
12699 return S_OK;
12700}
12701
12702/**
12703 * @note Locks the same as Host::captureUSBDevice() does.
12704 */
12705STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12706{
12707 LogFlowThisFunc(("\n"));
12708
12709 AutoCaller autoCaller(this);
12710 AssertComRCReturnRC(autoCaller.rc());
12711
12712#ifdef VBOX_WITH_USB
12713 /* if captureDeviceForVM() fails, it must have set extended error info */
12714 clearError();
12715 MultiResult rc = mParent->host()->checkUSBProxyService();
12716 if (FAILED(rc)) return rc;
12717
12718 USBProxyService *service = mParent->host()->usbProxyService();
12719 AssertReturn(service, E_FAIL);
12720 return service->captureDeviceForVM(this, Guid(aId).ref());
12721#else
12722 NOREF(aId);
12723 return E_NOTIMPL;
12724#endif
12725}
12726
12727/**
12728 * @note Locks the same as Host::detachUSBDevice() does.
12729 */
12730STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12731{
12732 LogFlowThisFunc(("\n"));
12733
12734 AutoCaller autoCaller(this);
12735 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12736
12737#ifdef VBOX_WITH_USB
12738 USBProxyService *service = mParent->host()->usbProxyService();
12739 AssertReturn(service, E_FAIL);
12740 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12741#else
12742 NOREF(aId);
12743 NOREF(aDone);
12744 return E_NOTIMPL;
12745#endif
12746}
12747
12748/**
12749 * Inserts all machine filters to the USB proxy service and then calls
12750 * Host::autoCaptureUSBDevices().
12751 *
12752 * Called by Console from the VM process upon VM startup.
12753 *
12754 * @note Locks what called methods lock.
12755 */
12756STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12757{
12758 LogFlowThisFunc(("\n"));
12759
12760 AutoCaller autoCaller(this);
12761 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12762
12763#ifdef VBOX_WITH_USB
12764 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12765 AssertComRC(rc);
12766 NOREF(rc);
12767
12768 USBProxyService *service = mParent->host()->usbProxyService();
12769 AssertReturn(service, E_FAIL);
12770 return service->autoCaptureDevicesForVM(this);
12771#else
12772 return S_OK;
12773#endif
12774}
12775
12776/**
12777 * Removes all machine filters from the USB proxy service and then calls
12778 * Host::detachAllUSBDevices().
12779 *
12780 * Called by Console from the VM process upon normal VM termination or by
12781 * SessionMachine::uninit() upon abnormal VM termination (from under the
12782 * Machine/SessionMachine lock).
12783 *
12784 * @note Locks what called methods lock.
12785 */
12786STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12787{
12788 LogFlowThisFunc(("\n"));
12789
12790 AutoCaller autoCaller(this);
12791 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12792
12793#ifdef VBOX_WITH_USB
12794 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12795 AssertComRC(rc);
12796 NOREF(rc);
12797
12798 USBProxyService *service = mParent->host()->usbProxyService();
12799 AssertReturn(service, E_FAIL);
12800 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12801#else
12802 NOREF(aDone);
12803 return S_OK;
12804#endif
12805}
12806
12807/**
12808 * @note Locks this object for writing.
12809 */
12810STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12811 IProgress **aProgress)
12812{
12813 LogFlowThisFuncEnter();
12814
12815 AssertReturn(aSession, E_INVALIDARG);
12816 AssertReturn(aProgress, E_INVALIDARG);
12817
12818 AutoCaller autoCaller(this);
12819
12820 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12821 /*
12822 * We don't assert below because it might happen that a non-direct session
12823 * informs us it is closed right after we've been uninitialized -- it's ok.
12824 */
12825 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12826
12827 /* get IInternalSessionControl interface */
12828 ComPtr<IInternalSessionControl> control(aSession);
12829
12830 ComAssertRet(!control.isNull(), E_INVALIDARG);
12831
12832 /* Creating a Progress object requires the VirtualBox lock, and
12833 * thus locking it here is required by the lock order rules. */
12834 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12835
12836 if (control == mData->mSession.mDirectControl)
12837 {
12838 ComAssertRet(aProgress, E_POINTER);
12839
12840 /* The direct session is being normally closed by the client process
12841 * ----------------------------------------------------------------- */
12842
12843 /* go to the closing state (essential for all open*Session() calls and
12844 * for #checkForDeath()) */
12845 Assert(mData->mSession.mState == SessionState_Locked);
12846 mData->mSession.mState = SessionState_Unlocking;
12847
12848 /* set direct control to NULL to release the remote instance */
12849 mData->mSession.mDirectControl.setNull();
12850 LogFlowThisFunc(("Direct control is set to NULL\n"));
12851
12852 if (mData->mSession.mProgress)
12853 {
12854 /* finalize the progress, someone might wait if a frontend
12855 * closes the session before powering on the VM. */
12856 mData->mSession.mProgress->notifyComplete(E_FAIL,
12857 COM_IIDOF(ISession),
12858 getComponentName(),
12859 tr("The VM session was closed before any attempt to power it on"));
12860 mData->mSession.mProgress.setNull();
12861 }
12862
12863 /* Create the progress object the client will use to wait until
12864 * #checkForDeath() is called to uninitialize this session object after
12865 * it releases the IPC semaphore.
12866 * Note! Because we're "reusing" mProgress here, this must be a proxy
12867 * object just like for LaunchVMProcess. */
12868 Assert(mData->mSession.mProgress.isNull());
12869 ComObjPtr<ProgressProxy> progress;
12870 progress.createObject();
12871 ComPtr<IUnknown> pPeer(mPeer);
12872 progress->init(mParent, pPeer,
12873 Bstr(tr("Closing session")).raw(),
12874 FALSE /* aCancelable */);
12875 progress.queryInterfaceTo(aProgress);
12876 mData->mSession.mProgress = progress;
12877 }
12878 else
12879 {
12880 /* the remote session is being normally closed */
12881 Data::Session::RemoteControlList::iterator it =
12882 mData->mSession.mRemoteControls.begin();
12883 while (it != mData->mSession.mRemoteControls.end())
12884 {
12885 if (control == *it)
12886 break;
12887 ++it;
12888 }
12889 BOOL found = it != mData->mSession.mRemoteControls.end();
12890 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12891 E_INVALIDARG);
12892 // This MUST be erase(it), not remove(*it) as the latter triggers a
12893 // very nasty use after free due to the place where the value "lives".
12894 mData->mSession.mRemoteControls.erase(it);
12895 }
12896
12897 /* signal the client watcher thread, because the client is going away */
12898 mParent->updateClientWatcher();
12899
12900 LogFlowThisFuncLeave();
12901 return S_OK;
12902}
12903
12904/**
12905 * @note Locks this object for writing.
12906 */
12907STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12908{
12909 LogFlowThisFuncEnter();
12910
12911 CheckComArgOutPointerValid(aProgress);
12912 CheckComArgOutPointerValid(aStateFilePath);
12913
12914 AutoCaller autoCaller(this);
12915 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12916
12917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12918
12919 AssertReturn( mData->mMachineState == MachineState_Paused
12920 && mConsoleTaskData.mLastState == MachineState_Null
12921 && mConsoleTaskData.strStateFilePath.isEmpty(),
12922 E_FAIL);
12923
12924 /* create a progress object to track operation completion */
12925 ComObjPtr<Progress> pProgress;
12926 pProgress.createObject();
12927 pProgress->init(getVirtualBox(),
12928 static_cast<IMachine *>(this) /* aInitiator */,
12929 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12930 FALSE /* aCancelable */);
12931
12932 Utf8Str strStateFilePath;
12933 /* stateFilePath is null when the machine is not running */
12934 if (mData->mMachineState == MachineState_Paused)
12935 composeSavedStateFilename(strStateFilePath);
12936
12937 /* fill in the console task data */
12938 mConsoleTaskData.mLastState = mData->mMachineState;
12939 mConsoleTaskData.strStateFilePath = strStateFilePath;
12940 mConsoleTaskData.mProgress = pProgress;
12941
12942 /* set the state to Saving (this is expected by Console::SaveState()) */
12943 setMachineState(MachineState_Saving);
12944
12945 strStateFilePath.cloneTo(aStateFilePath);
12946 pProgress.queryInterfaceTo(aProgress);
12947
12948 return S_OK;
12949}
12950
12951/**
12952 * @note Locks mParent + this object for writing.
12953 */
12954STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12955{
12956 LogFlowThisFunc(("\n"));
12957
12958 AutoCaller autoCaller(this);
12959 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12960
12961 /* endSavingState() need mParent lock */
12962 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12963
12964 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12965 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12966 && mConsoleTaskData.mLastState != MachineState_Null
12967 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12968 E_FAIL);
12969
12970 /*
12971 * On failure, set the state to the state we had when BeginSavingState()
12972 * was called (this is expected by Console::SaveState() and the associated
12973 * task). On success the VM process already changed the state to
12974 * MachineState_Saved, so no need to do anything.
12975 */
12976 if (FAILED(iResult))
12977 setMachineState(mConsoleTaskData.mLastState);
12978
12979 return endSavingState(iResult, aErrMsg);
12980}
12981
12982/**
12983 * @note Locks this object for writing.
12984 */
12985STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12986{
12987 LogFlowThisFunc(("\n"));
12988
12989 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12990
12991 AutoCaller autoCaller(this);
12992 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12993
12994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12995
12996 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12997 || mData->mMachineState == MachineState_Teleported
12998 || mData->mMachineState == MachineState_Aborted
12999 , E_FAIL); /** @todo setError. */
13000
13001 Utf8Str stateFilePathFull = aSavedStateFile;
13002 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13003 if (RT_FAILURE(vrc))
13004 return setError(VBOX_E_FILE_ERROR,
13005 tr("Invalid saved state file path '%ls' (%Rrc)"),
13006 aSavedStateFile,
13007 vrc);
13008
13009 mSSData->strStateFilePath = stateFilePathFull;
13010
13011 /* The below setMachineState() will detect the state transition and will
13012 * update the settings file */
13013
13014 return setMachineState(MachineState_Saved);
13015}
13016
13017STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13018 ComSafeArrayOut(BSTR, aValues),
13019 ComSafeArrayOut(LONG64, aTimestamps),
13020 ComSafeArrayOut(BSTR, aFlags))
13021{
13022 LogFlowThisFunc(("\n"));
13023
13024#ifdef VBOX_WITH_GUEST_PROPS
13025 using namespace guestProp;
13026
13027 AutoCaller autoCaller(this);
13028 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13029
13030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13031
13032 CheckComArgOutSafeArrayPointerValid(aNames);
13033 CheckComArgOutSafeArrayPointerValid(aValues);
13034 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13035 CheckComArgOutSafeArrayPointerValid(aFlags);
13036
13037 size_t cEntries = mHWData->mGuestProperties.size();
13038 com::SafeArray<BSTR> names(cEntries);
13039 com::SafeArray<BSTR> values(cEntries);
13040 com::SafeArray<LONG64> timestamps(cEntries);
13041 com::SafeArray<BSTR> flags(cEntries);
13042 unsigned i = 0;
13043 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13044 it != mHWData->mGuestProperties.end();
13045 ++it)
13046 {
13047 char szFlags[MAX_FLAGS_LEN + 1];
13048 it->first.cloneTo(&names[i]);
13049 it->second.strValue.cloneTo(&values[i]);
13050 timestamps[i] = it->second.mTimestamp;
13051 /* If it is NULL, keep it NULL. */
13052 if (it->second.mFlags)
13053 {
13054 writeFlags(it->second.mFlags, szFlags);
13055 Bstr(szFlags).cloneTo(&flags[i]);
13056 }
13057 else
13058 flags[i] = NULL;
13059 ++i;
13060 }
13061 names.detachTo(ComSafeArrayOutArg(aNames));
13062 values.detachTo(ComSafeArrayOutArg(aValues));
13063 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13064 flags.detachTo(ComSafeArrayOutArg(aFlags));
13065 return S_OK;
13066#else
13067 ReturnComNotImplemented();
13068#endif
13069}
13070
13071STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13072 IN_BSTR aValue,
13073 LONG64 aTimestamp,
13074 IN_BSTR aFlags)
13075{
13076 LogFlowThisFunc(("\n"));
13077
13078#ifdef VBOX_WITH_GUEST_PROPS
13079 using namespace guestProp;
13080
13081 CheckComArgStrNotEmptyOrNull(aName);
13082 CheckComArgNotNull(aValue);
13083 CheckComArgNotNull(aFlags);
13084
13085 try
13086 {
13087 /*
13088 * Convert input up front.
13089 */
13090 Utf8Str utf8Name(aName);
13091 uint32_t fFlags = NILFLAG;
13092 if (aFlags)
13093 {
13094 Utf8Str utf8Flags(aFlags);
13095 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13096 AssertRCReturn(vrc, E_INVALIDARG);
13097 }
13098
13099 /*
13100 * Now grab the object lock, validate the state and do the update.
13101 */
13102 AutoCaller autoCaller(this);
13103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13104
13105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13106
13107 switch (mData->mMachineState)
13108 {
13109 case MachineState_Paused:
13110 case MachineState_Running:
13111 case MachineState_Teleporting:
13112 case MachineState_TeleportingPausedVM:
13113 case MachineState_LiveSnapshotting:
13114 case MachineState_DeletingSnapshotOnline:
13115 case MachineState_DeletingSnapshotPaused:
13116 case MachineState_Saving:
13117 case MachineState_Stopping:
13118 break;
13119
13120 default:
13121 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13122 VBOX_E_INVALID_VM_STATE);
13123 }
13124
13125 setModified(IsModified_MachineData);
13126 mHWData.backup();
13127
13128 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13129 if (it != mHWData->mGuestProperties.end())
13130 {
13131 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
13132 {
13133 it->second.strValue = aValue;
13134 it->second.mFlags = fFlags;
13135 it->second.mTimestamp = aTimestamp;
13136 }
13137 else
13138 mHWData->mGuestProperties.erase(it);
13139
13140 mData->mGuestPropertiesModified = TRUE;
13141 }
13142
13143 /*
13144 * Send a callback notification if appropriate
13145 */
13146 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13147 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13148 RTSTR_MAX,
13149 utf8Name.c_str(),
13150 RTSTR_MAX, NULL)
13151 )
13152 {
13153 alock.release();
13154
13155 mParent->onGuestPropertyChange(mData->mUuid,
13156 aName,
13157 aValue,
13158 aFlags);
13159 }
13160 }
13161 catch (...)
13162 {
13163 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13164 }
13165 return S_OK;
13166#else
13167 ReturnComNotImplemented();
13168#endif
13169}
13170
13171STDMETHODIMP SessionMachine::LockMedia()
13172{
13173 AutoCaller autoCaller(this);
13174 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13175
13176 AutoMultiWriteLock2 alock(this->lockHandle(),
13177 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13178
13179 AssertReturn( mData->mMachineState == MachineState_Starting
13180 || mData->mMachineState == MachineState_Restoring
13181 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13182
13183 clearError();
13184 alock.release();
13185 return lockMedia();
13186}
13187
13188STDMETHODIMP SessionMachine::UnlockMedia()
13189{
13190 unlockMedia();
13191 return S_OK;
13192}
13193
13194STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13195 IMediumAttachment **aNewAttachment)
13196{
13197 CheckComArgNotNull(aAttachment);
13198 CheckComArgOutPointerValid(aNewAttachment);
13199
13200 AutoCaller autoCaller(this);
13201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13202
13203 // request the host lock first, since might be calling Host methods for getting host drives;
13204 // next, protect the media tree all the while we're in here, as well as our member variables
13205 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13206 this->lockHandle(),
13207 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13208
13209 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13210
13211 Bstr ctrlName;
13212 LONG lPort;
13213 LONG lDevice;
13214 bool fTempEject;
13215 {
13216 AutoCaller autoAttachCaller(this);
13217 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13218
13219 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13220
13221 /* Need to query the details first, as the IMediumAttachment reference
13222 * might be to the original settings, which we are going to change. */
13223 ctrlName = pAttach->getControllerName();
13224 lPort = pAttach->getPort();
13225 lDevice = pAttach->getDevice();
13226 fTempEject = pAttach->getTempEject();
13227 }
13228
13229 if (!fTempEject)
13230 {
13231 /* Remember previously mounted medium. The medium before taking the
13232 * backup is not necessarily the same thing. */
13233 ComObjPtr<Medium> oldmedium;
13234 oldmedium = pAttach->getMedium();
13235
13236 setModified(IsModified_Storage);
13237 mMediaData.backup();
13238
13239 // The backup operation makes the pAttach reference point to the
13240 // old settings. Re-get the correct reference.
13241 pAttach = findAttachment(mMediaData->mAttachments,
13242 ctrlName.raw(),
13243 lPort,
13244 lDevice);
13245
13246 {
13247 AutoCaller autoAttachCaller(this);
13248 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13249
13250 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13251 if (!oldmedium.isNull())
13252 oldmedium->removeBackReference(mData->mUuid);
13253
13254 pAttach->updateMedium(NULL);
13255 pAttach->updateEjected();
13256 }
13257
13258 setModified(IsModified_Storage);
13259 }
13260 else
13261 {
13262 {
13263 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13264 pAttach->updateEjected();
13265 }
13266 }
13267
13268 pAttach.queryInterfaceTo(aNewAttachment);
13269
13270 return S_OK;
13271}
13272
13273// public methods only for internal purposes
13274/////////////////////////////////////////////////////////////////////////////
13275
13276/**
13277 * Called from the client watcher thread to check for expected or unexpected
13278 * death of the client process that has a direct session to this machine.
13279 *
13280 * On Win32 and on OS/2, this method is called only when we've got the
13281 * mutex (i.e. the client has either died or terminated normally) so it always
13282 * returns @c true (the client is terminated, the session machine is
13283 * uninitialized).
13284 *
13285 * On other platforms, the method returns @c true if the client process has
13286 * terminated normally or abnormally and the session machine was uninitialized,
13287 * and @c false if the client process is still alive.
13288 *
13289 * @note Locks this object for writing.
13290 */
13291bool SessionMachine::checkForDeath()
13292{
13293 Uninit::Reason reason;
13294 bool terminated = false;
13295
13296 /* Enclose autoCaller with a block because calling uninit() from under it
13297 * will deadlock. */
13298 {
13299 AutoCaller autoCaller(this);
13300 if (!autoCaller.isOk())
13301 {
13302 /* return true if not ready, to cause the client watcher to exclude
13303 * the corresponding session from watching */
13304 LogFlowThisFunc(("Already uninitialized!\n"));
13305 return true;
13306 }
13307
13308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13309
13310 /* Determine the reason of death: if the session state is Closing here,
13311 * everything is fine. Otherwise it means that the client did not call
13312 * OnSessionEnd() before it released the IPC semaphore. This may happen
13313 * either because the client process has abnormally terminated, or
13314 * because it simply forgot to call ISession::Close() before exiting. We
13315 * threat the latter also as an abnormal termination (see
13316 * Session::uninit() for details). */
13317 reason = mData->mSession.mState == SessionState_Unlocking ?
13318 Uninit::Normal :
13319 Uninit::Abnormal;
13320
13321#if defined(RT_OS_WINDOWS)
13322
13323 AssertMsg(mIPCSem, ("semaphore must be created"));
13324
13325 /* release the IPC mutex */
13326 ::ReleaseMutex(mIPCSem);
13327
13328 terminated = true;
13329
13330#elif defined(RT_OS_OS2)
13331
13332 AssertMsg(mIPCSem, ("semaphore must be created"));
13333
13334 /* release the IPC mutex */
13335 ::DosReleaseMutexSem(mIPCSem);
13336
13337 terminated = true;
13338
13339#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13340
13341 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13342
13343 int val = ::semctl(mIPCSem, 0, GETVAL);
13344 if (val > 0)
13345 {
13346 /* the semaphore is signaled, meaning the session is terminated */
13347 terminated = true;
13348 }
13349
13350#else
13351# error "Port me!"
13352#endif
13353
13354 } /* AutoCaller block */
13355
13356 if (terminated)
13357 uninit(reason);
13358
13359 return terminated;
13360}
13361
13362/**
13363 * @note Locks this object for reading.
13364 */
13365HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13366{
13367 LogFlowThisFunc(("\n"));
13368
13369 AutoCaller autoCaller(this);
13370 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13371
13372 ComPtr<IInternalSessionControl> directControl;
13373 {
13374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13375 directControl = mData->mSession.mDirectControl;
13376 }
13377
13378 /* ignore notifications sent after #OnSessionEnd() is called */
13379 if (!directControl)
13380 return S_OK;
13381
13382 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13383}
13384
13385/**
13386 * @note Locks this object for reading.
13387 */
13388HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13389 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13390{
13391 LogFlowThisFunc(("\n"));
13392
13393 AutoCaller autoCaller(this);
13394 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13395
13396 ComPtr<IInternalSessionControl> directControl;
13397 {
13398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13399 directControl = mData->mSession.mDirectControl;
13400 }
13401
13402 /* ignore notifications sent after #OnSessionEnd() is called */
13403 if (!directControl)
13404 return S_OK;
13405 /*
13406 * instead acting like callback we ask IVirtualBox deliver corresponding event
13407 */
13408
13409 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13410 return S_OK;
13411}
13412
13413/**
13414 * @note Locks this object for reading.
13415 */
13416HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13417{
13418 LogFlowThisFunc(("\n"));
13419
13420 AutoCaller autoCaller(this);
13421 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13422
13423 ComPtr<IInternalSessionControl> directControl;
13424 {
13425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13426 directControl = mData->mSession.mDirectControl;
13427 }
13428
13429 /* ignore notifications sent after #OnSessionEnd() is called */
13430 if (!directControl)
13431 return S_OK;
13432
13433 return directControl->OnSerialPortChange(serialPort);
13434}
13435
13436/**
13437 * @note Locks this object for reading.
13438 */
13439HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13440{
13441 LogFlowThisFunc(("\n"));
13442
13443 AutoCaller autoCaller(this);
13444 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13445
13446 ComPtr<IInternalSessionControl> directControl;
13447 {
13448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13449 directControl = mData->mSession.mDirectControl;
13450 }
13451
13452 /* ignore notifications sent after #OnSessionEnd() is called */
13453 if (!directControl)
13454 return S_OK;
13455
13456 return directControl->OnParallelPortChange(parallelPort);
13457}
13458
13459/**
13460 * @note Locks this object for reading.
13461 */
13462HRESULT SessionMachine::onStorageControllerChange()
13463{
13464 LogFlowThisFunc(("\n"));
13465
13466 AutoCaller autoCaller(this);
13467 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13468
13469 ComPtr<IInternalSessionControl> directControl;
13470 {
13471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13472 directControl = mData->mSession.mDirectControl;
13473 }
13474
13475 /* ignore notifications sent after #OnSessionEnd() is called */
13476 if (!directControl)
13477 return S_OK;
13478
13479 return directControl->OnStorageControllerChange();
13480}
13481
13482/**
13483 * @note Locks this object for reading.
13484 */
13485HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13486{
13487 LogFlowThisFunc(("\n"));
13488
13489 AutoCaller autoCaller(this);
13490 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13491
13492 ComPtr<IInternalSessionControl> directControl;
13493 {
13494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13495 directControl = mData->mSession.mDirectControl;
13496 }
13497
13498 /* ignore notifications sent after #OnSessionEnd() is called */
13499 if (!directControl)
13500 return S_OK;
13501
13502 return directControl->OnMediumChange(aAttachment, aForce);
13503}
13504
13505/**
13506 * @note Locks this object for reading.
13507 */
13508HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13509{
13510 LogFlowThisFunc(("\n"));
13511
13512 AutoCaller autoCaller(this);
13513 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13514
13515 ComPtr<IInternalSessionControl> directControl;
13516 {
13517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13518 directControl = mData->mSession.mDirectControl;
13519 }
13520
13521 /* ignore notifications sent after #OnSessionEnd() is called */
13522 if (!directControl)
13523 return S_OK;
13524
13525 return directControl->OnCPUChange(aCPU, aRemove);
13526}
13527
13528HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13529{
13530 LogFlowThisFunc(("\n"));
13531
13532 AutoCaller autoCaller(this);
13533 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13534
13535 ComPtr<IInternalSessionControl> directControl;
13536 {
13537 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13538 directControl = mData->mSession.mDirectControl;
13539 }
13540
13541 /* ignore notifications sent after #OnSessionEnd() is called */
13542 if (!directControl)
13543 return S_OK;
13544
13545 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13546}
13547
13548/**
13549 * @note Locks this object for reading.
13550 */
13551HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13552{
13553 LogFlowThisFunc(("\n"));
13554
13555 AutoCaller autoCaller(this);
13556 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13557
13558 ComPtr<IInternalSessionControl> directControl;
13559 {
13560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13561 directControl = mData->mSession.mDirectControl;
13562 }
13563
13564 /* ignore notifications sent after #OnSessionEnd() is called */
13565 if (!directControl)
13566 return S_OK;
13567
13568 return directControl->OnVRDEServerChange(aRestart);
13569}
13570
13571/**
13572 * @note Locks this object for reading.
13573 */
13574HRESULT SessionMachine::onUSBControllerChange()
13575{
13576 LogFlowThisFunc(("\n"));
13577
13578 AutoCaller autoCaller(this);
13579 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13580
13581 ComPtr<IInternalSessionControl> directControl;
13582 {
13583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13584 directControl = mData->mSession.mDirectControl;
13585 }
13586
13587 /* ignore notifications sent after #OnSessionEnd() is called */
13588 if (!directControl)
13589 return S_OK;
13590
13591 return directControl->OnUSBControllerChange();
13592}
13593
13594/**
13595 * @note Locks this object for reading.
13596 */
13597HRESULT SessionMachine::onSharedFolderChange()
13598{
13599 LogFlowThisFunc(("\n"));
13600
13601 AutoCaller autoCaller(this);
13602 AssertComRCReturnRC(autoCaller.rc());
13603
13604 ComPtr<IInternalSessionControl> directControl;
13605 {
13606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13607 directControl = mData->mSession.mDirectControl;
13608 }
13609
13610 /* ignore notifications sent after #OnSessionEnd() is called */
13611 if (!directControl)
13612 return S_OK;
13613
13614 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13615}
13616
13617/**
13618 * @note Locks this object for reading.
13619 */
13620HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13621{
13622 LogFlowThisFunc(("\n"));
13623
13624 AutoCaller autoCaller(this);
13625 AssertComRCReturnRC(autoCaller.rc());
13626
13627 ComPtr<IInternalSessionControl> directControl;
13628 {
13629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13630 directControl = mData->mSession.mDirectControl;
13631 }
13632
13633 /* ignore notifications sent after #OnSessionEnd() is called */
13634 if (!directControl)
13635 return S_OK;
13636
13637 return directControl->OnClipboardModeChange(aClipboardMode);
13638}
13639
13640/**
13641 * @note Locks this object for reading.
13642 */
13643HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13644{
13645 LogFlowThisFunc(("\n"));
13646
13647 AutoCaller autoCaller(this);
13648 AssertComRCReturnRC(autoCaller.rc());
13649
13650 ComPtr<IInternalSessionControl> directControl;
13651 {
13652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13653 directControl = mData->mSession.mDirectControl;
13654 }
13655
13656 /* ignore notifications sent after #OnSessionEnd() is called */
13657 if (!directControl)
13658 return S_OK;
13659
13660 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13661}
13662
13663/**
13664 * @note Locks this object for reading.
13665 */
13666HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13667{
13668 LogFlowThisFunc(("\n"));
13669
13670 AutoCaller autoCaller(this);
13671 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13672
13673 ComPtr<IInternalSessionControl> directControl;
13674 {
13675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13676 directControl = mData->mSession.mDirectControl;
13677 }
13678
13679 /* ignore notifications sent after #OnSessionEnd() is called */
13680 if (!directControl)
13681 return S_OK;
13682
13683 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13684}
13685
13686/**
13687 * @note Locks this object for reading.
13688 */
13689HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13690{
13691 LogFlowThisFunc(("\n"));
13692
13693 AutoCaller autoCaller(this);
13694 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13695
13696 ComPtr<IInternalSessionControl> directControl;
13697 {
13698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13699 directControl = mData->mSession.mDirectControl;
13700 }
13701
13702 /* ignore notifications sent after #OnSessionEnd() is called */
13703 if (!directControl)
13704 return S_OK;
13705
13706 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13707}
13708
13709/**
13710 * Returns @c true if this machine's USB controller reports it has a matching
13711 * filter for the given USB device and @c false otherwise.
13712 *
13713 * @note locks this object for reading.
13714 */
13715bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13716{
13717 AutoCaller autoCaller(this);
13718 /* silently return if not ready -- this method may be called after the
13719 * direct machine session has been called */
13720 if (!autoCaller.isOk())
13721 return false;
13722
13723#ifdef VBOX_WITH_USB
13724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13725
13726 switch (mData->mMachineState)
13727 {
13728 case MachineState_Starting:
13729 case MachineState_Restoring:
13730 case MachineState_TeleportingIn:
13731 case MachineState_Paused:
13732 case MachineState_Running:
13733 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13734 * elsewhere... */
13735 alock.release();
13736 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13737 default: break;
13738 }
13739#else
13740 NOREF(aDevice);
13741 NOREF(aMaskedIfs);
13742#endif
13743 return false;
13744}
13745
13746/**
13747 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13748 */
13749HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13750 IVirtualBoxErrorInfo *aError,
13751 ULONG aMaskedIfs)
13752{
13753 LogFlowThisFunc(("\n"));
13754
13755 AutoCaller autoCaller(this);
13756
13757 /* This notification may happen after the machine object has been
13758 * uninitialized (the session was closed), so don't assert. */
13759 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13760
13761 ComPtr<IInternalSessionControl> directControl;
13762 {
13763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13764 directControl = mData->mSession.mDirectControl;
13765 }
13766
13767 /* fail on notifications sent after #OnSessionEnd() is called, it is
13768 * expected by the caller */
13769 if (!directControl)
13770 return E_FAIL;
13771
13772 /* No locks should be held at this point. */
13773 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13774 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13775
13776 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13777}
13778
13779/**
13780 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13781 */
13782HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13783 IVirtualBoxErrorInfo *aError)
13784{
13785 LogFlowThisFunc(("\n"));
13786
13787 AutoCaller autoCaller(this);
13788
13789 /* This notification may happen after the machine object has been
13790 * uninitialized (the session was closed), so don't assert. */
13791 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13792
13793 ComPtr<IInternalSessionControl> directControl;
13794 {
13795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13796 directControl = mData->mSession.mDirectControl;
13797 }
13798
13799 /* fail on notifications sent after #OnSessionEnd() is called, it is
13800 * expected by the caller */
13801 if (!directControl)
13802 return E_FAIL;
13803
13804 /* No locks should be held at this point. */
13805 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13806 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13807
13808 return directControl->OnUSBDeviceDetach(aId, aError);
13809}
13810
13811// protected methods
13812/////////////////////////////////////////////////////////////////////////////
13813
13814/**
13815 * Helper method to finalize saving the state.
13816 *
13817 * @note Must be called from under this object's lock.
13818 *
13819 * @param aRc S_OK if the snapshot has been taken successfully
13820 * @param aErrMsg human readable error message for failure
13821 *
13822 * @note Locks mParent + this objects for writing.
13823 */
13824HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13825{
13826 LogFlowThisFuncEnter();
13827
13828 AutoCaller autoCaller(this);
13829 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13830
13831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13832
13833 HRESULT rc = S_OK;
13834
13835 if (SUCCEEDED(aRc))
13836 {
13837 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13838
13839 /* save all VM settings */
13840 rc = saveSettings(NULL);
13841 // no need to check whether VirtualBox.xml needs saving also since
13842 // we can't have a name change pending at this point
13843 }
13844 else
13845 {
13846 // delete the saved state file (it might have been already created);
13847 // we need not check whether this is shared with a snapshot here because
13848 // we certainly created this saved state file here anew
13849 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13850 }
13851
13852 /* notify the progress object about operation completion */
13853 Assert(mConsoleTaskData.mProgress);
13854 if (SUCCEEDED(aRc))
13855 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13856 else
13857 {
13858 if (aErrMsg.length())
13859 mConsoleTaskData.mProgress->notifyComplete(aRc,
13860 COM_IIDOF(ISession),
13861 getComponentName(),
13862 aErrMsg.c_str());
13863 else
13864 mConsoleTaskData.mProgress->notifyComplete(aRc);
13865 }
13866
13867 /* clear out the temporary saved state data */
13868 mConsoleTaskData.mLastState = MachineState_Null;
13869 mConsoleTaskData.strStateFilePath.setNull();
13870 mConsoleTaskData.mProgress.setNull();
13871
13872 LogFlowThisFuncLeave();
13873 return rc;
13874}
13875
13876/**
13877 * Deletes the given file if it is no longer in use by either the current machine state
13878 * (if the machine is "saved") or any of the machine's snapshots.
13879 *
13880 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13881 * but is different for each SnapshotMachine. When calling this, the order of calling this
13882 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13883 * is therefore critical. I know, it's all rather messy.
13884 *
13885 * @param strStateFile
13886 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13887 */
13888void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13889 Snapshot *pSnapshotToIgnore)
13890{
13891 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13892 if ( (strStateFile.isNotEmpty())
13893 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13894 )
13895 // ... and it must also not be shared with other snapshots
13896 if ( !mData->mFirstSnapshot
13897 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13898 // this checks the SnapshotMachine's state file paths
13899 )
13900 RTFileDelete(strStateFile.c_str());
13901}
13902
13903/**
13904 * Locks the attached media.
13905 *
13906 * All attached hard disks are locked for writing and DVD/floppy are locked for
13907 * reading. Parents of attached hard disks (if any) are locked for reading.
13908 *
13909 * This method also performs accessibility check of all media it locks: if some
13910 * media is inaccessible, the method will return a failure and a bunch of
13911 * extended error info objects per each inaccessible medium.
13912 *
13913 * Note that this method is atomic: if it returns a success, all media are
13914 * locked as described above; on failure no media is locked at all (all
13915 * succeeded individual locks will be undone).
13916 *
13917 * The caller is responsible for doing the necessary state sanity checks.
13918 *
13919 * The locks made by this method must be undone by calling #unlockMedia() when
13920 * no more needed.
13921 */
13922HRESULT SessionMachine::lockMedia()
13923{
13924 AutoCaller autoCaller(this);
13925 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13926
13927 AutoMultiWriteLock2 alock(this->lockHandle(),
13928 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13929
13930 /* bail out if trying to lock things with already set up locking */
13931 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13932
13933 MultiResult mrc(S_OK);
13934
13935 /* Collect locking information for all medium objects attached to the VM. */
13936 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13937 it != mMediaData->mAttachments.end();
13938 ++it)
13939 {
13940 MediumAttachment* pAtt = *it;
13941 DeviceType_T devType = pAtt->getType();
13942 Medium *pMedium = pAtt->getMedium();
13943
13944 MediumLockList *pMediumLockList(new MediumLockList());
13945 // There can be attachments without a medium (floppy/dvd), and thus
13946 // it's impossible to create a medium lock list. It still makes sense
13947 // to have the empty medium lock list in the map in case a medium is
13948 // attached later.
13949 if (pMedium != NULL)
13950 {
13951 MediumType_T mediumType = pMedium->getType();
13952 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13953 || mediumType == MediumType_Shareable;
13954 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13955
13956 alock.release();
13957 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13958 !fIsReadOnlyLock /* fMediumLockWrite */,
13959 NULL,
13960 *pMediumLockList);
13961 alock.acquire();
13962 if (FAILED(mrc))
13963 {
13964 delete pMediumLockList;
13965 mData->mSession.mLockedMedia.Clear();
13966 break;
13967 }
13968 }
13969
13970 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13971 if (FAILED(rc))
13972 {
13973 mData->mSession.mLockedMedia.Clear();
13974 mrc = setError(rc,
13975 tr("Collecting locking information for all attached media failed"));
13976 break;
13977 }
13978 }
13979
13980 if (SUCCEEDED(mrc))
13981 {
13982 /* Now lock all media. If this fails, nothing is locked. */
13983 alock.release();
13984 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13985 alock.acquire();
13986 if (FAILED(rc))
13987 {
13988 mrc = setError(rc,
13989 tr("Locking of attached media failed"));
13990 }
13991 }
13992
13993 return mrc;
13994}
13995
13996/**
13997 * Undoes the locks made by by #lockMedia().
13998 */
13999void SessionMachine::unlockMedia()
14000{
14001 AutoCaller autoCaller(this);
14002 AssertComRCReturnVoid(autoCaller.rc());
14003
14004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14005
14006 /* we may be holding important error info on the current thread;
14007 * preserve it */
14008 ErrorInfoKeeper eik;
14009
14010 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14011 AssertComRC(rc);
14012}
14013
14014/**
14015 * Helper to change the machine state (reimplementation).
14016 *
14017 * @note Locks this object for writing.
14018 * @note This method must not call saveSettings or SaveSettings, otherwise
14019 * it can cause crashes in random places due to unexpectedly committing
14020 * the current settings. The caller is responsible for that. The call
14021 * to saveStateSettings is fine, because this method does not commit.
14022 */
14023HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14024{
14025 LogFlowThisFuncEnter();
14026 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14027
14028 AutoCaller autoCaller(this);
14029 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14030
14031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14032
14033 MachineState_T oldMachineState = mData->mMachineState;
14034
14035 AssertMsgReturn(oldMachineState != aMachineState,
14036 ("oldMachineState=%s, aMachineState=%s\n",
14037 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14038 E_FAIL);
14039
14040 HRESULT rc = S_OK;
14041
14042 int stsFlags = 0;
14043 bool deleteSavedState = false;
14044
14045 /* detect some state transitions */
14046
14047 if ( ( oldMachineState == MachineState_Saved
14048 && aMachineState == MachineState_Restoring)
14049 || ( ( oldMachineState == MachineState_PoweredOff
14050 || oldMachineState == MachineState_Teleported
14051 || oldMachineState == MachineState_Aborted
14052 )
14053 && ( aMachineState == MachineState_TeleportingIn
14054 || aMachineState == MachineState_Starting
14055 )
14056 )
14057 )
14058 {
14059 /* The EMT thread is about to start */
14060
14061 /* Nothing to do here for now... */
14062
14063 /// @todo NEWMEDIA don't let mDVDDrive and other children
14064 /// change anything when in the Starting/Restoring state
14065 }
14066 else if ( ( oldMachineState == MachineState_Running
14067 || oldMachineState == MachineState_Paused
14068 || oldMachineState == MachineState_Teleporting
14069 || oldMachineState == MachineState_LiveSnapshotting
14070 || oldMachineState == MachineState_Stuck
14071 || oldMachineState == MachineState_Starting
14072 || oldMachineState == MachineState_Stopping
14073 || oldMachineState == MachineState_Saving
14074 || oldMachineState == MachineState_Restoring
14075 || oldMachineState == MachineState_TeleportingPausedVM
14076 || oldMachineState == MachineState_TeleportingIn
14077 )
14078 && ( aMachineState == MachineState_PoweredOff
14079 || aMachineState == MachineState_Saved
14080 || aMachineState == MachineState_Teleported
14081 || aMachineState == MachineState_Aborted
14082 )
14083 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14084 * snapshot */
14085 && ( mConsoleTaskData.mSnapshot.isNull()
14086 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14087 )
14088 )
14089 {
14090 /* The EMT thread has just stopped, unlock attached media. Note that as
14091 * opposed to locking that is done from Console, we do unlocking here
14092 * because the VM process may have aborted before having a chance to
14093 * properly unlock all media it locked. */
14094
14095 unlockMedia();
14096 }
14097
14098 if (oldMachineState == MachineState_Restoring)
14099 {
14100 if (aMachineState != MachineState_Saved)
14101 {
14102 /*
14103 * delete the saved state file once the machine has finished
14104 * restoring from it (note that Console sets the state from
14105 * Restoring to Saved if the VM couldn't restore successfully,
14106 * to give the user an ability to fix an error and retry --
14107 * we keep the saved state file in this case)
14108 */
14109 deleteSavedState = true;
14110 }
14111 }
14112 else if ( oldMachineState == MachineState_Saved
14113 && ( aMachineState == MachineState_PoweredOff
14114 || aMachineState == MachineState_Aborted
14115 || aMachineState == MachineState_Teleported
14116 )
14117 )
14118 {
14119 /*
14120 * delete the saved state after Console::ForgetSavedState() is called
14121 * or if the VM process (owning a direct VM session) crashed while the
14122 * VM was Saved
14123 */
14124
14125 /// @todo (dmik)
14126 // Not sure that deleting the saved state file just because of the
14127 // client death before it attempted to restore the VM is a good
14128 // thing. But when it crashes we need to go to the Aborted state
14129 // which cannot have the saved state file associated... The only
14130 // way to fix this is to make the Aborted condition not a VM state
14131 // but a bool flag: i.e., when a crash occurs, set it to true and
14132 // change the state to PoweredOff or Saved depending on the
14133 // saved state presence.
14134
14135 deleteSavedState = true;
14136 mData->mCurrentStateModified = TRUE;
14137 stsFlags |= SaveSTS_CurStateModified;
14138 }
14139
14140 if ( aMachineState == MachineState_Starting
14141 || aMachineState == MachineState_Restoring
14142 || aMachineState == MachineState_TeleportingIn
14143 )
14144 {
14145 /* set the current state modified flag to indicate that the current
14146 * state is no more identical to the state in the
14147 * current snapshot */
14148 if (!mData->mCurrentSnapshot.isNull())
14149 {
14150 mData->mCurrentStateModified = TRUE;
14151 stsFlags |= SaveSTS_CurStateModified;
14152 }
14153 }
14154
14155 if (deleteSavedState)
14156 {
14157 if (mRemoveSavedState)
14158 {
14159 Assert(!mSSData->strStateFilePath.isEmpty());
14160
14161 // it is safe to delete the saved state file if ...
14162 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14163 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14164 // ... none of the snapshots share the saved state file
14165 )
14166 RTFileDelete(mSSData->strStateFilePath.c_str());
14167 }
14168
14169 mSSData->strStateFilePath.setNull();
14170 stsFlags |= SaveSTS_StateFilePath;
14171 }
14172
14173 /* redirect to the underlying peer machine */
14174 mPeer->setMachineState(aMachineState);
14175
14176 if ( aMachineState == MachineState_PoweredOff
14177 || aMachineState == MachineState_Teleported
14178 || aMachineState == MachineState_Aborted
14179 || aMachineState == MachineState_Saved)
14180 {
14181 /* the machine has stopped execution
14182 * (or the saved state file was adopted) */
14183 stsFlags |= SaveSTS_StateTimeStamp;
14184 }
14185
14186 if ( ( oldMachineState == MachineState_PoweredOff
14187 || oldMachineState == MachineState_Aborted
14188 || oldMachineState == MachineState_Teleported
14189 )
14190 && aMachineState == MachineState_Saved)
14191 {
14192 /* the saved state file was adopted */
14193 Assert(!mSSData->strStateFilePath.isEmpty());
14194 stsFlags |= SaveSTS_StateFilePath;
14195 }
14196
14197#ifdef VBOX_WITH_GUEST_PROPS
14198 if ( aMachineState == MachineState_PoweredOff
14199 || aMachineState == MachineState_Aborted
14200 || aMachineState == MachineState_Teleported)
14201 {
14202 /* Make sure any transient guest properties get removed from the
14203 * property store on shutdown. */
14204
14205 HWData::GuestPropertyMap::const_iterator it;
14206 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14207 if (!fNeedsSaving)
14208 for (it = mHWData->mGuestProperties.begin();
14209 it != mHWData->mGuestProperties.end(); ++it)
14210 if ( (it->second.mFlags & guestProp::TRANSIENT)
14211 || (it->second.mFlags & guestProp::TRANSRESET))
14212 {
14213 fNeedsSaving = true;
14214 break;
14215 }
14216 if (fNeedsSaving)
14217 {
14218 mData->mCurrentStateModified = TRUE;
14219 stsFlags |= SaveSTS_CurStateModified;
14220 }
14221 }
14222#endif
14223
14224 rc = saveStateSettings(stsFlags);
14225
14226 if ( ( oldMachineState != MachineState_PoweredOff
14227 && oldMachineState != MachineState_Aborted
14228 && oldMachineState != MachineState_Teleported
14229 )
14230 && ( aMachineState == MachineState_PoweredOff
14231 || aMachineState == MachineState_Aborted
14232 || aMachineState == MachineState_Teleported
14233 )
14234 )
14235 {
14236 /* we've been shut down for any reason */
14237 /* no special action so far */
14238 }
14239
14240 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14241 LogFlowThisFuncLeave();
14242 return rc;
14243}
14244
14245/**
14246 * Sends the current machine state value to the VM process.
14247 *
14248 * @note Locks this object for reading, then calls a client process.
14249 */
14250HRESULT SessionMachine::updateMachineStateOnClient()
14251{
14252 AutoCaller autoCaller(this);
14253 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14254
14255 ComPtr<IInternalSessionControl> directControl;
14256 {
14257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14258 AssertReturn(!!mData, E_FAIL);
14259 directControl = mData->mSession.mDirectControl;
14260
14261 /* directControl may be already set to NULL here in #OnSessionEnd()
14262 * called too early by the direct session process while there is still
14263 * some operation (like deleting the snapshot) in progress. The client
14264 * process in this case is waiting inside Session::close() for the
14265 * "end session" process object to complete, while #uninit() called by
14266 * #checkForDeath() on the Watcher thread is waiting for the pending
14267 * operation to complete. For now, we accept this inconsistent behavior
14268 * and simply do nothing here. */
14269
14270 if (mData->mSession.mState == SessionState_Unlocking)
14271 return S_OK;
14272
14273 AssertReturn(!directControl.isNull(), E_FAIL);
14274 }
14275
14276 return directControl->UpdateMachineState(mData->mMachineState);
14277}
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